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

这篇具有很好参考价值的文章主要介绍了树莓派4B与智能涡轮流量计通过RS485(modbus RTU协议)收发数据(二)(Hyperledger Fabric环境中上传数据)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

一、测试流量计通信

流量计说明书如下:

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

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

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

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

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

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

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

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

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

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

发送:
01 03 00 00 00 02 C4 0B

01-设备地址为1
03-代表查询功能
00 00-查询的寄存器起始地址,由此得知储存瞬时流量值这一数据的寄存器起始地址为0
00 02-查询的寄存器数量,由此得知瞬时流量这一数据由2个寄存器储存
C4 0B-CRC校验码

接收:
01 03 04 00 00 00 00 FA 33

01-设备地址为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升水。

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

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

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

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

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

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

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

三、数据处理预备工作

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

/*
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 {
	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 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()
        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 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)    

query_total.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 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}
do
	sudo python /home/pi/RS485_CAN_HAT_Code/485/python/query_now.py
	time=$(date "+%Y-%m-%d %H:%M")
	sleep 3
	echo " " >> data.txt
	while read rows
	do
		n=$rows
		break
	done < data.txt
	sudo python /home/pi/RS485_CAN_HAT_Code/485/python/query_total.py
	sleep 3
	echo " " >> data.txt
	while read rows
	do
		t=$rows
		break
	done < data.txt
	echo "这是第"$i"次查询到并添加的数据:"
	echo "Now(L/H):"$n" Total(L):"$t" time:"$time
	cmd="'{\"Args\":[\"AddData\",\"$time\",\"$n\",\"$t\"]}'"
	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 174
#break
done

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

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

 四、运行结果

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

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

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

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

 搞定!文章来源地址https://www.toymoban.com/news/detail-420263.html

到了这里,关于树莓派4B与智能涡轮流量计通过RS485(modbus RTU协议)收发数据(二)(Hyperledger Fabric环境中上传数据)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于树莓派4B的智能无人巡逻小车设计

    计算机工程实训报告 题目                 智能警用无人巡逻小车                                视频演示地址:树莓派暑期工程实训-模拟警用无人巡逻小车_哔哩哔哩_bilibili 一、实验内容与要求 本实验使用 Yahboom 生产的树莓派 4B,需要组员对树莓派小车的各

    2024年02月11日
    浏览(28)
  • 树莓派4B无屏幕安装Ubuntu20.04并通过ssh操作远程桌面

    因为手上没有合适的屏幕(其实是没有适配HDMI的屏幕,全是VGA接口) 所以用了些邪门(也不是啦)的方法去完成,整个过程只要读卡器和一根网线 在其中我遇到的困难包括但不限于:连不上wifi、树莓派烧录软件烧录的Ubuntu系统没有桌面、无屏幕和键盘、 这个过程踩了居多

    2024年02月03日
    浏览(45)
  • 基于树莓派4B设计的智能家居系统(华为云IOT)

    基于树莓派的智能家居控制系统(华为云IOT) 本次设计实现了一个基于树莓派的智能家居系统,可以对家庭环境进行实时监测和控制,提高居家安全性和舒适度。该系统采用了多种传感器和模块,包括温湿度传感器、烟雾传感器、火焰传感器、光敏传感器、雨滴传感器、LED灯光

    2024年02月08日
    浏览(36)
  • 基于树莓派4B与STM32的智能门禁系统项目(代码开源)

    前言: 本文为手把手教学 嵌入式经典项目 —— 智能门禁项目 ,本次项目采用  树莓派4B  与  STM32F103C8T6  进行联合开发。项目充分发挥各自 CPU 的优势与长处,将人脸识别的大计算量任务给 树莓派4B ,将门禁系统的控制部分交给 STM32 进行处理。该项目算是嵌入式人工智能

    2024年02月16日
    浏览(34)
  • 100个实战项目——在树莓派4B+Ubuntu20.04桌面版配置下运行智能小车(一)

    主机SSH远程链接从机 查看python版本 python 我的是python3.8 所以我需要安装pip3 sudo apt install python3-pip 接着安装程序需要的引脚库 sudo pip3 install RPi.GPIO 注意必须要有sudo,因为我是远程遥控的树莓派,没有权限运行程序,只能通过sudo获得权限,才不会报错 接着执行 sudo python3 auto

    2024年01月22日
    浏览(47)
  • 【树莓派4B为例的树莓派接口认识】

    1:SOC芯片 树莓派采用博通(Broadcom)BCM2711芯片作为SOC芯片,芯片上集成了CPU、GPU、DSP及SDRAM内存等,其中CPU和GPU共享内存,可以在系统中手工修改内存占比。4代的树莓派将内存变为了可选择的。 2:以太网接口 以太网接口让树莓派能以有线的方式接入计算机网络,这让我们

    2024年02月15日
    浏览(18)
  • 树莓派4B上安装Gitlab

    参考连接: 树莓派上使用 GitLab 搭建专业 Git 服务 | 树莓派实验室 gitlab reconfigure 卡住 ruby_block[wait for redis service socket] action run_芹菜学长的博客-CSDN博客 以及用到了讯飞星火 系统版本信息 1.进入 giblab安装页面gitlab/gitlab-ce - Installation · packages.gitlab.com,有个 quick install 复制 cu

    2024年02月10日
    浏览(28)
  • 在 树莓派 4B 中安装 Windows

    不过, Win11 应该也是可以的, 但是我在安装时, 因为路由器不带科学上网, 所以首先在欢迎页面就进不去, 给劝退了. 如果你的路由器可以科学上网的话, 那安装 Win11 的步骤也是相同的 目前仅支持网线, WiFi暂时无法使用, 等待更新 蓝牙可以正常使用 (看这个CPU调度感觉还蛮均衡的

    2024年02月02日
    浏览(31)
  • 树莓派4B(Raspberry Pi 4B)使用docker搭建springBoot/springCloud服务

    前提:本文基于Ubuntu,Java8,SpringBoot 2.6.13讲解 准备SpringBoot/SpringCloud项目jar包 用 maven 打包springBoot/springCloud项目,先在本地跑一跑,是否可以正常运行,特别注意哈!如果项目访问数据库,redis等运行在docker容器的服务,那么你的IP不能配置成树莓派IP,必须是docker network 内分

    2024年02月22日
    浏览(31)
  • 初始树莓派 + VMware17 安装树莓派(Raspberry Pi 4B/5)

    一年的考研生活过去了,充满了挑战与收获。如今,我又回到了编程的世界,准备以更新一期 树莓派 系列结合 人工智能/深度学习/计算机视觉/自然语言处理 作为我的毕业设计博客的主题。这一决定既是对过去学习的总结,也是对未来的展望。 树莓派(Raspberry Pi)是一款小

    2024年02月19日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包