-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
'UART' object has no attribute 'wait_tx_done' #34
Comments
Hello @volkar1 Could you to try to test the |
@volkar1 Sorry, I understood wrong. I think that is really a bug using |
@volkar1 While @brainelectronics do not check this bug, please try if this works for you:
Here has a discussion about the same bug. I got this code above from there, on second reply Please, let us know if works. |
@volkar1 as I don't have a non ESP32 MicroPython board at the moment, could you confirm this suggestion? I would provide a bugfix version the next day. Raspberry Pi Pico W boards will be delivered in a few days so I can also test and resolve #7 |
@beyonlo and @brainelectronics thank you for your quick response. I tested it yesterday and it does transmit, and receive data but it fails quite a lot. It fails with So in case I try to read 3 registers like that:
It fails on the third reading 60% of the times... since I'm trying to read register 107 through 112 I also tried this:
and it fails on about 40% of the readings. Im wondering if this might be caused by pre-delay or post-delay between switching |
@brainelectronics Just to be sure: did you tested the I will need to run the Thank you in advance! |
@volkar1 you assumption is correct. Every time you call a holding register read function or any other function that is sending something the control pin is used. If you instead read/write multiple registers with one command as you tested as well, the pin will be used twice. This is the reason why you get a lower failure rate. So, Yes it is connected to the pre or post delay as you supposed. I'll check and adjust the value as soon as my Pico arrived. |
@beyonlo yes I use the ESP32 UART but without a control pin for the RS485. I use a board with auto transmission control. Of course I agree with you. The code has to work the same on every board! I assume by end of the week the boards should be here and the fix available |
@brainelectronics great. My two kits with @volkar1 are you using the same chip right? |
According to the Modbus Protocol the frame would be always 11 bits due to these two options
The minimum time between two frames is defined as 3.5 characters time, defended in this lib by micropython-modbus/umodbus/serial.py Line 78 in eee18c2
This is valid for single register readings. And of course static. As more than one register can be read, which still would only contain 8 data bits, setting more than one register will (if it is not just a set of coils) have more than 8 data bits and thereby (more or less slightly) violate the timing. Additionally there is no wait time between setting the control pin high and sending the data see micropython-modbus/umodbus/serial.py Lines 183 to 186 in eee18c2
The first few bits of a frame could be lost, the CRC is invalid and the client could reject the data. Above 19200 baud, the time between two frames gets less important/critical, that's why it is usually fixed to 1750us. Nevertheless the time between setting/resetting the pin and sending data becomes more important as more bits "can be lost" within a shorter period of time. In case of @volkar1 it might be a combination of 3 things: no pre send wait time, longer message length, wrong post send wait time. |
My @volkar1 any news to reduce error?
@brainelectronics I will to do that to check if error stop, or at least, reduce! But in my perception using |
Yes, you're right, there shall be no errors in the RTU Implementation as well. The protocol tries to prevent issues, but it can of course not solve issues caused by wrong timing. I'm looking forward to your findings @beyonlo |
Hi @brainelectronics I added my two kits/boads: MAX485 module (that are exactly the same of used by @volkar1), but unfortunately do not works. I changed the if self._ctrlPin:
self._ctrlPin(1)
time.sleep_us(1000)
self._uart.write(serial_pdu)
if self._ctrlPin:
self.char_time_us = 1000000 * (2 + self._data_bits + self._stop_bits) / self._baudrate
#while not self._uart.wait_tx_done(2):
#machine.idle()
time.sleep_us(0 + int(self.char_time_us * len(serial_pdu)))
self._ctrlPin(0)
So, I tried many others versions that I have downloaded in the past that works using Slave RTU:
Ps: very strange show Master RTU: >>> from umodbus.serial import Serial as ModbusRTUMaster
>>> rtu_pins = (17, 18)
>>> host = ModbusRTUMaster(baudrate=9600, data_bits=8, stop_bits=1, parity=None, pins=rtu_pins, ctrl_pin=15)
>>> host.read_coils(slave_addr=10, starting_addr=123, coil_qty=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "umodbus/serial.py", line 204, in read_coils
File "umodbus/serial.py", line 178, in _send_receive
File "umodbus/serial.py", line 182, in _validate_resp_hdr
OSError: no data received from slave
>>> Ps: Before that I added the I don't know if the problem is about the I'm using two Just to be sure, I'm using 5V in that VCC, is that correct right? |
@beyonlo VCC can be either 3.3V or 5V, I think I was using 3.3V and it works just fine. I also set jumper on De, Re pins and then connect it to the pin on microcontroller. |
@volkar1 and @brainelectronics Works!!! I used a multimeter to find cable/wire problem and works! I'm using the My test using Master:
Ps: As this version (1.1.0) is no possible to config in the register_definitions a list of many holding registers, I could not to do a more extensive test reading a high quantity of registers. I just read that 8 COILs.
Slave: part of rtu_client_example.py:
The changed code was this: if self._ctrlPin:
self._ctrlPin(1)
time.sleep_us(1000)
self._uart.write(serial_pdu)
if self._ctrlPin:
self.char_time_us = 1000000 * (2 + self._data_bits + self._stop_bits) / self._baudrate
#while not self._uart.wait_tx_done(2):
#machine.idle()
time.sleep_us(0 + int(self.char_time_us * len(serial_pdu)))
self._ctrlPin(0) I did the test from |
@beyonlo could you please re-test with 2.1.1-rc17.dev41? See #41 |
The latest MicroPython version (anything released after 1.19.1) will support |
Thank you very much @brainelectronics for this release, I did the tests over
ModBus RTU Slave:
ModBus RTU Master - test with COILS - reading 16 coils - no errors: $ cat read_coils.py
import time
from umodbus import version
print('Running ModBus version: {}'.format(version.__version__))
from umodbus.serial import Serial as ModbusRTUMaster
rtu_pins = (17, 18)
address = 125
qty = 16
host = ModbusRTUMaster(baudrate=115200, data_bits=8, stop_bits=1, parity=None, pins=rtu_pins, ctrl_pin=15)
print('COIL request test.')
print('Reading qty={} from address {}:'.format(qty, address))
values = host.read_coils(slave_addr=10, starting_addr=address, coil_qty=qty)
print('Result: {}'.format(values))
success = True
counter_requests = 100
fred_time = 30
print('Testing {} requests, each {}ms. Wait...'.format(counter_requests, fred_time))
start_time = time.ticks_ms()
for i in range(counter_requests):
res = host.read_coils(slave_addr=10, starting_addr=address, coil_qty=qty)
time.sleep_ms(fred_time)
if res != values:
print('Error found')
success = False
break
end_time = time.ticks_ms()
delta_time = time.ticks_diff(end_time, start_time)
if success:
print('Done ModBus RTU (via RS485) requests without error in {}ms.'.format(delta_time))
print('Just a observation: the total time was {}ms. So {} / {} requests is {}ms for each request, so was not capable to do each request in {}ms as configured in this test. Is that normal?'.format(delta_time, delta_time, counter_requests, delta_time/counter_requests, fred_time)) $ mpremote run read_coils.py
Running ModBus version: 2.1.1-rc17.dev41
COIL request test.
Reading qty=16 from address 125:
Result: [True, True, True, True, True, False, False, False, False, True, True, True, False, False, False, True]
Testing 100 requests, each 30ms. Wait...
Done ModBus RTU (via RS485) requests without error in 8479ms.
Just a observation: the total time was 8479ms. So 8479 / 100 requests is 84.79ms for each request, so was not capable to do each request in 30ms as configured in this test. Is that normal? ModBus RTU Master - test with HOLDING REGISTERS - reading 33 registers - no errors: $ cat read_holding_registers.py
import time
from umodbus import version
print('Running ModBus version: {}'.format(version.__version__))
from umodbus.serial import Serial as ModbusRTUMaster
rtu_pins = (17, 18)
address = 130
qty = 33
host = ModbusRTUMaster(baudrate=115200, data_bits=8, stop_bits=1, parity=None, pins=rtu_pins, ctrl_pin=15)
print('HOLDING REGISTER request test.')
print('Reading qty={} from address {}:'.format(qty, address))
values = host.read_holding_registers(slave_addr=10, starting_addr=address, register_qty=qty, signed=False)
print('Result: {}'.format(values))
success = True
counter_requests = 100
fred_time = 30
print('Testing {} requests, each {}ms. Wait...'.format(counter_requests, fred_time))
start_time = time.ticks_ms()
for i in range(counter_requests):
res = host.read_holding_registers(slave_addr=10, starting_addr=address, register_qty=qty, signed=False)
time.sleep_ms(fred_time)
if res != values:
print('Error found')
success = False
break
end_time = time.ticks_ms()
delta_time = time.ticks_diff(end_time, start_time)
if success:
print('Done ModBus RTU (via RS485) requests without error in {}ms.'.format(delta_time))
print('Just a observation: the total time was {}ms. So {} / {} requests is {}ms for each request, so was not capable to do each request in {}ms as configured in this test. Is that normal?'.format(delta_time, delta_time, counter_requests, delta_time/counter_requests, fred_time)) $ mpremote run read_holding_registers.py
Running ModBus version: 2.1.1-rc17.dev41
HOLDING REGISTER request test.
Reading qty=33 from address 130:
Result: (34, 12, 14, 0, 10, 4, 2, 1345, 34, 8, 1100, 350, 456, 754, 324, 423, 530, 90, 320, 34, 244, 355, 606, 656, 640, 620, 677, 623, 234, 567, 34, 56, 68)
Testing 100 requests, each 30ms. Wait...
Done ModBus RTU (via RS485) requests without error in 8375ms.
Just a observation: the total time was 8375ms. So 8375 / 100 requests is 83.75ms for each request, so was not capable to do each request in 30ms as configured in this test. Is that normal?
Note: I think that you already know, but I would like to notice you just to be sure about that. The version 2.1.1-rc17.dev41 has not changed code with that code above, so the In the self._data_bits = data_bits
self._stop_bits = stop_bits
self._baudrate = baudrate and in the if self._ctrlPin:
self._ctrlPin(1)
time.sleep_us(1000)
self._uart.write(serial_pdu)
if self._ctrlPin:
self.char_time_us = 1000000 * (2 + self._data_bits + self._stop_bits) / self._baudrate
#while not self._uart.wait_tx_done(2):
# machine.idle()
#time.sleep_us(self._t35chars)
time.sleep_us(0 + int(self.char_time_us * len(serial_pdu)))
self._ctrlPin(0) Ps: I did not tests doing requests with functions |
…ime calculation in serial.py, resolve #34
…e Modbus RTU PDU, related to #34
…f serial.py, related to #34
@beyonlo could you please re-test one more time with 2.1.2-rc18.dev42? The change contains also a little speed improvement, I hope it works as expected. |
@beyonlo As you added a constant delay of 30ms after each read function, the actual time of one read operation is # don't forget to import :)
import machine
# other code
for i in range(counter_requests):
this_start_time = time.ticks_ms()
res = host.read_holding_registers(slave_addr=10, starting_addr=address, register_qty=qty, signed=False)
while time.ticks_ms() <= this_start_time + fred_time:
# time.sleep_ms(1) # works also of course
machine.idle()
if res != values:
print('Error found')
success = False
break
end_time = time.ticks_ms() With 2.1.2-rc18.dev42 at micropython-modbus/umodbus/serial.py Line 178 in c36d586
|
@brainelectronics this version do not works, Slave: $ mpremote run rtu_client_example.py
Running ModBus version: 2.1.2-rc18.dev42 Master: >>> from umodbus import version
>>> print('Running ModBus version: {}'.format(version.__version__))
Running ModBus version: 2.1.2-rc18.dev42
>>> from umodbus.serial import Serial as ModbusRTUMaster
>>> rtu_pins = (17, 18)
>>> host = ModbusRTUMaster(baudrate=115200, data_bits=8, stop_bits=1, parity=None, pins=rtu_pins, ctrl_pin=15)
>>> host.read_coils(slave_addr=10, starting_addr=123, coil_qty=1)
Traceback (most recent call last): <-- Here I did the `CTRL+C`
File "<stdin>", line 1, in <module>
File "/lib/umodbus/serial.py", line 353, in read_coils
File "/lib/umodbus/serial.py", line 284, in _send_receive
File "/lib/umodbus/serial.py", line 178, in _uart_read
KeyboardInterrupt:
>>>
>>> Ps: used the same |
@brainelectronics I found the problem: On the: micropython-modbus/umodbus/serial.py Line 178 in c36d586
self._t35chars , that is microseconds variable, but you do not changed to be time.sleep_us (sleep in microseconds). I just changed to time.sleep_us(self._t35chars) and works fine.
Thank you very much! |
@brainelectronics Follow the new tests results with that modification that I did above in the Slave: $ mpremote run rtu_client_example.py
Running ModBus version: 2.1.2-rc18.dev42 Master - COIL tests:
$ mpremote run read_coils.py
Running ModBus version: 2.1.2-rc18.dev42
COIL request test.
Reading qty=16 from address 125:
Result: [True, True, True, True, True, False, False, False, False, True, True, True, False, False, False, True]
Testing 100 requests, each 30ms. Wait...
Done ModBus RTU (via RS485) requests without error in 4174ms.
The total time was 4174ms. So 4174 / 100 requests is 41.74ms. So, 41.74ms - 30ms (freq_time delay) = 11.74ms. So the total time operation used by Modbus Protocol is 11.74ms Master - HOLDING REGISTER tests: $ cat read_holding_registers.py
import time
from umodbus import version
print('Running ModBus version: {}'.format(version.__version__))
from umodbus.serial import Serial as ModbusRTUMaster
rtu_pins = (17, 18)
address = 130
qty = 33
host = ModbusRTUMaster(baudrate=115200, data_bits=8, stop_bits=1, parity=None, pins=rtu_pins, ctrl_pin=15)
print('HOLDING REGISTER request test.')
print('Reading qty={} from address {}:'.format(qty, address))
values = host.read_holding_registers(slave_addr=10, starting_addr=address, register_qty=qty, signed=False)
print('Result: {}'.format(values))
success = True
counter_requests = 100
fred_time = 30
print('Testing {} requests, each {}ms. Wait...'.format(counter_requests, fred_time))
start_time = time.ticks_ms()
for i in range(counter_requests):
res = host.read_holding_registers(slave_addr=10, starting_addr=address, register_qty=qty, signed=False)
time.sleep_ms(fred_time)
if res != values:
print('Error found')
success = False
break
end_time = time.ticks_ms()
delta_time = time.ticks_diff(end_time, start_time)
delta_time_div_counter_requests = delta_time/counter_requests
modbus_time_operation = delta_time_div_counter_requests - fred_time
if success:
print('Done ModBus RTU (via RS485) requests without error in {}ms.'.format(delta_time))
print('The total time was {}ms. So {} / {} requests is {}ms. So, {}ms - {}ms (freq_time delay) = {}ms. So the total time operation used by Modbus Protocol is {}ms'.format(delta_time, delta_time, counter_requests, delta_time_div_counter_requests, delta_time_div_counter_requests, fred_time, modbus_time_operation, modbus_time_operation))
Thank for this new release with improvements on the speed! I think that Ps: I do not running anything together this tests, like as my Off-topic: is possible to run the As while True:
result = client.process() So I need two that loops running ( |
Thank you for the finding! It's now fixed in 2.1.2-rc19.dev42 |
Works perfect with this version: $ mpremote run read_coils.py
Running ModBus version: 2.1.2-rc19.dev42
COIL request test.
Reading qty=16 from address 125:
Result: [True, True, True, True, True, False, False, False, False, True, True, True, False, False, False, True]
Testing 100 requests, each 30ms. Wait...
Done ModBus RTU (via RS485) requests without error in 4172ms.
The total time was 4172ms. So 4172 / 100 requests is 41.72ms. So, 41.72ms - 30ms (freq_time delay) = 11.72ms. So the total time operation used by Modbus Protocol is 11.72ms $ mpremote run read_holding_registers.py
Running ModBus version: 2.1.2-rc19.dev42
HOLDING REGISTER request test.
Reading qty=33 from address 130:
Result: (34, 12, 14, 0, 10, 4, 2, 1345, 34, 8, 1100, 350, 456, 754, 324, 423, 530, 90, 320, 34, 244, 355, 606, 656, 640, 620, 677, 623, 234, 567, 34, 56, 68)
Testing 100 requests, each 30ms. Wait...
Done ModBus RTU (via RS485) requests without error in 4764ms.
The total time was 4764ms. So 4764 / 100 requests is 47.64ms. So, 47.64ms - 30ms (freq_time delay) = 17.64ms. So the total time operation used by Modbus Protocol is 17.64ms |
I'm trying to use this library on Pi Pico W to communicate with ModbusRTU device. I'm using MAX485 for modbus bridge (required ctrl_pin).
When you try to read holding registers it fails with the following error:
ModbusRTU is trying to call "wait_tx_done" from machine UART lib, and that function is not included in machine library.
micropython-modbus/umodbus/serial.py
Line 162 in c45d6cc
The text was updated successfully, but these errors were encountered: