上一篇博客配置好了树莓派端的串口通信,这次在加入涡轮流量计之前也先用PC端模拟树莓派测试一下该仪表是否能正常工作。

一、测试流量计通信

流量计说明书如下:

并且在设备上电时以(9600,8n1格式)自动发送四个字节:

返码格式:站地址(1字节)+波特率(2字节)+格式(1字节)

8n1格式指8个数据位,无校验,1个停止位,8e1与8o1分别对应偶校验和奇校验。

将流量计与USB转485转换器及24V电源正确接线,打开PC端串口,开启电源,观察接收窗口信息(注意接收区和发送区均调成十六进制显示):

接收到返码为“01 25 80 00”,其意义为:设备地址1,波特率9600,格式8n1

依据说明书的样例,在PC端发送以下命令:

这一命令用于查询瞬时流量值,依据上篇学过的modbus-RTU协议,可以对该命令作如下解析:

发送:
01 03 00 00 00 02 C4 0B01-设备地址为1
03-代表查询功能
00 00-查询的寄存器起始地址,由此得知储存瞬时流量值这一数据的寄存器起始地址为0
00 02-查询的寄存器数量,由此得知瞬时流量这一数据由2个寄存器储存
C4 0B-CRC校验码接收:
01 03 04 00 00 00 00 FA 3301-设备地址为1
03-查询功能
04-表示后面有4个字节的数据,因为查询了2个寄存器,所以返回2*2个字节数据
00 00 00 00-查询所得的数据
FA 33-CRC校验码

确认了流量计通讯正常后,将树莓派与流量计正确接线,在通讯实验前我事先利用CRC校验算法算好了所有将要用到的命令的校验位值,依据说明书记录的各项数据类型可以推算该数据占用的寄存器个数,例如32位无符号占用4字节,即2个寄存器,因此查询时需要从起始地址往后查两个寄存器的数据,各个数据的起始地址在说明书中均已给出。

二、流量计实验前准备工作

所有拟用到的完整命令如下:

01 03 00 00 00 02 C4 0B        //查询瞬时流量值(L/H)
01 03 00 02 00 02 65 CB        //查询累计流量(L)
01 03 00 08 00 02 45 C9        //查询累计脉冲数
01 03 17 71 00 02 91 A4        //查询仪器系数
01 06 00 06 00 01 A8 0B        //累计流量清零
01 06 00 07 00 01 F9 CB        //累计脉冲数清零

解释一下脉冲数和仪器系数,流量计工作时,内部的水流会推动里面的涡轮叶片转动,每当叶片经过磁铁时会产生感应信号,再经过放大器之类的将脉冲送到计数器里,而仪器系数就是表明多少个脉冲来表示一升水的流量,比如仪器系数1000就表示1000个脉冲1升水。

首先查询一下仪器系数(左边发送,右边接收):

可以看到返回的数据位是“00 00 03 11”,将十六进制311转成十进制为785,说明此流量计仪器系数为785,785个脉冲一升水。

清零累计流量与累计脉冲数:

清零之后我先往流量计里倒了一瓶自来水做实验,对比其脉冲数和流量值的关系,最后确认了瞬时流量值和累计流量值的数据位返回的均是保留两位小数的十六进制形式,比如累计流量返码的数据位是“00 00 01 00”即十六进制的100,转化为十进制256,表示的水量是2.56升。

到这准备工作都已经完成,想要采集流量数据只要把流量计装到工作环境里开始工作就完事了,随时可以通过receive和send两个python文件发送计算好的命令来查询和修改流量计采集的各项数据。接下来我就夹带点私货了~

三、数据处理预备工作

以上两步基本确认了树莓派与流量计的通讯没有问题,不出意外的话实验可以顺利进行的。那我在这里先准备一下实验数据的处理(这一节其实和上篇写的实验目标没啥关系hhh)。我最终是想将查询流量计所得的瞬时流量、累计流量两项数据上传到搭建好的Hyperledger Fabric环境中,此前对照官方示例fabcar写了个test链码:

/*
SPDX-License-Identifier: Apache-2.0
*/package mainimport ("encoding/json""fmt""github.com/hyperledger/fabric-contract-api-go/contractapi"
)type SmartContract struct {contractapi.Contract
}type Data struct {Now  string `json:"now(L/H)"`Total   string `json:"total(L)"`
}type QueryResult struct {Key    string `json:"Key"`Record *Data
}func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {datas := []Data{Data{Now:"0", Total: "0"},}for data := range datas {dataAsBytes, _ := json.Marshal(data)err := ctx.GetStub().PutState("2022-07-20 00:00", 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, now string, total string) error {data := Data{Now:    now,Total:   total,}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())}
}

上传的数据叫做Data,Record包含“total”(累计流量,单位L)和“now”(瞬时流量,单位L/H);Key是查询的时间,形如“2022-07-20 19:00”,将格式化的定长时间字符串作为Key可以避免由字典序排列引起的查询结果乱序问题。

