communicating to a modbus server with python code

J

Thread Starter

Jeff

Hello, I would like to write a python code to read and write to different registers (I think this is the proper word?) on a modbus server. we are running a calibration system with a plc that read from this modbus server. I want to read and write to a modbus server to talk to the plc. I have no idea where to start this process, I know very little about plc's and modbus's etc. if someone can point me in the right direction I would greatly appreciate your help! If someone has an example code that would be super great! thanks.
 
L

Lynn A Linse

<p>I am not sure what your question is - I have lots of python code but it is far too much to offer as is.

<p>Stock python has no serial support. For serial, you'll need some serial tool like pyserial - this hides details of OS and allows Linux (or Windows) style serial calls on either OS. A web search of pyserial will turn up a download site - such as http://pyserial.sourceforge.net/ . The "Vaults of parnassus" is another nice source for Python tools
including pyserial. http://py.vaults.ca/~x/parnassus/apyllo.py/

<p>Creating binary messages is not hard in Python, but a bit ugly. You use lots of "chr(x)" function to build up a binary string and to parse a binary response lots of "ord()". Other than that, look at the spec at www.modus-ida.org for details of the actual protocol.

<p>Here is my CRC16 routine including a few test cases (written with no regard for CPU speed, since that is not why one uses Python).
<pre>
==================================================

table = tuple()

# crc16_Init() - Initialize the CRC-16 table (crc16_Table[])
def init_table( ):
global table

if( (len( table) == 256) and (table[1] == 49345)):
# print "Table already init!"
return

lst = []
i = 0
while( i < 256):
data = (i << 1)
crc = 0
j = 8
while( j > 0):
data >>= 1
if( (data ^ crc) & 0x0001):
crc = (crc >> 1) ^ 0xA001
else:
crc >>= 1
j -= 1

lst.append( crc)
# print "entry %d = %x" % ( i, table)
i += 1

table = tuple( lst)
return

# given a Byte, Calc a modbus style CRC-16 by look-up table
def calcByte( ch, crc):
init_table( )
if( type(ch) == type("c")):
by = ord( ch)
else:
by = ch
crc = (crc >> 8) ^ table[(crc ^ by) & 0xFF]
return (crc & 0xFFFF)

def calcString( st, crc):
init_table()
# print "st = ", list( st)
for ch in st:
crc = (crc >> 8) ^ table[(crc ^ ord(ch)) & 0xFF]
# print " crc=%x" % crc
return crc

def testCRC( ):
init_table()

# test Modbus
print "testing Modbus messages with crc16.py"
print "test case #1:",
crc = 0xFFFF
st = "\xEA\x03\x00\x00\x00\x64"
for ch in st:
crc = calcByte( ch, crc)
if( crc != 0x3A53):
print "BAD - ERROR - FAILED!",
print "expect:0x3A53 but saw 0x%x" % crc
else:
print "Ok"

print "test case #2:",
st = "\x4b\x03\x00\x2c\x00\x37"
crc = calcString( st, 0xFFFF)
if( crc != 0xbfcb):
print "BAD - ERROR - FAILED! ",
print "expect:0xBFCB but saw 0x%x" % crc
else:
print "Ok"

print "test case #3:",
st = "\x0d\x01\x00\x62\x00\x33"
crc = calcString( st, 0xFFFF)
if( crc != 0x0ddd):
print "BAD - ERROR - FAILED!",
print "expect:0x0DDD but saw 0x%x" % crc
else:
print "Ok"

# test Modbus
print
print "testing DF1 messages with crc16.py"

print "test case #1:",
st =
"\x07\x11\x41\x00\x53\xB9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
crc = calcString( st, 0x0000)
crc = calcByte( "\x03", crc)
if( crc != 0x4C6B):
print "BAD - ERROR - FAILED!",
print "expect:0x4C6B but saw 0x%x" % crc
else:
print "Ok"

return
</pre>
 
T

Tallak Tveide

I would suggest looking at http://code.google.com/p/pymodbus/

You need to find out whether you are writing a TCPIP modbus server or a serial link.

Also - the PLC is normally the server (or slave) while your PC acts as a client (or master) in the simplest use case which is reading and writing some values to a PLS.

A Ruby library exists as well called RModbus which is really simple to use with TCPIP...
 
Hi

I'm trying to extract data from a PLC to a RPi model B using modbus.

I'm using sipex 485 half duplex chip. I have managed to set up the circuit, but I'm not making too much head way with the python code apart from testing the Tx Rx channels and making sure I have pyserial installed.

I'm trying extract the data from 3 registers in the PLC. the modbus card is installed, and I have the registers of interest pointing to the holding registers of the modbus card. Any advice would be great.
 
L

Lynn August Linse

My advice - try to locate a 'pc comm module/cable' which is 100% the same as what sounds like an embedded module. Then run any of a dozen free "Modbus/RTU test tools", which hopefully have a 'trace' or 'log' mode so you can see both the binary request and response.

Then in your Python code, split the creation/parsing of the binary messages away from your actual serial code. Thus you can code & unittest your module to create & parse those exact same messages. Confirm the CRC is perfect, and so on. While you're at it, do some bulk unittest where you send the same pack to every slave address 0x01 to 0xFF.

