If you have serial Modbus/RTU slaves attached to embebbed serial
modules, then you'll need to create the original Modbus/RTU requests to
send. This is quite easily done with Python.
In the last weeks I have researched a lot about this subject. Many forums say to use libraries: Pymodbus, Minimal Modbus or Modbus-tk. Tested all and say: that it is easier to use and create PySerial polling messages directly than using these libraries.
Requirement: Download PySerial Library
These tests have been compiled in python 2.7
First i created the CRC16.py
What is CRC?
Wikipedia says:
"A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data. Blocks of data entering these systems get a short check value attached, based on the remainder of a polynomial division
of their contents; on retrieval the calculation is repeated, and
corrective action can be taken against presumed data corruption if the
check values do not match."
CRC16 on Python:
#!/usr/bin/python
# 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])
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
# EXECUTE
table = tuple()
def main():
testCRC()
if __name__ == "__main__":
main()
Now you import the function CRC16 on the modbus application
MODBUS on Python
This is my modbus simulator (Slave).
#!/usr/bin/python
#!/usr/bin/env python
import serial
import time
#Import function CRC16
from CRC16 import calcString
# SERIAL PC CONFIG.
ser = serial.Serial(port='COM2',baudrate=19200)
# FUNCTIONS
# Decimal to Hex.
def dec2hex(n):
lo = n & 0x00FF
hi = (n & 0xFF00) >> 8
return chr(hi) + chr(lo)
#return "%02x" % n
# Hex. to Decimal
def hex2dec(s):
return int(s, 16)
# Invert byte ( LO and HI )
def swapLoHi(n):
lo = n & 0x00FF
hi = (n & 0xFF00) >> 8
return lo << 8 | hi
# Calc. CRC16
def stCRC(msg):
crc = calcString(msg, 0xFFFF)
crc = swapLoHi(crc)
return dec2hex(crc)
def HiLo(n):
lo = n & 0x00FF
hi = (n & 0xFF00) >> 8
return hi | lo
# MODBUS PROTOCOL DEFINES
READ_HOLDING = 3
READ_INPUT = 4
PRESET_SINGLE = 6
PRESET_MULTIPLE = 10
# INPUT VALUES
nb = input('Value 1: ')
nc = input('Value 2: ')
nd = input('Value 3: ')
ne = input('Value 4: ')
# CONVERT VALUE TO STRING HEX
value1= chr(nb)
value2 = chr(nc)
value3 = chr(nd)
value4 = chr(ne)
### DEBUG MSG. ###
ReadHolding = "\x01\x03\x02\x00" + value1
stMsg = ReadHolding + stCRC(ReadHolding)
ReadInput1 = "\x02\x04\x02\x00" + value2 + "\x00"+ value3 +"\x84"
ReadInput2 = "\x03\x04\x02\x00" + value2+ "\x00\x0a\x84"
PresetSingle1 = "\x01\x06\x00\x00\x00\x0A"
PresetMultiple1 = "\x01\x10\x70\x00\x06"
PresetMultiple = PresetMultiple1 + stCRC(PresetMultiple1)
PresetSingle = PresetSingle1 + stCRC(PresetSingle1)
## LOOP MODBUS
while True:
out = ''
# let's wait one second before reading output (let's give device time to answer)
time.sleep(1)
# MODBUS READ BUFFER
while ser.inWaiting() > 0:
out += ser.read(1)
### DEBUG ###
if out != '':
print "uC Response"
print out[0].encode('hex_codec')
print out[1].encode('hex_codec')
print out[2].encode('hex_codec')
print out[3].encode('hex_codec')
print out[4].encode('hex_codec')
print out[5].encode('hex_codec')
print out[6].encode('hex_codec')
print out[7].encode('hex_codec')
# GET PROTOCOL
value = int(out[1].encode('hex_codec'),16)
print value
# READ HOLDING MSG.
if value is READ_HOLDING:
print "read holding"
ReadHolding = "\x01\x03\x02\x00" + value1
stMsg = ReadHolding + stCRC(ReadHolding)
ser.write(stMsg)
time.sleep(1) # for 100 millisecond delay
# READ INPUT MSG.
elif value is READ_INPUT:
print "read input"
nc = nc + 1
nd = nd + 1
ne = ne + 1
value2 = chr(nc)
value3 = chr(nd)
value4 = chr(ne)
ReadInput1 = "\x01\x04\x22\x00" + value1+ "\x00"+ value2 + "\x00" + value3 + "\x00"
stMsg1 = ReadInput1 + stCRC(ReadInput1)
ser.write (stMsg1)
time.sleep(1) #for 100 millisecond delay
# PRESET SINGLE MSG.
elif value is PRESET_SINGLE:
print "preset single"
val = int(out[5].encode('hex_codec'),16)
power = chr(val) # debug pot
ser.write (PresetSingle)
# PRESET MULTIPLE MSG.
elif value is PRESET_MULTIPLE:
print "preset multiple"
ser.write (PresetMultiple)
time.sleep(1) # for 100 millisecond delay
RecievedData = ""
#while ser.inWaiting() > 0:
# RecievedData = ser.read(1)
#print RecievedData
What is Modbus?
The best site ever to explain it : Modbus