Raspberry Pi RS485/Uart Modbus

David Ryan picture David Ryan · May 16, 2018 · Viewed 7.3k times · Source

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 time
import serial
import RPi.GPIO as GPIO
GPIO.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 + 1

write_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 time
import RPi.GPIO as GPIO
GPIO.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/transmit
ByteStringToSend = "\x00\x03\x01\xb8\x00\x01\x04\x02"
ser.write(ByteStringToSend)
time.sleep(0.005) #baud for 57600
GPIO.output(12, 0) #pin set to low/receive
ReceivedData = ""
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 python
import minimalmodbus
import time

print minimalmodbus._getDiagnosticString()

minimalmodbus.BAUDRATE=57600
minimalmodbus.PARITY='N'
minimalmodbus.BYTESIZE=8
minimalmodbus.STOPBITS=1
minimalmodbus.TIMEOUT=0.1

instrument = minimalmodbus.Instrument('/dev/ttyUSB0', 0)  #port and slave
#instrument.debug = True
while 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

Answer

David Ryan picture David Ryan · May 17, 2018

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.