?本文章为原创,转载请注明出处!
登录平台:IOTOS?爱投斯物联中台
账号:iotos_test ? ?密码:iotos123
代码地址:IOTOSDK-Python: IOTOS Python版本SDK,自带原生接口和采集引擎 (gitee.com)
目录
前言
驱动目的
适用范围
使用示例
驱动代码
驱动解析
????????Modbus协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络例如以太网)和其它设备之间可以通信。它已经成为一通用工业标准。有了它,不同厂商生产的控制设备可以连成工业网络,进行集中监控此协议定义了一个控制器能认识使用的消息结构,而不管它们是经过何种网络进行通信的。它描述了一控制器请求访问其它设备的过程,如果回应来自其它设备的请求,以及怎样侦测错误并记录。它制定了消息域格局和内容的公共格式。
????????Modbus具有两种串行传输模式:分别为ASCII和RTU。Modbus是一种单主站的主从通信模式,Modbus网络上只能有一个主站存在,主站在Modbus网络上没有地址,每个从站必须有唯一的地址,从站的地址范围为0 - 247,其中0为广播地址,从站的实际地址范围为1 - 247。
????????Modbus RTU通信以主从的方式进行数据传输,在传输的过程中Modbus RTU主站是主动方,即主站发送数据请求报文到从站,Modbus RTU从站返回响应报文。
????????驱动将以串口连接的易事特UPS电源EA660G3的数据拿到并上云(该型号的UPS电源为非标准的modbus协议)
????????易事特UPS电源EA660G3,以串口连接工控机或电脑
-
使用示例 - ?首先,将易事特UPS电源的串口连接至工控机或者电脑上:
- ?进入爱投斯中台,账号为iotos_test,密码为iotos123,创建网关
- 创建设备示例点击【我的设备】 -> 【通信网关】 -> 【设备实例】->【创建设备】
- 填写【设备名称】、选择刚刚创建的【模板驱动】和【通信网关】。参数topic为mqtt订阅的topic,用来拿去设备的数据,host和port为mqtt服务器的地址和端口号
- ?创建数据点,点击【我的设备】 -> 【通信网关】 -> 【设备实例】 -> 【数据点】,并在【设备实例】下拉列表选择刚刚创建的设备实例
?
?并在高级配置中配置数据点的相关标志
?第一个点配置"private"属性用于驱动识别表示第一点
?其余的点配置point和index,point表示指向哪个点,index表示取第几个数据,下面示例为第二个数据点,这里point填第一个点的oid,index填1,后面的数据点一次,更改index获取不同位的数据即可
- ?在【我的设备】 -> 【通信网关】中找到刚才创建的网关,点击【高级】
- 点击 【我的设备】 -> 【通信网关】 -> 【设备实例】->【数据点】,选择刚才创建的设备实例
-
驱动代码 - 由于UPS电源与工控机或者电脑是串口连接,数据需要上报到线上的中台,但是工控机配置过低无法运行驱动文件,所以中间利用mqtt进行了数据的相关转移,若UPS电源连接的工控机或者电脑可以直接运行驱动则无需利用mqtt进行数据转移,可以直接上传至中台,这里的代码是包含了mqtt服务的。
- 数据的获取和mqtt的发布(脚本)
# encoding: utf-8
# python 2.7
import random
import time
import threading
from paho.mqtt import client as mqtt_client
import serial
from serial import Serial
import modbus_tk.modbus_rtu as modbus_rtu
class info():
def __init__(self):
self.broker = '***.***.***.***' #mqtt服务器地址,需要自行填写
self.port = *** #mqtt的端口,需要自行填写
self.topic_airc = "/python/mqtt/ups"
# generate client ID with pub prefix randomly
self.client_id = 'admin123'
def connect_mqtt(self):
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to MQTT Broker!")
else:
print("Failed to connect, return code %d\n", rc)
client = mqtt_client.Client(self.client_id)
client.on_connect = on_connect
client.connect(self.broker, self.port,60)
return client
def publish(self,client):
while True:
try:
time.sleep(3)
data_rtu = com_get_info()
msg1 = data_rtu.master()
msg_1 = "{}".format(msg1)
result1 = client.publish(self.topic_airc, msg_1)
status1 = result1[0]
if status1 == 0:
print("Send {} to topic_airc {}".format(msg_1,self.topic_airc))
else:
print("Failed to send message to topic_airc {}".format(self.topic_airc))
except Exception,e:
pass
def run(self):
self.client = self.connect_mqtt()
self.client.loop_start()
self.publish(self.client)
class com_get_info():
def master(self):
try:
ser = serial.Serial("com3", 9600, timeout=0.5)
ser.write(b'\xEA\x01\x01\xEA\x01\x81\x00\x82\xEF')
pre_data = ser.readline()
print pre_data
data = ''.join([hex(ord(c)) for c in pre_data])
data1 = data.split('0x')
data2 = ''
for i in data1:
if len(i) == 1:
data2 += '0'
data2 += i
U101 = int(data2[12: 16]) * 0.1
U102 = int(data2[16: 20]) * 0.1
U103 = int(data2[20: 24]) * 0.1
U104 = int(data2[24: 28]) * 0.1
U105 = int(data2[28: 32]) * 0.1
U106 = int(data2[32: 36]) * 0.1
U107 = int(data2[36: 40]) * 0.1
U108 = int(data2[40: 44]) * 0.1
U109 = int(data2[44: 48]) * 0.1
U110 = int(data2[48: 52]) * 0.1
U111 = int(data2[52: 56]) * 0.1
U112 = int(data2[56: 60]) * 0.1
# 输出R相视在功率
A = int(data2[84: 87])
if int(data2[87]) > 0:
U113 = A + 1
# 输出S相视在功率
B = int(data2[88: 91])
if int(data2[91]) > 0:
U114 = B + 1
# 输出T相视在功率
C = int(data2[92: 95])
if int(data2[94]) > 0:
U115 = C + 1
# 1号电池组电压
U116 = int(data2[60: 64]) * 0.1
# 2号电池组电压
U117 = int(data2[64: 68]) * 0.1
# 逆变输出频率
U120 = int(data2[76: 80]) * 0.1
# 主输入状态
if U101 > 150:
U123 = 1
else:
U123 = 0
# 逆变状态
if U104 > 150:
U124 = 1
else:
U124 = 0
# 旁路输入状态
if U107 > 150:
U125 = 1
else:
U125 = 0
# 电池供电状态
if U101 > 100:
U126 = 1
else:
U126 = 0
# 整流器状态
if U125 == 1 and U126 == 0:
U127 = 1
else:
U127 = 0
# 旁路供电状态
if U125 == 1 and U110 > 0.1 and U124 == 0:
U128 = 1
else:
U128 = 0
# 输入C相过压
if U103 > 250:
U0113 = 1
else:
U0113 = 0
# 输入C相欠压
if U103 < 180:
U0114 = 1
else:
U0114 = 0
# 输入B相过压
if U102 > 250:
U0115 = 1
else:
U0115 = 0
# 输入B相欠压
if U102 < 180:
U0116 = 1
else:
U0116 = 0
# 输入A相过压
if U101 > 250:
U0117 = 1
else:
U0117 = 0
# 输入A相欠压
if U101 < 180:
U0118 = 1
else:
U0118 = 0
# 旁路C相过压
if U109 > 250:
U0123 = 1
else:
U0123 = 0
# 旁路C相欠压
if U109 < 180:
U0124 = 1
else:
U0124 = 0
# 旁路B相过压
if U108 > 250:
U0125 = 1
else:
U0125 = 0
# 旁路B相欠压
if U108 < 180:
U0126 = 1
else:
U0126 = 0
# 旁路A相过压
if U107 > 250:
U0127 = 1
else:
U0127 = 0
# 旁路A相欠压
if U107 < 180:
U0128 = 1
else:
U0128 = 0
# 逆变C相过压
if U106 > 250:
U0133 = 1
else:
U0133 = 0
# 逆变C相欠压
if U106 < 180:
U0134 = 1
else:
U0134 = 0
# 逆变B相过压
if U105 > 250:
U0135 = 1
else:
U0135 = 0
# 逆变B相过压
if U105 < 180:
U0136 = 1
else:
U0136 = 0
# 逆变A相过压
if U104 > 250:
U0137 = 1
else:
U0137 = 0
# 逆变A相欠压
if U104 < 180:
U0138 = 1
else:
U0138 = 0
data_code_false = (U101, U102, U103, U104, U105, U106, U107, U108, U109, U110, U111, U112, A, B, C, U116, U117, U120, U123, U124, U125, U126, U127, U128, U0113, U0114, U0115, U0116, U0117, U0118, U0123, U0124, U0125, U0126, U0127, U0128, U0133, U0134, U0135, U0136, U0137, U0138,)
print len(data_code_false)
return data_code_false
except Exception,e:
pass
if __name__ == '__main__':
info = info()
self = info.run()
- mqtt的订阅,并将获得的数据上传至中台(驱动文件)
#!coding:utf8
import sys
sys.path.append("..")
from driver import *
import json
import time
import random
import paho.mqtt.client as mqtt
reload(sys)
sys.setdefaultencoding('utf8')
def on_subscribe(client, userdata, mid, granted_qos):
print("On Subscribed: qos = %d" % granted_qos)
def on_disconnect(client, userdata, rc):
if rc != 0:
print("Unexpected disconnection %s" % rc)
_msg=''
class XyDriver(IOTOSDriverI):
def on_connect(self,client, userdata, flags, rc):
print("Connected with result code " + str(rc))
client.subscribe(self._topic) # 订阅消息
def on_message(self, client, userdata, msg):
self.debug(msg.topic + " " + str(msg.qos) + " " + str(msg.payload))
# a = msg.payload
global _msg
_msg=tuple(eval(msg.payload))
self.debug('data is collected')
self.debug(_msg)
def InitComm(self, attrs):
self.online(True)
self.setPauseCollect(False)
self.setCollectingOneCircle(False)
#获取中台填写的mqtt地址,端口以及需要订阅的topic
self._topic = self.sysAttrs['config']['param']['topic']
self._host = self.sysAttrs['config']['param']['host']
self._port = self.sysAttrs['config']['param']['port']
self.client=mqtt.Client()
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self.client.on_subscribe = on_subscribe
self.client.on_disconnect = on_disconnect
self.client.username_pw_set(username='', password='')
# 连接到服务器
self.client.connect(host=self._host.encode('utf-8'), port=self._port, keepalive=60)
self.client.loop_start()
def Collecting(self, dataId):
try:
# self.debug('111111111111111111111111111')
self.client.on_message=self.on_message
# self.debug('22222222222222222222222222')
self.debug(_msg)
if 'private' in self.data2attrs[dataId]['config']['param'] and self.data2attrs[dataId]['config']['param']['private']=='first_point':
if type(_msg)==tuple:
return _msg
else:
return ()
except Exception as e:
self.debug('error'+e)
-
驱动解析 - 编写环境为python2(python3也可以),首先需要导入modbus_tk和paho.mqtt等、数据解析和modbus通讯的相关包
# encoding: utf-8
# python 3.6
import random
import time
import threading
from paho.mqtt import client as mqtt_client
import serial
from serial import Serial
import modbus_tk.modbus_rtu as modbus_rtu
- ?利用modbus_tk里面的modbus_rtu打开服务器的串口并向其发送指令,其中serialObjTmp = serial.Serial(port='com11', baudrate=9600, timeout=0.5)中port表示串口号,baudrate表示波特率,timeout表示超时时间。打开串口后利用ser.write(b'\xEA\x01\x01\xEA\x01\x81\x00\x82\xEF')向串口发送数据,需要发送十六进制的bytes类型。读取数据后进行数据的处理
class com_get_info():
def master(self):
try:
ser = serial.Serial("com3", 9600, timeout=0.5)
ser.write(b'\xEA\x01\x01\xEA\x01\x81\x00\x82\xEF')
pre_data = ser.readline()
print pre_data
data = ''.join([hex(ord(c)) for c in pre_data])
data1 = data.split('0x')
data2 = ''
for i in data1:
if len(i) == 1:
data2 += '0'
data2 += i
U101 = int(data2[12: 16]) * 0.1
U102 = int(data2[16: 20]) * 0.1
U103 = int(data2[20: 24]) * 0.1
U104 = int(data2[24: 28]) * 0.1
U105 = int(data2[28: 32]) * 0.1
U106 = int(data2[32: 36]) * 0.1
U107 = int(data2[36: 40]) * 0.1
U108 = int(data2[40: 44]) * 0.1
U109 = int(data2[44: 48]) * 0.1
U110 = int(data2[48: 52]) * 0.1
U111 = int(data2[52: 56]) * 0.1
U112 = int(data2[56: 60]) * 0.1
# 输出R相视在功率
A = int(data2[84: 87])
if int(data2[87]) > 0:
U113 = A + 1
# 输出S相视在功率
B = int(data2[88: 91])
if int(data2[91]) > 0:
U114 = B + 1
# 输出T相视在功率
C = int(data2[92: 95])
if int(data2[94]) > 0:
U115 = C + 1
# 1号电池组电压
U116 = int(data2[60: 64]) * 0.1
# 2号电池组电压
U117 = int(data2[64: 68]) * 0.1
# 逆变输出频率
U120 = int(data2[76: 80]) * 0.1
# 主输入状态
if U101 > 150:
U123 = 1
else:
U123 = 0
# 逆变状态
if U104 > 150:
U124 = 1
else:
U124 = 0
# 旁路输入状态
if U107 > 150:
U125 = 1
else:
U125 = 0
# 电池供电状态
if U101 > 100:
U126 = 1
else:
U126 = 0
# 整流器状态
if U125 == 1 and U126 == 0:
U127 = 1
else:
U127 = 0
# 旁路供电状态
if U125 == 1 and U110 > 0.1 and U124 == 0:
U128 = 1
else:
U128 = 0
# 输入C相过压
if U103 > 250:
U0113 = 1
else:
U0113 = 0
# 输入C相欠压
if U103 < 180:
U0114 = 1
else:
U0114 = 0
# 输入B相过压
if U102 > 250:
U0115 = 1
else:
U0115 = 0
# 输入B相欠压
if U102 < 180:
U0116 = 1
else:
U0116 = 0
# 输入A相过压
if U101 > 250:
U0117 = 1
else:
U0117 = 0
# 输入A相欠压
if U101 < 180:
U0118 = 1
else:
U0118 = 0
# 旁路C相过压
if U109 > 250:
U0123 = 1
else:
U0123 = 0
# 旁路C相欠压
if U109 < 180:
U0124 = 1
else:
U0124 = 0
# 旁路B相过压
if U108 > 250:
U0125 = 1
else:
U0125 = 0
# 旁路B相欠压
if U108 < 180:
U0126 = 1
else:
U0126 = 0
# 旁路A相过压
if U107 > 250:
U0127 = 1
else:
U0127 = 0
# 旁路A相欠压
if U107 < 180:
U0128 = 1
else:
U0128 = 0
# 逆变C相过压
if U106 > 250:
U0133 = 1
else:
U0133 = 0
# 逆变C相欠压
if U106 < 180:
U0134 = 1
else:
U0134 = 0
# 逆变B相过压
if U105 > 250:
U0135 = 1
else:
U0135 = 0
# 逆变B相过压
if U105 < 180:
U0136 = 1
else:
U0136 = 0
# 逆变A相过压
if U104 > 250:
U0137 = 1
else:
U0137 = 0
# 逆变A相欠压
if U104 < 180:
U0138 = 1
else:
U0138 = 0
data_code_false = (U101, U102, U103, U104, U105, U106, U107, U108, U109, U110, U111, U112, A, B, C, U116, U117, U120, U123, U124, U125, U126, U127, U128, U0113, U0114, U0115, U0116, U0117, U0118, U0123, U0124, U0125, U0126, U0127, U0128, U0133, U0134, U0135, U0136, U0137, U0138,)
print len(data_code_false)
return data_code_false
except Exception,e:
pass
- 读取数据并处理后返回,利用mqtt将其发布到对应的topic
class info():
def __init__(self):
self.broker = '***.***.***.***' #mqtt服务器地址,需要自行填写
self.port = *** #mqtt的端口,需要自行填写
self.topic_airc = "/python/mqtt/ups" #准备发布到mqtt的topic
# generate client ID with pub prefix randomly
self.client_id = 'admin123'
def connect_mqtt(self):
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to MQTT Broker!")
else:
print("Failed to connect, return code %d\n", rc)
client = mqtt_client.Client(self.client_id)
client.on_connect = on_connect
client.connect(self.broker, self.port,60)
return client
def publish(self,client):
while True:
try:
time.sleep(3)
#com_get_info()函数获取从串口拿到的数据
data_rtu = com_get_info()
msg1 = data_rtu.master()
msg_1 = "{}".format(msg1)
#将msg1发布到对应的topic
result1 = client.publish(self.topic_airc, msg_1)
status1 = result1[0]
if status1 == 0:
print("Send {} to topic_airc {}".format(msg_1,self.topic_airc))
else:
print("Failed to send message to topic_airc {}".format(self.topic_airc))
except Exception,e:
pass
def run(self):
self.client = self.connect_mqtt()
self.client.loop_start()
self.publish(self.client)
- 驱动文件(订阅并将数据上传至中台),首先先导入mqtt和驱动运行的依赖文件
#!coding:utf8
import sys
sys.path.append("..")
from driver import *
import json
import time
import random
import paho.mqtt.client as mqtt
reload(sys)
sys.setdefaultencoding('utf8')
- 初始化mqtt和驱动,获取中台上设备示例的配置并且连接至mqtt
def on_subscribe(client, userdata, mid, granted_qos):
print("On Subscribed: qos = %d" % granted_qos)
def on_disconnect(client, userdata, rc):
if rc != 0:
print("Unexpected disconnection %s" % rc)
_msg=''
class XyDriver(IOTOSDriverI):
def on_connect(self,client, userdata, flags, rc):
print("Connected with result code " + str(rc))
client.subscribe(self._topic) # 订阅消息
def on_message(self, client, userdata, msg):
self.debug(msg.topic + " " + str(msg.qos) + " " + str(msg.payload))
# a = msg.payload
global _msg
_msg=tuple(eval(msg.payload))
self.debug('data is collected')
self.debug(_msg)
def InitComm(self, attrs):
self.online(True)
self.setPauseCollect(False)
self.setCollectingOneCircle(False)
self._topic = self.sysAttrs['config']['param']['topic']
self._host = self.sysAttrs['config']['param']['host']
self._port = self.sysAttrs['config']['param']['port']
self.client=mqtt.Client()
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self.client.on_subscribe = on_subscribe
self.client.on_disconnect = on_disconnect
self.client.username_pw_set(username='', password='')
# 连接到服务器
self.client.connect(host=self._host.encode('utf-8'), port=self._port, keepalive=60)
self.client.loop_start()
def Collecting(self, dataId):
try:
# self.debug('111111111111111111111111111')
self.client.on_message=self.on_message
# self.debug('22222222222222222222222222')
self.debug(_msg)
#只采集第一个点并将订阅得到的数据返回
if 'private' in self.data2attrs[dataId]['config']['param'] and self.data2attrs[dataId]['config']['param']['private']=='first_point':
if type(_msg)==tuple:
return _msg
else:
return ()
except Exception as e:
self.debug('error'+e)
?至此,易事特UPS电源驱动的开发完成,即可实现精密空调的数据上云
|