稍微修改一下上篇博客里的收发python文件,主要是调试时出现过参数类型的问题,修改完之后receive.py在接收数据后截取出数据位,转化成十进制,再转化为浮点数除以100,然后以字符串形式存入data.txt。

receive.py:

# -*- coding:utf-8 -*-
import RPi.GPIO as GPIO
import serialEN_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()data=string[6:14]print(data)res=int(data,16)#print(res)result=float(res)/100#print(result)note=open('/home/pi/Desktop/hyperledger/multinodes-pi/data.txt',mode='w')note.write(str(result))note.close()#break

为了方便shell脚本的调用,我把原先的send.py分成了两个,一个query_now用来查询瞬时流量,一个query_total用来查询累计流量。

query_now.py:

# -*- coding:utf-8 -*-
import RPi.GPIO as GPIO
import serialEN_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)

query_total.py:

# -*- coding:utf-8 -*-
import RPi.GPIO as GPIO
import serialEN_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 02 00 02 65 CB'
str=bytes.fromhex(strInput)
print(str)
n = t.write(str)
print (n)

然后就是shell脚本的编写,我预想的逻辑是首先开启receive.py保持接收数据,然后3min一次循环调用脚本。每次循环先是调用一次query_now.py,此时data.txt会通过receive.py存入瞬时流量值的数据,用shell命令读取出数据存入变量n;再调用一次query_total.py,相同的方法读出累计流量值数据存入变量t。n和t再和当前时间time组成一条命令“AddData”写入add.sh这一脚本,最后在cli容器里调用add脚本完成数据上传的操作。为了预留足够的时间给流量计通讯以及python的数据写入等工作,我在运行pyhton文件之后会等待3s的时间,确保数据写入的操作已经完成。

但是很不幸,一个莫名其妙的bug卡了我整整一个下午。。按照上述的逻辑进行到shell命令从data.txt中读命令这一步时,它读出来的永远是空串?在确认了逻辑无误后我在网上查了很久也查不到和我遇到的相同的bug,相对来说比较可能的说法是脚本读取文件时光标位置出错,由于文件没有关闭,读取操作结束后光标会一直停留在文件末尾,所以下次再读时是从文件末尾开始读,读出来的就是空串。这样看来是我shell命令读完之后文件没有正确关闭吗?那起码第一次得能读出数据吧,我连一次都没读出来过。。另一次文件操作也就只有receive.py里写入数据的过程了,但是我也的确写了关闭文件这个操作,我唯一能猜测的可能性就是树莓派系统的shell命令行对文件是否关闭的判断与python有点冲突,因为我用shell脚本读其他未被python操作过的文件都是能读出数据的,唯独这个被python改写过的data.txt不行。后来我就想怎么让shell读取时光标再重新回到文件起始位置?我用shell也写点东西进去,写入完毕之后应该会关上文件吧?于是我就用shell语句写了一个空格添加在data.txt的末尾,然后再用“while read rows”读取,就这样还真解决了这个bug。。。当然以上的原因都是我的猜想,如果有大佬明白真正原因的也请评论区告诉我一下~感谢!

最后能成功运行的test.sh是这样的:

#!/bin/bash
for i in {1..20}
dosudo python /home/pi/RS485_CAN_HAT_Code/485/python/query_now.pytime=$(date "+%Y-%m-%d %H:%M")sleep 3echo " " >> data.txtwhile read rowsdon=$rowsbreakdone < data.txtsudo python /home/pi/RS485_CAN_HAT_Code/485/python/query_total.pysleep 3echo " " >> data.txtwhile read rowsdot=$rowsbreakdone < data.txtecho "这是第"$i"次查询到并添加的数据:"echo "Now(L/H):"$n" Total(L):"$t" time:"$timecmd="'{\"Args\":[\"AddData\",\"$time\",\"$n\",\"$t\"]}'"echo "Add命令:"$cmdecho "#!/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.shsleep 174
#break
done

差不多准备就绪了,在家里的洗手间搭了一下实验环境:

四、运行结果

test.sh启动,挂了大概一个小时:

Org1查询的结果,按照时间顺序排序:

搞定!

