简体   繁体   中英

How to read RS485 Thermometer data with python

The idea of the project is to get the data from the RS485 devices and write it for visualisation later.

I have raspberry pi 4 with 4GB RAM with this hat and this sensor.

I'm not sure how should I send and receive the data from the sensor. From its documentation:

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

As I don't really get the idea of the modbus communication I think that these values should be the request I send to the device to get a valid responce. This is the code I'm trying to use with 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)

This gets me an empty list. From here the message has a structure like 11 03 006B 0003 7687

How do I structure the messages to the device to get a proper response?

UPDATE So as I read the gpio pins on the pi don't support parity. For that reason I managet to get a hold on one of these . With that the working code is utilising the pymodbus library:

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()

Output is a list with all the data from the thermometer.

UPDATE 2 As stated in the comments the pymodbus serial is slow for a fast response like the device I have, so I managed to get a some response with this:

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()

It's not the most elegant solution, but at least I get a responses from it. But again it's not always good for measurment - sometimes the response doesn't contain the whole message and I end up with unusable string.

UPDATE 3 After talking with @MarcosG for implementing the pymodbus and libmodbus method for communication:

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()

The problem with the pylibmodbus not working was a conflict between the new and the original libmodbus library. The device responds to the code (blinks) and in terminal I receive the value I'm looking for - 247 => 24.7C .

This is a textbook example of the software vs. hardware signaling on Modbus.

Your USB-RS485 provides automatic hardware signaling and it works out of the box. On the other hand, your hat uses one of the serial ports on your RPi and a GPIO line for signaling so you need to either toggle the line yourself within your code (which is, as you have already noticed very inefficient and will not work reliably for most scenarios) or use libmodbus as explained in my answer on the link above.

Just follow the steps on my answer and make sure you remove any other standard version of the libmodbus library you might have installed before with:

sudo apt-get purge libmodbus*

You can also use my python wrapper for libmodbus if you want to keep working with Python.

As I explained in my answer, this trick (software signaling) should work reliably for at-home projects but I would not recommend it for mission-critical or any other safety-related applications.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM