目標(biāo):使用樹(shù)莓派4B與CANHAT擴(kuò)展板讀取智能插排測(cè)量的各項(xiàng)數(shù)據(jù)(RS485+modbus RTU),獲取的數(shù)據(jù)上傳到Hyperledger Fabric框架。
之前學(xué)習(xí)過(guò)了modbus RTU協(xié)議,在智能渦輪流量計(jì)的實(shí)驗(yàn)中應(yīng)用過(guò)一次,這次用這個(gè)帶485模塊的智能插座再?gòu)?fù)習(xí)一次~
實(shí)驗(yàn)材料:
樹(shù)莓派4B/8G:
?CANHAT擴(kuò)展板:
?USB-485轉(zhuǎn)換器:
?RS485機(jī)柜排插:
?RJ45水晶頭轉(zhuǎn)8PIN端子:
?樹(shù)莓派相關(guān)庫(kù)與例程在上次實(shí)驗(yàn)已經(jīng)安裝過(guò)了,步驟可參照官網(wǎng):
RS485 CAN HAT - Waveshare Wiki
一、PC端串口測(cè)試
還是先使用PC端的串口調(diào)試助手測(cè)試一下智能插座的通訊。先將設(shè)備正確接線(xiàn):
?這次的智能插排RS485模塊接線(xiàn)口是水晶頭而不是通常的AB端子,所以還需要一個(gè)水晶頭轉(zhuǎn)端子線(xiàn),接線(xiàn)如上圖的說(shuō)明書(shū)所示。
調(diào)試前先看看設(shè)備的通信說(shuō)明書(shū):
?可以看到一個(gè)寄存器同樣也是表示2字節(jié)的數(shù)據(jù),電量用兩個(gè)寄存器表示也就是4個(gè)字節(jié),其他數(shù)據(jù)應(yīng)該都只占用一個(gè)寄存器。這次的說(shuō)明書(shū)詳細(xì)一些,還給出了數(shù)據(jù)轉(zhuǎn)換公式。
具體各項(xiàng)數(shù)據(jù)存儲(chǔ)的寄存器地址如下:
?計(jì)算一下需要用到的modbus命令:
01 03 00 48 00 01 04 1C 查詢(xún)電壓值
01 03 00 49 00 01 55 DC 查詢(xún)電流值
01 03 00 4A 00 01 A5 DC 查詢(xún)有功功率
01 03 00 4B 00 02 B4 1D 查詢(xún)有功總電能
01 03 00 4D 00 01 14 1D 查詢(xún)功率因數(shù)
01 03 00 4E 00 02 A4 1C 查詢(xún)二氧化碳排量
以查詢(xún)有功總電能為例,串口調(diào)試助手發(fā)送命令后接收到如下數(shù)據(jù):
數(shù)據(jù)位為“00 00 01 23”,十進(jìn)制數(shù)值為291,根據(jù)數(shù)據(jù)轉(zhuǎn)換公式值=DATA/3200,算得有功總電能為0.09kWh,經(jīng)驗(yàn)證數(shù)據(jù)無(wú)誤:
?二、樹(shù)莓派與智能插排通信
python文件編寫(xiě)如下:
receive.py:
# -*- coding:utf-8 -*-
import RPi.GPIO as GPIO
import serial
EN_485 = 4
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(EN_485,GPIO.OUT)
GPIO.output(EN_485,GPIO.LOW)
ser = serial.Serial("/dev/ttyAMA0",9600,timeout=1) # open first serial port
while 1:
Str = ser.readall()
if Str:
print (Str)
string=Str.hex()
print(string)
#print(res)
note=open('/home/pi/Desktop/hyperledger/multinodes-pi/data.txt',mode='w')
note.write(string)
note.close()
#break
receive.py與上次實(shí)驗(yàn)有所區(qū)別,主要因?yàn)檫@次查詢(xún)的數(shù)據(jù)較多,且每種數(shù)據(jù)儲(chǔ)存方式不同,如電流電壓等都儲(chǔ)存在一個(gè)寄存器中,也就是說(shuō)返回的數(shù)據(jù)位是2個(gè)字節(jié),而電能與二氧化碳排量存儲(chǔ)在兩個(gè)寄存器中,所以返回的數(shù)據(jù)為4個(gè)字節(jié),所以需要截取的數(shù)據(jù)位是不同的。除此以外相比于渦輪流量計(jì)查詢(xún)到的數(shù)據(jù),這次實(shí)驗(yàn)查詢(xún)到的不同的值轉(zhuǎn)換公式也不同,如有功功率就是返回的數(shù)據(jù)位轉(zhuǎn)化成十進(jìn)制后的值,單位為W;而有功總電能則是返回的數(shù)據(jù)轉(zhuǎn)化為十進(jìn)制后再除以3200,單位為kWh。所以我準(zhǔn)備在receive接收返碼時(shí)進(jìn)行一次判斷,因?yàn)楫?dāng)返回的數(shù)據(jù)位有2個(gè)字節(jié)時(shí),返碼總長(zhǎng)度為7字節(jié);返回?cái)?shù)據(jù)位有4個(gè)字節(jié)時(shí),返碼總長(zhǎng)度為9字節(jié)。所以通過(guò)接收到的數(shù)組長(zhǎng)度就能確定需要截取的數(shù)據(jù)位的位置了,如果返碼總長(zhǎng)度為7字節(jié),截取[6:10],總長(zhǎng)度為9字節(jié),則截取[6:14]。截取數(shù)據(jù)位之后將其轉(zhuǎn)為十進(jìn)制存入data.txt,操作data時(shí)我感覺(jué)用shell命令處理這么多浮點(diǎn)數(shù)的運(yùn)算寫(xiě)起來(lái)比較麻煩,所以在shell腳本調(diào)用指定的send.py數(shù)據(jù)查詢(xún)1s后,依據(jù)查詢(xún)的數(shù)據(jù)類(lèi)型在對(duì)應(yīng)的send文件中將data.txt文件的數(shù)據(jù)進(jìn)行換算再重新寫(xiě)入,最后data.txt中存的就是我需要的最終數(shù)據(jù)。
send.py
# -*- coding:utf-8 -*-
import RPi.GPIO as GPIO
import serial
EN_485 = 4
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(EN_485,GPIO.OUT)
GPIO.output(EN_485,GPIO.HIGH)
t = serial.Serial("/dev/ttyAMA0",9600)
print (t.portstr)
strInput = '01 03 00 00 00 02 C4 0B'
str=bytes.fromhex(strInput)
print(str)
n = t.write(str)
print (n)
send文件通過(guò)修改strInput來(lái)發(fā)送不同的查詢(xún)命令,將得到的結(jié)果存入data.txt并使用腳本讀取。在send文件中還需要進(jìn)行數(shù)據(jù)轉(zhuǎn)換操作,不同數(shù)據(jù)轉(zhuǎn)換公式不同,下面是電壓voltage與總電能energy的查詢(xún)文件:
voltage.py:
# -*- coding:utf-8 -*-
import RPi.GPIO as GPIO
import serial
import time
EN_485 = 4
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(EN_485,GPIO.OUT)
GPIO.output(EN_485,GPIO.HIGH)
t = serial.Serial("/dev/ttyAMA0",9600)
#print (t.portstr)
strInput = '01 03 00 48 00 01 04 1C'
string=bytes.fromhex(strInput)
#print(string)
n = t.write(string)
#print (n)
time.sleep(2)
f=open('/home/pi/Desktop/hyperledger/multinodes-pi/data.txt',mode='r')
data=f.readlines()
f.close()
res=int(data[0])
res=float(res)/100
f=open('/home/pi/Desktop/hyperledger/multinodes-pi/data.txt',mode='w')
f.write(str(res))
f.close()
time.sleep(1)
energy.py:
# -*- coding:utf-8 -*-
import RPi.GPIO as GPIO
import serial
import time
EN_485 = 4
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(EN_485,GPIO.OUT)
GPIO.output(EN_485,GPIO.HIGH)
t = serial.Serial("/dev/ttyAMA0",9600)
#print (t.portstr)
strInput = '01 03 00 4B 00 02 B4 1D'
string=bytes.fromhex(strInput)
#print(string)
n = t.write(string)
#print (n)
time.sleep(2)
f=open('/home/pi/Desktop/hyperledger/multinodes-pi/data.txt',mode='r')
data=f.readlines()
f.close()
res=int(data[0])
res=float(res)/3200
f=open('/home/pi/Desktop/hyperledger/multinodes-pi/data.txt',mode='w')
f.write(str(res))
f.close()
time.sleep(1)
相對(duì)應(yīng)地,結(jié)合上次實(shí)驗(yàn)的渦輪流量計(jì),對(duì)hyperledger fabric的鏈碼也做了一些修改,預(yù)想的情景下一個(gè)樹(shù)莓派采集一組流量計(jì)、智能插排、氣量計(jì)的數(shù)據(jù)并將其上傳到鏈上,為了方便區(qū)分多組儀表的數(shù)據(jù),Key值再加入三位ID來(lái)表示這一組儀表的編號(hào)。如“2022-8-2 001 003”的Key值表示2022年8月2日這天采集到的ID為001的這組儀表的第3條數(shù)據(jù)。
修改后鏈碼如下:
/*
SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
type SmartContract struct {
contractapi.Contract
}
type Data struct {
Flow_now string `json:"flow_now(L/H)"`
Flow_total string `json:"flow_total(L)"`
Voltage string `json:"voltage(V)"`
Current string `json:"current(A)"`
Power string `json:"power(W)"`
Energy string `json:"energy(kWh)"`
Factor string `json:"factor"`
Emissions string `json:"emissions(Kg)"`
Time string `json:"time"`
}
type QueryResult struct {
Key string `json:"Key"`
Record *Data
}
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
datas := []Data{
Data{Flow_now: "0", Flow_total: "0", Voltage: "0", Current: "0", Power: "0", Energy: "0", Factor: "0", Emissions: "0", Time: "00:00"},
}
for data := range datas {
dataAsBytes, _ := json.Marshal(data)
err := ctx.GetStub().PutState("2022-07-20 000 000", dataAsBytes)
if err != nil {
return fmt.Errorf("Failed to put to world state. %s", err.Error())
}
}
return nil
}
func (s *SmartContract) AddData(ctx contractapi.TransactionContextInterface, dataNumber string, flow_now string, flow_total string, voltage string, current string, power string, energy string, factor string, emissions string, time string) error {
data := Data{
Flow_now: flow_now,
Flow_total: flow_total,
Voltage: voltage,
Current: current,
Power: power,
Energy: energy,
Factor: factor,
Emissions: emissions,
Time: time,
}
dataAsBytes, _ := json.Marshal(data)
return ctx.GetStub().PutState(dataNumber, dataAsBytes)
}
func (s *SmartContract) QueryData(ctx contractapi.TransactionContextInterface, dataNumber string) (*Data, error) {
dataAsBytes, err := ctx.GetStub().GetState(dataNumber)
if err != nil {
return nil, fmt.Errorf("Failed to read from world state. %s", err.Error())
}
if dataAsBytes == nil {
return nil, fmt.Errorf("%s does not exist", dataNumber)
}
data := new(Data)
_ = json.Unmarshal(dataAsBytes, data)
return data, nil
}
func (s *SmartContract) QueryAllDatas(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) {
startKey := ""
endKey := ""
resultsIterator, err := ctx.GetStub().GetStateByRange(startKey, endKey)
if err != nil {
return nil, err
}
defer resultsIterator.Close()
results := []QueryResult{}
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
data := new(Data)
_ = json.Unmarshal(queryResponse.Value, data)
queryResult := QueryResult{Key: queryResponse.Key, Record: data}
results = append(results, queryResult)
}
return results, nil
}
func main() {
chaincode, err := contractapi.NewChaincode(new(SmartContract))
if err != nil {
fmt.Printf("Error create test chaincode: %s", err.Error())
return
}
if err := chaincode.Start(); err != nil {
fmt.Printf("Error starting test chaincode: %s", err.Error())
}
}
shell腳本:
#!/bin/bash
pre=$(date "+%Y-%m-%d")
num="1"
for i in {1..20}
do
now=$(date "+%Y-%m-%d")
if [ $pre != $now ]
then
num="1"
pre=$now
fi
id=$num
len=${#id}
while [ $len -le 2 ]
do
id="0"$id
let len+=1
done
let num+=1
time=$(date "+%H:%M")
res=$now" 001 "$id
sudo python /home/pi/RS485_CAN_HAT_Code/485/python/voltage.py
echo " " >> data.txt
while read rows
do
voltage=$rows
break
done < data.txt
sudo python /home/pi/RS485_CAN_HAT_Code/485/python/current.py
echo " " >> data.txt
while read rows
do
current=$rows
break
done < data.txt
sudo python /home/pi/RS485_CAN_HAT_Code/485/python/power.py
echo " " >> data.txt
while read rows
do
power=$rows
break
done < data.txt
sudo python /home/pi/RS485_CAN_HAT_Code/485/python/energy.py
echo " " >> data.txt
while read rows
do
energy=$rows
break
done < data.txt
sudo python /home/pi/RS485_CAN_HAT_Code/485/python/factor.py
echo " " >> data.txt
while read rows
do
factor=$rows
break
done < data.txt
sudo python /home/pi/RS485_CAN_HAT_Code/485/python/emissions.py
echo " " >> data.txt
while read rows
do
emissions=$rows
break
done < data.txt
echo "這是第"$i"次查詢(xún)到并添加的數(shù)據(jù):"
echo "flow_now(L/H):"$n" flow_total(L):"$t" voltage(V):"$voltage" current(A):"$current" power(W):"$power" energy(kWh):"$energy" factor:"$factor" emissions(Kg):"$emissions" time:"$time
n=0
t=0
cmd="'{\"Args\":[\"AddData\",\"$res\",\"$n\",\"$t\",\"$voltage\",\"$current\",\"$power\",\"$energy\",\"$factor\",\"$emissions\",\"$time\"]}'"
echo "Add命令:"$cmd
echo "#!/bin/bash
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n test --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c "$cmd "
exit"> add.sh
docker cp add.sh cli:/opt/gopath/src/github.com/hyperledger/fabric/peer/
docker exec -it cli bash add.sh
sleep 42
#break
done
腳本運(yùn)行結(jié)果:
Org1查詢(xún)結(jié)果:
?~~文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-791419.html
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-791419.html
到了這里,關(guān)于樹(shù)莓派4B與智能插排通過(guò)RS485(modbus RTU協(xié)議)通信的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!