文章目录
任务目标
在消化学习 server.c和client.c 套接字代码、python-modbus-over-tcp.py 代码基础上,试着用C编程完成modbus协议,从云端服务器读取温湿度数据。
实验材料
- PyCharm2022
- mysql
- wireshark
- 小熊猫C++软件
Modbus协议简述
(1)协议概述:
Modbus协议是一种串行通信协议,是Modicon公司(现在的施耐德电气Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表的。Modbus协议是应用层协议,已经成为工业领域通信协议的业界标准,是工业电子设备之间常用的连接方式。
Modbus是一个master/slave架构的协议,有一个节点是master节点,其他使用Modbus协 议参与通信的节点是slave节点,每一个slave设备都有一个唯一的地址。只有被指定为master节点的节点可以启动一个命令。所有的Modbus数据帧包含了校验码,保证传输的正确性。基本的ModBus命令能指令一个slave设备改变它的寄存器的某个值,控制或者读取一个I/O端口,以及指挥设备回送一个或者多个其寄存器中的数据。
(2)modbus主从协议原理
Modbus串行链路协议是一个主-从协议。在同一时间,只能将一个主站连接到总线,将一个或多个从站(最大数量为247)连接到相同的串行总线。Modbus 通讯总是由主站发起,当从站没有收到来自主站的请求时,将不会发送数据。主站同时只能启动一个Modbus事务处理,从站之间不能相互通信。
(3)modbus串行传输模式
RTU模式:每个8Bit字节包含两个4Bit的十六进制字符, 其优点是在同样的波特率下,可比ASCII方式传送更多的数据,但是每个信息必须以连续的数据流传输。
ASCII模式:信息中的每个8 Bit字节需2个ASCII字符,其优点是准许字符的传输间隔达到1s 而不产生错误。
使用python示例代码,完成Modbus协议从云端获取信息
这是使用Python示例代码来获取云端信息的方法,请求数据的方式为UDP,注意要修改成自己的数据库名称和密码。
import socket
import sys
import struct
import time
import pymysql
import datetime
#实现采集温度传感器和静力水准仪
def crc16(string):
#data = bytes.fromhex(string)
data=string
crc = 0xFFFF
for pos in data:
crc ^= pos
for i in range(8):
if ((crc & 1) != 0):
crc >>= 1
crc ^= 0xA001
else:
crc >>= 1
return hex(((crc & 0xff) << 8) + (crc >> 8))
#连接数据库
def MySQLConnect():
connection = pymysql.connect(
host='localhost', # IP,MySQL数据库服务器IP地址
port=3306, # 端口,默认3306,可以不输入
user='root', # 数据库用户名
password='123456', # 数据库登录密码
database='sensor', # 要连接的数据库
charset='utf8' # 字符集,注意不是'utf-8'
)
return connection
#插入温湿度采集到数据库
def AddData1(wd,sd,time):
# 连接数据库
conn = MySQLConnect()
# 使用cursor()方法创建一个游标对象cursor
cursor = conn.cursor()
# 插入数据库
sql = "INSERT INTO temp_hum_sensor(temp, hum, time) VALUES (%s,%s,%s); "
cursor.execute(sql, [wd, sd, time])
# 提交事务
conn.commit()
# 关闭游标
cursor.close()
# 关闭数据库连接
conn.close()
#插入静力水准仪采集到数据库
def AddData2(id,water_level,time):
# 连接数据库
conn = MySQLConnect()
# 使用cursor()方法创建一个游标对象cursor
cursor = conn.cursor()
# 插入数据库
sql = "INSERT INTO static_level(id, water_level, time) VALUES (%s,%s,%s); "
cursor.execute(sql, [id, water_level, time])
# 提交事务
conn.commit()
# 关闭游标
cursor.close()
# 关闭数据库连接
conn.close()
#采集温度传感器的数据
def getDataTemp(cmd):
#flag标志采集的次数
flag=0
last = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print(last)
last1 = time.time()
cmd = bytes.fromhex(cmd)
#print(cmd)
crc = crc16(cmd)
crc = bytes.fromhex(crc[2:])
#得到发送的指令(modbus协议定义内容+校验)
cmd = cmd + crc
udp.sendto(cmd, ('demo-monitor.igong.com', 8001))
try:
data, addr = udp.recvfrom(8192)
except socket.timeout:
print("超时")
sys.exit(1)
crc = data[-2:]
crc1 = crc16(data[:-2])
crc1 = crc1[2:]
if (len(crc1) == 3):
crc1 = '0' + crc1
crc1 = bytes.fromhex(crc1)
# print(crc1)
if crc != crc1:
print("CRC16校验失败!")
sys.exit(2)
# 解析数据
wd, sd = struct.unpack('>ii', data[4:12])
wd = wd / 100.
print("温度:", wd, "湿度:", sd)
AddData1(wd, sd, last)
flag=flag+1
while True:
now= time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
#print(s)
now1=time.time()
#每隔5s获取一次数据