如何用python读取RS485温度计数据

How to read RS485 Thermometer data with python

项目的思路是从RS485设备中获取数据,然后写入用于可视化。

我有 raspberry pi 4 4GB 内存和 this hat and this 传感器。

我不确定应该如何从传感器发送和接收数据。来自其文档:

THT-02 follows the RTU information frame protocol. In order to ensure the integrity of the information
frame, a pause time of 3.5 characters or more is required at the beginning and end of each information
frame, each byte of the information frame needs to be transmitted continuously. If there is a pause time greater than 1.5
characters, the sensor will treat it as invalid information and will not respond.

The sensor allows the host to use the function code 03 to read the temperature and humidity measurement
value of the sensor and other information. The information frame format of the 03 code is as follows:

Field Description Example

Slave address               01
Function code               03
Register address high byte  00
Register address low byte   00
High byte of query quantity 00
Low byte of query quantity  08
CRC check code low byte     44
CRC check code high byte    0C

Sensor response information frame

Slave address               01
Function code               03
Return the number of bytes  10
Temperature data high byte  00
Temperature data low byte   FA
Humidity data high byte     02
Low byte of humidity data   58
1 high byte reserved        00
1 low byte reserved         00
2 high byte reserved        00
2 low byte reserved         00
Address code high byte      00
Address code low byte       01
Baud rate high byte         25
Baud rate low byte          80
Hardware version high byte  06
Hardware version low byte   00
Software version high byte  00
Software version low byte   0A
CRC check code low byte     D4
CRC check code high byte    64

因为我不太了解 modbus 通信,所以我认为这些值应该是我发送到设备以获得有效响应的请求。 这是我尝试与 pyserial:

一起使用的代码
import time
import sys
import serial

ser = serial.Serial(
    port='/dev/ttyS0', baudrate=9600,
    parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS, xonxoff=False,
    rtscts=True, dsrdtr=True, timeout=3)

print(ser.name)
ser.write(br'\x01\x03\x00\x00\x00\x08\x44\x0C\r\n')
ret=str(ser.readlines())
print(ret)

这给我一个空列表。 来自 here 的消息具有类似于 11 03 006B 0003 7687

的结构

如何构造发送到设备的消息以获得正确的响应?

更新 所以当我读到 pi 上的 gpio 引脚不支持奇偶校验时。出于这个原因,我设法抓住了 these 之一。工作代码正在利用 pymodbus 库:

from pymodbus.client.sync import ModbusSerialClient as ModbusClient

client= ModbusClient(
    method = "RTU", 
    port = "/dev/ttyUSB0", 
    stopbits = 1, 
    bytesize = 8, 
    parity = 'N', 
    baudrate=9600)

client.strict = False
connection = client.connect()

FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)

rr = client.read_holding_registers(0, 2, unit=5)
log.debug(rr)
print(rr.registers)

client.close()

输出是一个包含温度计所有数据的列表。

更新 2 正如评论中所述,pymodbus 串行对于像我拥有的​​设备这样的快速响应来说速度很慢,所以我设法得到了一些回应:

import time, serial
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)

s = serial.Serial(
    port = '/dev/ttyS0',
    baudrate = 9600,
    parity = serial.PARITY_NONE,
    stopbits = serial.STOPBITS_ONE,
    bytesize = serial.EIGHTBITS,
    xonxoff = False,
    rtscts = False, 
    dsrdtr =True,
    timeout = 1
)

temp = b'\x05\x03\x00\x00\x00\x01\x85\x8e' 
#temp response \x03\x02\x00\xea\xc8\x0b => \x00\xea = 234 => 23.4 C

humidity = b'\x05\x03\x00\x01\x00\x01\xd4\x4e' 
# humidity response  \x03\x02\x01\xda\xc9\x8f => \x01\xdd = 474 => 47.4 %

GPIO.output(12, 1)
time.sleep(0.01)
#s.write(temp)
s.write(humidity)
time.sleep(0.01)
GPIO.output(12, 0)
res = s.readlines()
print('reading')
print(res)

GPIO.cleanup()
s.close()

这不是最优雅的解决方案,但至少我从中得到了回应。但同样,它并不总是适合测量 - 有时响应不包含整个消息,我最终得到无法使用的字符串。

更新 3 在与@MarcosG 讨论实现 pymodbuslibmodbus 通信方法后:

from pylibmodbus import ModbusRtu


# For Python 3.x you have to explicitly indicate ASCII enconding
client=ModbusRtu(
    device="/dev/ttyS0".encode("ascii"), 
    baud=9600, 
    parity="N".encode("ascii"), 
    data_bit=8, 
    stop_bit=1
)

#Read and set timeout
timeout_sec = client.get_response_timeout()
print(timeout_sec)
client.set_response_timeout(timeout_sec+1)

#Connect
client.connect()

SERVER_ID=5
BCM_PIN_DE=18
BCM_PIN_RE=17

#Set Slave ID number
client.set_slave(SERVER_ID)

#Enable RPi GPIO Functions
client.enable_rpi(1)

#Define pin numbers to be used as Read Enable (RE) and Drive Enable (DE)
client.configure_rpi_bcm_pins(BCM_PIN_DE,BCM_PIN_RE)

#Export pin direction (set as outputs)
client.rpi_pin_export_direction()

#Write Modbus registers, 10 starting from 0
#client.write_registers(0, [0]*10)

#Read 10 input registers starting from number 0
result=(client.read_registers(0, 1))

#Show register values
print(result[0])

#Release pins and close connection
client.rpi_pin_unexport_direction()
client.close()

pylibmodbus 不工作的问题是新的和原来的 libmodbus 库之间的冲突。设备响应代码(闪烁)并且在终端中我收到我正在寻找的值 - 247 => 24.7C.

这是 Modbus 上 software vs. hardware signaling 的教科书示例。

您的 USB-RS485 提供自动硬件信号,开箱即用。另一方面,您的帽子使用 RPi 上的一个串行端口和一条 GPIO 线路来发送信号,因此您需要自己在代码中切换线路(正如您已经注意到的那样效率很低并且无法可靠地工作对于大多数情况)或使用 libmodbus,如我在上面 link 的回答中所述。

只需按照我的回答中的步骤操作,并确保删除之前可能安装的 libmodbus 库的任何其他标准版本:

sudo apt-get purge libmodbus*

如果您想继续使用 Python,您也可以将我的 python wrapper 用于 libmodbus

正如我在回答中所解释的那样,这个技巧(软件信号)应该可以可靠地用于家庭项目,但我不建议将其用于关键任务或任何其他与安全相关的应用程序。