树莓派4B与智能涡轮流量计通过RS485(modbus RTU协议)收发数据(二)(Hyperledger Fabric环境中上传数据)相关推荐

  1. 树莓派4B与智能涡轮流量计通过RS485(modbus RTU协议)收发数据(一)

    目标:使用树莓派及CAN HAT扩展板读取智能涡轮流量计(RS485通讯改装,使用modbus-RTU协议)的各项测量数据. 由于是初次学习,对智能仪表以及树莓派串口通信等知识比较陌生,在此对实验过程 ...

  2. RS232(Modbus RTU)+RS485(Modbus RTU)协议RFID识别磁导航AGV小车传感器|定位仪CK-GL16-AB的安装与磁处理方法

    RS232(Modbus RTU)+RS485(Modbus RTU)协议RFID识别磁导航AGV小车传感器|定位仪CK-GL16-AB是一款面向AGV行业新推出的一款"跨界"传感 ...

  3. 串行通讯RS485 Modbus RTU协议控制

    一.内容简介 本文主要介绍欧姆龙CP1E做上位与SMC的LECP6电缸 之间串行通讯RS485 Modbus RTU协议程序控制说明. 二.设备简介         硬件:CP1E.CP1W-CIF1 ...

  4. Matlab Simulink RS-485 Modbus RTU协议串口通信实录

    目录 一.目标实现 二.基本原理 三.过程实录 一.基本原理 一.目标实现 最近需要进行一个大型试验,需要实现:通过上位机来实时收集波高仪数据并经过一系列复杂算法来控制下位机(IO模块即继电器)从而控 ...

  5. 树莓派4B、Python与三相四线多功能电力仪表通过RS485(modbus RTU协议)收发数据

    树莓派4B+Python与三相四线多功能电力仪表通过RS485(modbus RTU协议)接口发送和接收数据 请耐心把下面的警告⚠️看完 开始之前需要注意以下点:一.那就是安全,生命为本,安全第一.因 ...

  6. 基于树莓派4B的智能无人巡逻小车设计-答辩PPT

    视频演示:树莓派暑期工程实训-模拟警用无人巡逻小车_哔哩哔哩_bilibili 报告链接:基于树莓派4B的智能无人巡逻小车设计_猫猫爱吃小鱼的博客-CSDN博客      

  7. 8数据提供什么掩膜产品_工业轨式1-8路RS485数据(MODBUS RTU协议)厂家产品说明...

    产品描述 工业级数点对点光猫提供1-8路RS485(MODBUS RTU协议): 在光纤中传输,该产品突破了传统串行接口通讯距离与通讯速率的矛盾,同时,也解决了电磁干扰.地环干扰和雷电破坏的难题,大大 ...

  8. 一种PLC RS485 Modbus RTU无线联网的解决方案

    一种PLC RS485 Modbus RTU无线联网的解决方案 参考文章: (1)一种PLC RS485 Modbus RTU无线联网的解决方案 (2)https://www.cnblogs.com/ ...

  9. modbus RTU协议设备使用无线代替有线注意事项

    1.设备有线连接 Modbus是由Modicon(现为施耐德电气公司的一个品牌)在1979年发明的,是全球第一个真正用于工业现场的总线协议.ModBus网络是一个工业通信系统,由带智能终端的可编程序控 ...

最新文章

  1. Eclipse下svn的创建分支/合并/切换使用
  2. python使用正则表达式抽取文件中的IP地址
  3. linux两种方式改变文件权限,Linux更改文件权限的两种方式
  4. python开发环境和运行环境的区别_Django 开发环境与生产环境的区分详解
  5. 【数据结构与算法】之深入解析“实现strStr()”的求解思路与算法示例
  6. 百钱买白鸡与啤酒饮料
  7. java webservice 接收数据_WebService客户端,接收数据解析存入数据库
  8. Ubuntu18.04安装JDK1.8.0_11
  9. eclipse java jsp_Javaweb学习笔记4 使用Eclipse快速开发JSP
  10. VS2008无法正确打开项目/解决方案
  11. Linux系统下apt-get命令详解
  12. Html与JS正则表达式测试代码
  13. back to wuxi
  14. excel使用mysql数据库查询语句_如何通过Excel查询MySQL数据库
  15. PDF怎么拆分,PDF拆分技巧
  16. Nik Collection v3.0.7 2020 Mac/Win PS/LR超强调色滤镜合集Nik插件中文版+中文教程
  17. word打开文档很久很慢_word文档 excel文档打开速度比较慢怎么办
  18. ActiveMq简单使用
  19. springboot+maven+jwt学生信息增删查改
  20. python简单图形输出_基于 Python Matplotlib 模块的高质量图形输出

热门文章

  1. Cortex A8 LED 点亮程序分析
  2. php实现定时采集,懒人一键采集(采集+推送+定时)
  3. java录音机_GitHub - wylGeorge/RuphyRecorder: 基于Java的录音机(使用基于Rest API的页面操作,方便远程控制)...
  4. 多个站点同时抓取!怎么做到的?
  5. CC2530学习光敏传感器
  6. 计算机无效d盘的错误怎么解决,在电脑中安装软件时提示无效驱动器D的解决方法...
  7. android 根据坐标日期计算日出日落时间表
  8. 程序员的另一出路:大数据工程师
  9. 普源DS1000Z-E 系列数字示波器技术参数
  10. Chrome浏览器升级配置