Once your 'protocol create/parse' module is 100% working, feeding those messages to/from pyserial should 'just work'. Do remember to disable the software flow control! Life is worst when you are trying to debug what might be a parse verse data-comm can be tough, especially since Modbus/RTU slave behavior is to ignore anything not perfect.
 
Thanks you very much for your time and the great advice. I'm very new to this and wouldn't know where to begin with the python code. Would you have any examples of structure/layout ? or am I way out of my league?

Thanks again for reply
 
> I'm very new to this and wouldn't know where to begin with
> the python code. Would you have any examples of
> structure/layout? or am I way out of my league?

Hmmm, now "how to learn python" is a very different question - I see my Python Modbus RTU CRC post above is ten years old! My how time flies.

If you DO NOT know C, then Python is fairly easy to learn. If you are a traditional programmer, it can be mind-bending & annoying (I've used Python since 2002). On a resource-rich system, you can do anything in Python & its fast - on a small system, you have to be a bit more careful to do things effectively.

If your goal is to pull a dozen data points from a device and NOT to create a public tool, then design isn't very important. I tend to pass in parameters via dictionary (aka: keyed data or name-indexed array). This allow you to expand function without changing code. So using a power meter as example, I'd have a few 'refresh' routines which go out and pull a large block from modbus (read a range of registers) and save in memory, then a collection of 'get_total_kwh' (etc) which peels off the one data point of interest. Use the Python 'struct' module to convert the binary to floats, as it handles big-endian.

What you do with the data AFTER obtaining via Modbus is likely a dozen times harder than the Modbus. Pushing to cloud via web services or JSON RPC is a lot fussier, and if you want some GUI, then I'm no help at all, as I've been a middle-ware guy - take data from device A, tweak the form, and push it off to device B.
 
C

Curt Wuollet

Are you using one of the Modbus libraries? I've been able to retrieve data in a testing scenario from a Click with a Pi.

There are lots of examples extant for a Modbus master. What I haven't found is anything reasonably simple to turn the pi into a slave. And what I have found would be pretty nasty to integrate with my
applications.

I used a simple USB to RS485 converter from ebay. The prolific chips are best supported. The cheap adapters I used use a CH340 type, and it does work, but only at 9600 baud. I mess with this when I get spare time which isn't often, but It would make some things easier if the pi could be on the bus.

Regards
cww
 
I'm not in the business at all. I'm just trying to see if I can gather data from an AB micrologix PLC that's monitoring the temperature in a solar heated tank. I had a PT100, transmitter and I had the PLC already with a AD plus modbus cards that was being thrown away. I'm going for the ultra cheap method using a SIPEX 485 for half duplex communication as I don't really have any cash or card to purchase on-line and though it would be a good project to keep me busy. I've learned a lot from the PLC and a bit of instrumentation too calibrating the PT100. I tested the pyserial via echo to the TTYAMA0 with the chip and it looks good. I just hit a wall with the python set up for MODBUS RTU in half duplex. I didn't understand it was RTU until you mentioned it, I was looking at sync and a-sync for a while.

The goal is just to pull data from one holding register on the PLC, I was going to use a RDBMS to store the data.
 
I have Modbus libraries and have only tested the TxRx with pyserial which works fine using echo "sdkjfdskj" to TTYAMA0.

I can't afford to spend or have the means to buy online but I have a SPIEX 485 chip for half duplex comms and use GPIO to set the chip into Tx Rx states.

Could you send me a link to or any examples of the code you use?

thanks for the reply
 
C

Curt Wuollet

Google is your friend here. Hopefully you can find someone who has fought the thunderstorms and charged into the trees. I've been in electronics since the 70's and computers only a little less than that time and rather than start with a simple data transceiver chip, I would empt for something supported by Raspian and/or user experience. I'm not sure, but there are probably issues with your approach, not the least of which is you are either going to have to "fake" modbus or write at least a minimal Modbus master implementation and set the PLC up as a slave. It is not simple to meet all the requirements of modbus rtu. Not at all impossible, but certainly non trivial. Given that AB seems to only grudgingly support Modbus, I would expect that you will need to be spot on to make things work. I think I used some free code posted by someone with the same goals. I looked at MinimalModbus and PyModbus and they seemed like way overkill for as you say, one register. I can't promise anything, cause it's been a while now, but if I can find the lash-up I was using, I'll post a URL. It might be helpful to try a $12 USB to RS485 converter or the even cheaper TTL to RS485 converters from EBay.

Starting with something that has been known to work helps limit the variables. There needs to be receive/transmit management and these are sorted out for the more popular paths. Once you get your code working then you can hack hardware to your heart's content. These days, I'm usually happy with anything that works. A little research will get you setups that someone has had working, and the cost needn't be much more that your bare chip, maybe less. I can get an 8 channel relay card on EBay for less than I would spend for the relays and the TTL to RS232 cards I've used are around $3. At the least you can probably find a schematic for something that works and make sure you have the same functionality.
 
Top