Reputation: 319
I currently trying to receive data from my Arduino slave to my computer. I am successful in creating an Arduino slave. However, when I am trying to receive the data from my computer using the Pymodbus library, my code fails to receive the data from the Arduino and raises a ModbusIOException. For the specifications of my project, I am trying to build a Modbus RTU with the Arduino to simulate a sensor with random numbers as readings. The Arduino code uses the Modbus-Arduino library of Andre Sarmento.
https://github.com/andresarmento/modbus-arduino
I already checked my Arduino slave if it is working. I tried reading the data through a Modbus Master emulator (QModMaster), and it worked just fine. This might prove that the problem itself is from the code for the Master. Furthermore, the serial connection seems to work fine, since the self.client.connect() returns True.
These are the screenshots of the QModMaster configurations.
Slave configurations Serial Port configurations
Python code for the master:
class ModbusRTU:
def __init__(self, graph_name, port, baudrate=9600,
stopbits=1, bytesize=8, parity='N',
timeout=1):
self.graph_name = graph_name
self.client = ModbusSerialClient(method='rtu',
port=port,
baudrate=baudrate,
parity=parity,
timeout=timeout)
self.connection = self.client.connect()
result = self.client.read_holding_registers(address=0,
count=2,
unit=1)
print(result.registers)
if __name__ == '__main__':
modbus = ModbusRTU(graph_name='/dev/ttyACM0',
port='/dev/ttyACM0', baudrate=9600,
stopbits=1, bytesize=8, parity='N',
timeout=1)
print(modbus.check_connection())
Arduino code for the simulated slave and sensor:
#include <Modbus.h>
#include <ModbusSerial.h>
ModbusSerial mb;
const int READING = 0;
const int DECIMAL = 1;
void setup() {
mb.config(&Serial, 9600, SERIAL_8N1);
mb.setSlaveId(1);
mb.addHreg(READING);
mb.addHreg(DECIMAL);
}
void loop() {
mb.task();
mb.Hreg(READING, random(1, 201));
mb.Hreg(DECIMAL, random(0, 4));
}
Upon printing the results.registers
, it is supposedly a list of integers. However, it just raises a ModbusIOException with a message of:
'ModbusIOException' object has no attribute 'registers'
File "/home/kebaranas/PythonProjects/ThirsyWell/tools/utilities.py", line 21, in __init__
print(result.registers)
File "/home/kebaranas/PythonProjects/ThirsyWell/tools/utilities.py", line 29, in <module>
timeout=1)
It also gives this message as well.
Modbus Error: [Input/Output] Modbus Error: [Invalid Message] Incomplete message received, expected at least 2 bytes (0 received)
Upvotes: 1
Views: 5406
Reputation: 319
I already found out the solution for this thanks for the help of few people. The QModMaster uses a library called libmodbus. Since the Arduino simulated slave and sensor worked with QModMaster, it would be easier to change the previous library and instead use libmodbus. Luckily, there is a python equivalent for libmodbus which is pylibmodbus. This is the link for the library https://github.com/stephane/pylibmodbus.
from pylibmodbus import ModbusRtu
class ModbusRTU:
def __init__(self, port, baudrate=9600, databit=8, parity='None',
stopbit=1, timeout=1000):
self.parity = {'Odd': 'O', 'Even': 'E', 'None': 'N'}
self.modbus = ModbusRtu(device=port.encode('ascii'),
data_bit=databit, baud=baudrate,
parity=self.parity[parity] \
.encode('ascii'),
stop_bit=stopbit)
self.modbus.set_response_timeout(timeout/1000)
self.modbus.connect()
self.modbus.set_slave(1)
result = self.modbus.read_registers(0, 2)
print(result)
self.modbus.close()
if __name__ == '__main__':
main = ModbusRTU('/dev/ttyACM0', baudrate=9600, databit=8,
parity='None', stopbit=1)
Upvotes: 1
Reputation: 3516
You're defining holding registers in your slave but you're trying to read them as input registers, try changing this line:
result = self.client.read_input_registers(address=1, count=2, unit=1)
To:
result = self.client.read_holding_registers(address=1, count=2, unit=1)
Be aware that the Modbus spec defines this two different types of registers: holding and input, depending on the memory region they are placed in.
Upvotes: 1
Reputation: 34206
Before print(result.registers)
try the following code snippet:
if not result.isError():
print(result.registers)
else:
print("error: {}".format(result))
Also, fill in other ModbusSerialClient()
argument.
Here is updated of your code snippet:
from pymodbus.client.sync import ModbusSerialClient
class ModbusRTU:
def __init__(self, graph_name, port, baudrate=9600,
stopbits=1, bytesize=8, parity='N',
timeout=1):
self.graph_name = graph_name
self.client = ModbusSerialClient(
method='rtu',
port=port,
baudrate=baudrate,
parity=parity,
timeout=timeout,
stopbits=stopbits,
bytesize=bytesize
)
self.connection = self.client.connect()
result = self.client.read_input_registers(address=1,
count=2,
unit=1)
if not result.isError():
print(result.registers)
else:
print("error: {}".format(result))
if __name__ == '__main__':
modbus = ModbusRTU(
graph_name='/dev/ttyACM0',
port='/dev/ttyACM0', baudrate=9600,
stopbits=1, bytesize=8, parity='N',
timeout=1
)
Upvotes: 2