一、硬件设备
(1)有人物联口红DTU DR154(RS 485版本)
这个DTU非常给力,不用插卡自带esim卡,送8年流量,配置的话通过小程序【联博士】蓝牙配置(手机扫描DTU背后的二维码即可,蓝牙密码也在背面),省去了连接电脑硬件配置的繁琐步骤。
(2)温度传感器:速灵科RSDS5 (RS485接口)
注意速灵科RSDS5 设备地址默认为1,如果有多个传感器,需要设置不同地址,后面会介绍如何设置。
(3)12V电源适配器:为DTU和温度传感器供电
(4)腾讯云服务器
本博客是以自有的云服务器数据处理为例进行介绍的,使用有人云的例子请参考官网教程
二、设备参数配置
速灵科RSDS5参数配置
如果只有一个传感器,使用默认的地址即可,然后写hex指令读取温度(重点参考官网教程:http://www.jingsudz.com/Product/14625081.html):
【问题来了】,如果有多个传感器设备,那么需要配置寄存器,功能码为【06】,官方例子如下:
例子中没有说CRC校验位如何计算,产品说明提供了一段计算代码:
但更简单的方法是打开传感器对应的调试软件(可以不接传感器,需要打开串口),选择需要设置的参数,在下方会显示对应的hex指令,比如我现在有一个传感器默认地址是1,要设置地址为2,那么对应的指令为:
01060001000259CB
,01
代表当前设备地址,06
为写寄存器功能码,0001
为设备地址寄存器(2个字节),0002
为写入的设备地址(2个字节,范围1-255,这里设置为2),59CB
为CRC校验位,2个字节。
以上设置完成后,再发送指令读取一下设备地址释放更改成功:
02 03 00 01 00 01 D5 F9
如果传感器还没接上DTU,那么可以使用速灵科对应的软件按照上述方法进行设置,如果已经安装在DTU上不想拆下来,也可以通过DTU自带的串口功能发送hex指令进行设置,首先通过小程序【联博士】连接DTU,选择【串口调试】,设置如下:
DTU DR154网络配置(心跳包,注册包)
DTU DR154支持4路独立socket连接,首先通过小程序【联博士】连接DTU,选择【参数设置】,设置如下:
- 工作模式:网络透传模式
- SOCK A开关:【地址】:‘xx.xx.xx.xx服务器地址’, 【端口号】:20086,【连接类型】:TCP协议,【连接方式】:长连接,如果有需要可以设置其他3路
- 注册包和心跳包的设置如下图所示:
三、服务器TCP服务
1.启用发送串口心跳包
如果启用了心跳包功能,会定时向服务器发送数据,此时在服务器上运行tcp服务用于监测和保存数据,代码如下:
# coding=gbk
'''
有人DTU DR154数据接收理服务,监听端口:20086,时间间隔300s
速灵科 RS485读取温度指令:
01 设备地址
03 读指令功能码
00 寄存器起始,地址高
00 寄存器起始,地址低
00 读取数量,高位
01 读取数量,低位
84 CRC校验,高位
0A CRC校验,低位
返回数据:
01 返回设备地址
03 功能码
02 返回数据数量,2个字节
01 字节1,高位
11 字节2,低位
79 校验位,高位
D8 校验位,低位
有人DTU DR154设备返回数据:ICCID码+速灵科温度传感器返回数据(hex格式),例如:
'89861122229046607070\x01\x03\x02\x00\xf9x\x06'
'''
import socketserver
import time
import os
import logging
logging.basicConfig(level=logging.DEBUG,#控制台打印的日志级别
filename='error.log',
filemode='a',##模式,有w和a,w就是写模式,每次都会重新写日志,覆盖之前的日志
#a是追加模式,默认如果不写的话,就是追加模式
format=
'%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'
#日志格式
)
class MyTCPHandler(socketserver.BaseRequestHandler):
"""
1.读取数据
2.分割数据
3.温度转换
4.数据显示
5.数据保存
"""
def save_data(self, data:str, file_root='data.txt'):
with open(file_root,'a+') as f:
f.write(data+'\n')
def handle(self):
# self.request is the TCP socket connected to the client
# self.data = self.request.recv(1024)
print("{} wrote:".format(self.client_address[0]))
self.request.sendall(b'\x01\x03\x00\x00\x00\x01\x84\x0A')
localtime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
# print("{} wrote:".format(self.client_address[0]))
try:
self.data = self.request.recv(1024)
IMEI, temp = self.data.split(b'\x01')[0].decode(), int(self.data.split(b'\x01\x03')[1][1:3].hex(),16)/10.0
# time, IMEI, temperature
tcpmsg = f'{localtime}, {IMEI}, {temp}'
# self.save_data(tcpmsg)
print(tcpmsg)
except:
print('Failed to parse data: ', self.data)
logging.debug(f'Failed to parse data!, raw data is: {self.data}')
if __name__ == "__main__":
# 腾讯云服务器内网地址
HOST, PORT = "xxx.xxx.xxx.xxx", 20086
socketserver.TCPServer.allow_reuse_address = True
# Create the server, binding to localhost on port 9999
with socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) as server:
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
# 设置线程数量
server.daemon_threads = True
server.max_threads = 4
server.serve_forever()
2.未启用发送串口心跳包
如果未启用心跳包功能,那么需要通过tcp服务器定时发送hex温度获取指令,python代码如下:
# coding:gbk
from socket import *
from time import ctime
import time
print("=====================时间戳TCP服务器=====================");
HOST, PORT = "xxx.xxx.xxx.xxx", 20086
BUFSIZ = 1024 #接收数据缓冲大小
ADDR = (HOST, PORT)
tcpSerSock = socket(AF_INET, SOCK_STREAM) #创建TCP服务器套接字
tcpSerSock.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
tcpSerSock.bind(ADDR) #套接字与地址绑定
tcpSerSock.listen(5) #监听连接,同时连接请求的最大数目
data = None
while True:
print('等待客户端的连接...')
tcpCliSock, addr = tcpSerSock.accept() #接收客户端连接请求
print('取得连接:', addr)
while True:
tcpCliSock.sendall(b'\x01\x03\x00\x00\x00\x01\x84\x0A') # 发送16进制指令
localtime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
try:
data = tcpCliSock.recv(BUFSIZ) #连续接收指定字节的数据,接收到的是字节数组
IMEI, temp = data.split(b'\x01')[0].decode(), int(data.split(b'\x01\x03')[1][1:3].hex(),16)/10.0
tcpmsg = f'{localtime}, {IMEI}, {temp}'
print(tcpmsg)
except:
print('Failed to parse data: ', data)
time.sleep(1) # 间隔1秒
tcpCliSock.close() #关闭与客户端的连接
tcpSerSock.close() #关闭服务器socket