Python – Raspberry Pi RS485/Uart Modbus

gpio, modbus, python, raspberry-pi, rs485

I'm attempting to get an RS485 adapter connected at the UART to communicate via modbus on a Raspberry Pi. My end goal is to have all this working with a Node application, but so far my dev has been with Python.

My hardware connection looks like:

[Modbus-Device] <===> [RS485 chip <==> Raspberry PI GPIO] pins. The RS485 has three wires (Transmit, Receive, Direction) they are connected as follows

RaspiPi <=> Adapter

GPIO 14 (8) – Tx <=> Data+

GPIO 15 (10)- Rx <=>- Data-

GPIO 18 (12) – Direction

The RS485 isn't a typical 9-pin adapter. I have three wires running off a chip. A twisted pair that serves as differential set and a ground wire.

I have been able to send serial communications between this adpater and a USB-RS485 adapter by manually flipping GPIO18 for send/recieve. (Code below)[1]. This code is purely for proving the adapter works

I'm stuck at getting modbus to work on GPIO adapter. I've tried using minimalmodbus, which works well with the USB-RS485 adapter, but fails with the GPIO adapter. I suspect this is because the direction pin isn't being set.

The ideal resolution would be to find an RS485 driver for GPIO on the pi, but short of that I see three options

1 – Make my own driver (Something I am completely unfamiliar with)
2 – Somehow get a modbus library to flip the GPIO pin in Kernel space
3 – Manually send modbus messages over serial and adjust the GPIO pin in user space. This seems the easiest but also the worst in terms of speed and reliability. My code attempt is below [2]

Any advice on this topic would be very appreciated. If someone has done something similar before and could weigh in on my options that would be helpful. I've never worked on software at this level so I wouldn't find it surprising if there were some obvious answer I'm completely overlooking.

[1] This code communicates with another RS485 adapter on the raspberry pi connected with USB. This was written to prove the GPIO adapter is working and that I can control direction with Pin 12 on the Raspberry pi

import timeimport serialimport RPi.GPIO as GPIOGPIO.setmode(GPIO.BOARD)GPIO.setup(12, GPIO.OUT);ser = serial.Serial(       port= '/dev/ttyS0',       baudrate= 57600,       parity= serial.PARITY_NONE,       stopbits= serial.STOPBITS_ONE,       bytesize= serial.EIGHTBITS,       timeout=1)def write_add(): counter = 0; message = 0 while (True):    print "writing",    GPIO.output(12,1) #set high/transmit    ser.write('%d \n'%(message))    time.sleep(0.005) #baud for 57600    #time.sleep(0.5) #baud for 9600    GPIO.output(12, 0) #pin set to low/receive    loop_count = 0    res =""    while (res == ""):       res =ser.readline();       if(res != ""):         print ""         print "Read Cycles: "+str(loop_count)+" Total: "+str(counter)         print res         message = int(res) + 1         counter = counter + 1       elif(loop_count > 10):         res = "start over"       else:         print ".",         loop_count = loop_count + 1write_add()

[2] This code is attempting to communicate with another modbus device. My message is being sent, but the response is garabge. My assumption is that the GPIO direction pin is being flipped either too soon and cutting off the message or too late and missing some of the response.

import serial import timeimport RPi.GPIO as GPIOGPIO.setmode(GPIO.BOARD)GPIO.setup(12, GPIO.OUT)ser = serial.Serial(    port='/dev/ttyS0',    baudrate = 57600,    parity=serial.PARITY_NONE,    stopbits=serial.STOPBITS_ONE,    bytesize=serial.EIGHTBITS,    timeout=1)GPIO.output(12,1) #set high/transmitByteStringToSend = "\x00\x03\x01\xb8\x00\x01\x04\x02"ser.write(ByteStringToSend)time.sleep(0.005) #baud for 57600GPIO.output(12, 0) #pin set to low/receiveReceivedData = ""while (ReceivedData == ""):   RecievedData = ser.readline();   print RecievedData

[3] Working USB-RS-485 code. USB adapter on Pi connected to Modbus device This code reads register 440 every second.

#1/usr/bin/env pythonimport minimalmodbusimport timeprint minimalmodbus._getDiagnosticString()minimalmodbus.BAUDRATE=57600minimalmodbus.PARITY='N'minimalmodbus.BYTESIZE=8minimalmodbus.STOPBITS=1minimalmodbus.TIMEOUT=0.1instrument = minimalmodbus.Instrument('/dev/ttyUSB0', 0)  #port and slave#instrument.debug = Truewhile True:    batterVolt = instrument.read_register(440, 2) #register number, number decimals    print batterVolt    time.sleep(1)

Edit: Clarified scheme.
Edit2: Further clarified scheme and added/edited code

Best Solution

The code in example-2 actually works correctly. I just needed to format the response.

print RecievedData.encode('hex')

This will give the hex string in a modbus response format. As Andrej Debenjak alluded to time.sleep(x) will be dependent on baud rate and message size.

Side note: I found this page helpful in deciphering the modbus transmission.

http://www.modbustools.com/modbus.html

Edit: The ByteString I'm sending should not work on a proper modbus setup. The first byte x00 is the broadcast byte and should not solicit a response. It seems the hardware I'm working with has something funky going on. In a typical modbus setup you would need to address which device you're trying to communicate with. Thanks Marker for pointing this out.