1. 前言
工业4.0的浪潮下,许多中小型制造业企业渴望通过数字化转型谋求新的发展动力,然而,在转型之路上常常会面临一个问题:传统数据采集设备数量多、种类杂,不支持比较新颖的现场总线协议或者通信技术,最终导致企业难以迈出数字化转型的第一步。那么,有什么方法可以同时接入数量庞大的传统数据采集设备并同时满足不同数据采集设备的通讯需求呢?
传统数据采集设备普遍具有 RS232/RS485 或以太网接口,支持串口通信、MODBUS RTU协议、MODBUS TCP协议,所以最基础的解决方案就是通过 MODBUS 协议把传统设备数据采集上来,然后通过 HTTP 或 MQTT 等协议,把数据发送到云平台。这样子就可以满足不同种类的传统数据采集设备的通讯需求,掌握一个设备,就可以完成整个车间乃至整个工厂的数据上云。
虹科工业树莓派同时满足上述解决方案中的硬件接口和通讯协议要求,接下来我们一起来看看具体如何实现该方案。
2. 虹科工业树莓派实现方案
本方案中虹科工业树莓派通过MODBUS TCP协议连接多个底层设备的数据,通过 HTTP 协议将数据发送至 OneNET 平台。
2.1 配置 MODBUS TCP 主站
为每一个从站设备配置一个虚拟主站,填写相应从站IP地址,并配置寄存器地址及其相应的功能码。
2.2 编写程序,获取数据,并上传至OneNET平台。
2.2.1 获取相应寄存器数据
这分别涉及到对设备1和设备2数据的直接读取,对设备3数据的数据处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #设备1 Modbus数据 Device1 = [] Device1.append(getByteOffset( 'Input_Word_1' )) #设备1数据1 Device1.append(getByteOffset( 'Input_Word_1' )) #设备1数据2 #设备2 Modbus数据 Device2 = [] Device2.append(getByteOffset( 'Input_Word_1_i03' )) #设备2数据1 Device2.append(getByteOffset( 'Input_Word_1_i03' )) #设备2数据2 #IEE754,接收Device3从站浮点型数据,合并两个寄存器的十进制转浮点数 def int2float(): data_Int = readFromName( 'Input_Word_1_i04' ) data_float = readFromName( 'Input_Word_2_i04' ) datalist = [] datalist.append(data_Int>> 8 & 0xff ) datalist.append(data_Int & 0xff ) datalist.append(data_float>> 8 & 0xff ) datalist.append(data_float>> 8 & 0xff ) print (struct.unpack( '>f' ,struct.pack( '4B' , * datalist))[ 0 ]) return datalist |
2.2.2 配置云平台凭证信息
1 2 3 4 | #OneNet云平台连接凭证 APIKEY = '…' #改成你的APIKEY apiurl = '…' #改成你自己的 apiheaders = { 'api-key' : APIKEY, 'Content-Length' : '120' } |
2.2.3 上传数据至云平台
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #HTTP def http_put(): Data1 = Device1[ 0 ] Data2 = Device1[ 1 ] Data3 = Device2[ 0 ] Data4 = Device2[ 1 ] Data5 = int2float() CurTime = datetime.datetime.now() payload1 = { 'datastreams' : [{ "id" : "device1_1" , "datapoints" : [{ "at" : CurTime.isoformat(), "value" : Data1}]}]} payload2 = { 'datastreams' : [{ "id" : "device1_2" , "datapoints" : [{ "at" : CurTime.isoformat(), "value" : Data2}]}]} payload3 = { 'datastreams' : [{ "id" : "device2_1" , "datapoints" : [{ "at" : CurTime.isoformat(), "value" : Data3}]}]} payload4 = { 'datastreams' : [{ "id" : "device2_2" , "datapoints" : [{ "at" : CurTime.isoformat(), "value" : Data4}]}]} payload5 = { 'datastreams' : [{ "id" : "device3" , "datapoints" : [{ "at" : CurTime.isoformat(), "value" : Data5}]}]} print ( "当前时间为: %s" % CurTime.isoformat()) print ( "上传值为: %.3f" % Data1) print ( "上传值为: %.3f" % Data2) print ( "上传值为: %.3f" % Data3) print ( "上传值为: %.3f" % Data4) jdata = json.dumps(payload1) # 对数据进行JSON格式化编码 # 打印json内容 print (jdata) r1 = requests.post(apiurl, headers = apiheaders, data = json.dumps(payload1)) r2 = requests.post(apiurl, headers = apiheaders, data = json.dumps(payload2)) r3 = requests.post(apiurl, headers = apiheaders, data = json.dumps(payload3)) r4 = requests.post(apiurl, headers = apiheaders, data = json.dumps(payload4)) r5 = requests.post(apiurl, headers = apiheaders, data = json.dumps(payload5)) return r1,r2,r3,r4,r5 |
完整源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | #!/usr/bin/python3 # -*- coding: utf-8 -*- import sys import urllib.request import json import time import datetime import random import requests from doctest import OutputChecker from os import P_PID from time import sleep from threading import Thread from tkinter import W from myCtrlLib import * #OneNet云平台连接凭证 APIKEY = '…' #改成你的APIKEY apiurl = '…' #改成你自己的 apiheaders = { 'api-key' : APIKEY, 'Content-Length' : '120' } #设备1 Modbus数据 Device1 = [] Device1.append(getByteOffset( 'Input_Word_1' )) #设备1数据1 Device1.append(getByteOffset( 'Input_Word_1' )) #设备1数据2 #设备2 Modbus数据 Device2 = [] Device2.append(getByteOffset( 'Input_Word_1_i03' )) #设备2数据1 Device2.append(getByteOffset( 'Input_Word_1_i03' )) #设备2数据2 #IEE754,接收Device3从站浮点型数据,合并两个寄存器的十进制转浮点数 def int2float(): data_Int = readFromName( 'Input_Word_1_i04' ) data_float = readFromName( 'Input_Word_2_i04' ) datalist = [] datalist.append(data_Int>> 8 & 0xff ) datalist.append(data_Int & 0xff ) datalist.append(data_float>> 8 & 0xff ) datalist.append(data_float>> 8 & 0xff ) print (struct.unpack( '>f' ,struct.pack( '4B' , * datalist))[ 0 ]) return datalist #HTTP def http_put(): Data1 = Device1[ 0 ] Data2 = Device1[ 1 ] Data3 = Device2[ 0 ] Data4 = Device2[ 1 ] Data5 = int2float() CurTime = datetime.datetime.now() payload1 = { 'datastreams' : [{ "id" : "device1_1" , "datapoints" : [{ "at" : CurTime.isoformat(), "value" : Data1}]}]} payload2 = { 'datastreams' : [{ "id" : "device1_2" , "datapoints" : [{ "at" : CurTime.isoformat(), "value" : Data2}]}]} payload3 = { 'datastreams' : [{ "id" : "device2_1" , "datapoints" : [{ "at" : CurTime.isoformat(), "value" : Data3}]}]} payload4 = { 'datastreams' : [{ "id" : "device2_2" , "datapoints" : [{ "at" : CurTime.isoformat(), "value" : Data4}]}]} payload5 = { 'datastreams' : [{ "id" : "device3" , "datapoints" : [{ "at" : CurTime.isoformat(), "value" : Data5}]}]} print ( "当前时间为: %s" % CurTime.isoformat()) print ( "上传值为: %.3f" % Data1) print ( "上传值为: %.3f" % Data2) print ( "上传值为: %.3f" % Data3) print ( "上传值为: %.3f" % Data4) jdata = json.dumps(payload1) # 对数据进行JSON格式化编码 # 打印json内容 print (jdata) r1 = requests.post(apiurl, headers = apiheaders, data = json.dumps(payload1)) r2 = requests.post(apiurl, headers = apiheaders, data = json.dumps(payload2)) r3 = requests.post(apiurl, headers = apiheaders, data = json.dumps(payload3)) r4 = requests.post(apiurl, headers = apiheaders, data = json.dumps(payload4)) r5 = requests.post(apiurl, headers = apiheaders, data = json.dumps(payload5)) return r1,r2,r3,r4,r5 #运行 try : while True : time.sleep( 2 ) resp = http_put() resp1 = resp[ 0 ] resp2 = resp[ 1 ] resp3 = resp[ 2 ] resp4 = resp[ 3 ] resp5 = resp[ 4 ] print ( "OneNET请求结果(resp1):\n %s" % resp1) print ( "OneNET请求结果(resp2):\n %s" % resp2) print ( "OneNET请求结果(resp2):\n %s" % resp3) print ( "OneNET请求结果(resp2):\n %s" % resp4) print ( "OneNET请求结果(resp2):\n %s" % resp5) except : print ( "Unexpected error:" , sys.exc_info()[ 0 ]) raise |
源码中涉及的 getByteOffset() 函数和 readFromName() 函数来自 myCtrlLib 库文件,属于虹科自行开发的库文件,有兴趣的可以联系我们。
2.3 最终效果
3. 总结
基于我们提供的python库,不需要能力非常强大的软件开发工程师,就可以轻便地实现我们代码编写。
除此之外,虹科工业树莓派支持 MODBUS RTU 和 MODBUS TCP,并可以同时扩展30个从站设备,这就表示一个虹科工业树莓派可以同时采集多个传统设备的数据,并将其发送至云平台,在一定程度可以减少成本,以简便的方式帮助企业加速实现数字化转型。程序中包含了简单的MODBUS数据转换,有更高需求的话,可以基于工业树莓派做更复杂的数据处理以及边缘计算。
发表评论