eydam-prototyping / mp_modbus

Modbus Lib for Micropython
MIT License
11 stars 3 forks source link

ModBus Slave simple example #2

Open beyonlo opened 2 years ago

beyonlo commented 2 years ago

Hello!

Congratulations for the great work for ModBus lib for the MicroPython.

I'm trying to run the Slave mode but I have no success. Could you please to create a simple example to run the slave mode (RTU and TCP (Server)) using your code?

Just to clarify about my tests: I'm using two ESP32, where ESP32-01 run as Master-RTU and ESP32-02 as Slave-RTU, using RS485. The same to ModBus TCP, where ESP32-01 run as Master (TCP Client) and ESP32-02 as Slave (TCP Server), just using WiFi between them.

I'm using MicroPython version 1.17:

>>> import os
>>> os.uname()
(sysname='esp32', nodename='esp32', release='1.17.0', version='v1.17 on 2021-09-02', machine='ESP32 module (spiram) with ESP32')

Thank you in advance.

beyonlo commented 2 years ago

Hi @eydam-prototyping

I think that I find an error in the https://github.com/eydam-prototyping/mp_modbus/blob/master/mp_modbus_slave.py file. I removed the lines 112, 113 and 114 on mp_modbus_slave.py file to run using the ESP32 MicroPython 1.17.

Edit 1: I added too this self.uart.write(res) after line 118 on file https://github.com/eydam-prototyping/mp_modbus/blob/master/mp_modbus_slave.py#L118 because when enter in mp mode (MicroPython) was not there a write data to UART, like as in non MicroPython mode, on the line 123.

ESP32-02 run as Slave-RTU:

Edit 2: I forgot to put the context var - I copied from the example file https://github.com/eydam-prototyping/mp_modbus/blob/master/test/test_modbus_rtu_async.py :

>>> import uasyncio
>>> from machine import UART
>>> uart1 = UART(1, baudrate=9600, tx=26, rx=25)
>>> from mp_modbus_slave import modbus_rtu_slave, modbus_tcp_server
>>> context={
            "co":{"startAddr": 1000, "registers": bytearray([0xFF, 0x00, 0x00, 0x00]*5)},   # Coils: ON OFF ON OFF ON OFF ON OFF ON OFF
            "di":{"startAddr": 1000, "registers": bytearray([0xFF, 0x00, 0x00, 0x00]*5)},   # Digital Inputs: ON OFF ON OFF ON OFF ON OFF ON OFF
            "hr":{"startAddr": 1000, "registers": bytearray([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F])},
            "ir":{"startAddr": 1000, "registers": bytearray([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F])}
        }
>>> rtu_slave = modbus_rtu_slave(context=context, uart=uart1)
>>> coro = rtu_slave.run_async()
>>> uasyncio.run(coro)
starting async rtu slave

ESP32-01 run as Master-RTU:

>>> from mp_modbus_master import modbus_rtu_master, modbus_tcp_client
>>> rtu_master = modbus_rtu_master(uart_no=1, tx_pin=25, rx_pin=26, en_pin=27)
>>> frame = rtu_master.read_holding_registers(1000, 1)
<modbus_rtu_frame (response): device: 1, func_code: 3, frame:01 03 02 00 01 79 84>
>>> print(frame)
b'\x00\x01'
>>> frame = rtu_master.read_digital_inputs(1000, 1)
<modbus_rtu_frame (response): device: 1, func_code: 2, frame:01 02 01 00 a1 88>
>>> print(frame)
b'\x00'
>>> 

Is correct that results above (on Master), the b'\x00\x01' and b'\x00' based on context var on the Slave-RTU?

Observations: 1. I'm not using the RS485 in this test, just UART (10cm distance) to validate the use case, because I think that my 485 transceiver is broken. Are there problem to use this ModBus lib just using the UART? I just do not have the enable pin, so the enable pin will do nothing, right?

Edit 3: 2. I'm testing the sync mode, but I want to use the asyncio mode send/receive. I see that you created methods to send/receive in async mode (that is great) and is what I want, for RTU and TCP :) I not tested yet, because I'm trying to figureout in basic (sync) examples first are working very well.

Any examples to run the slave mode (RTU and TCP (Server)) on the MicroPython will be wellcome :)

Thank you very much.

eydam-prototyping commented 2 years ago

Hey, thank you for your contribution, but I am currently working on a way to integrate modbus into the micropython image itself, so that you can use the much more tested freemodbus library. I am not done with it now, but I think I can provide you a "preview" image with at least RTU and some parts of TCP working after the weekend.

beyonlo commented 2 years ago

Hi @eydam-prototyping

Hey, thank you for your contribution, but I am currently working on a way to integrate modbus into the micropython image itself, so that you can use the much more tested freemodbus library. I am not done with it now, but I think I can provide you a "preview" image with at least RTU and some parts of TCP working after the weekend.

When you say freemodbus are you talking about the https://github.com/cwalter-at/freemodbus ? If yes, that is great. Just a question: after you integrate the FreeMobBus on the MicroPython image, will the FreeMobBus work with usyncio too (in assynchronous mode for cooperative tasks) like as your implementation here in the mp_modbus?

I my opinion your implementation is amazing for the cooperative tasks, because you are using sending and receiving in async mode using the uasyncio, like on the async def _send_async() and async def run_async(self): for the Slave/Master mode , including Slave mode async with nonblocking socket and async send/receive in TCP Server, and so on. I like so much your implementation (even not finished yet) because it is very asynchronous using the uasyncio powerful. I'm afraid if with FreeModBus that will not work in the same way - for the cooperative tasks using uasyncio.

Anyway, I will appreciate to test your "preview" of integrated FreeModBus on the MicroPython. Please notice to me when your preview will done, that I will be glade to test it :)

Thank you very much!

eydam-prototyping commented 2 years ago

Hey,

I am not completely sure, but it could be the same. There is a freemodbus component in the ESP-IDF. I think, it runs as separate task in the background and is also nonblocking.

You can find it here: https://github.com/eydam-prototyping/micropython/wiki There is a link to my google drive, where you can download the latest image.

On the right side you can find a short documentation. There is also a new network interface. It is required for the modbus module.

I will add the missing functionality in the future, such as:

If you have more Ideas, please let me know :)

beyonlo commented 2 years ago

Hello @eydam-prototyping

I downloaded your firmware and did the tests following the docs in Wiki (https://github.com/eydam-prototyping/micropython/wiki) right side, but not worked :( I tested just the RTU (Master and Slave), that is what are are implemented and documented in that wiki.

I programed both ESP32 with the your firmware, one for the Slave and other for the Master.

ESP32-01 (RTU-Slave)

>>> import os
>>> os.uname()
(sysname='esp32', nodename='esp32', release='1.17.0', version='v1.17-287-g56e5361b8-dirty on 2022-01-09', machine='ESP32 module (spiram) with ESP32')
>>> import modbus
>>> s = modbus.Serial_Slave(1,2,9600,tx=25, rx=26)
>>> a_holding_1 = modbus.Register_Area(modbus.HOLDING,1,100)
>>> a_holding_2 = modbus.Register_Area(modbus.HOLDING,200,100)
>>> a_input_1 = modbus.Register_Area(modbus.INPUT,1,100)
>>> a_coil_1 = modbus.Register_Area(modbus.COIL,1,100)
>>> a_discrete_1 = modbus.Register_Area(modbus.DISCRETE,1,100)
>>> s.run()
>>> 

ESP32-01 (RTU-Master) -- ### Test 1 - This test I used the same of the https://github.com/eydam-prototyping/micropython/wiki/Module-modbus#example-rtu-master just adding the appropriate tx and rx PINs.

>>> import os
>>> os.uname()
(sysname='esp32', nodename='esp32', release='1.17.0', version='v1.17-287-g56e5361b8-dirty on 2022-01-09', machine='ESP32 module (spiram) with ESP32')
>>> import modbus
>>> m = modbus.Serial_Master(2, 9600, modbus.PARITY_NONE, tx=26, rx=25)
>>> m.run()
Guru Meditation Error: Core  0 panic'ed (StoreProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x4009b6e9  PS      : 0x00060c33  A0      : 0x80098984  A1      : 0x3ffca880  
A2      : 0x0000001c  A3      : 0x0000abab  A4      : 0x00060c23  A5      : 0x00060c20  
A6      : 0x0000cdcd  A7      : 0xb33fffff  A8      : 0x0000cdcd  A9      : 0x00000004  
A10     : 0x00000000  A11     : 0x00000001  A12     : 0x00000000  A13     : 0xa5a5a5a5  
A14     : 0xa5a5a5a5  A15     : 0x00000001  SAR     : 0x00000000  EXCCAUSE: 0x0000001d  
EXCVADDR: 0x0000001c  LBEG    : 0x40094d98  LEND    : 0x40094db4  LCOUNT  : 0x00000000  

Backtrace:0x4009b6e6:0x3ffca880 0x40098981:0x3ffca8b0 0x40098a39:0x3ffca8e0 0x4009b37a:0x3ffca900

ELF file SHA256: f67216a04b19b5ac

Rebooting...
ets Jul 29 2019 12:21:46

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:4604
ho 0 tail 12 room 4
load:0x40078000,len:13872
load:0x40080400,len:3356
entry 0x4008061c
MicroPython v1.17-287-g56e5361b8-dirty on 2022-01-09; ESP32 module (spiram) with ESP32
Type "help()" for more information.
>>>

ESP32-01 (RTU-Master) -- ### Test 2 - This test I used the same of the https://github.com/eydam-prototyping/micropython/wiki/Module-modbus#modbus-rtu-master just adding the appropriate tx and rx PINs.

>>> import os
>>> os.uname()
(sysname='esp32', nodename='esp32', release='1.17.0', version='v1.17-287-g56e5361b8-dirty on 2022-01-09', machine='ESP32 module (spiram) with ESP32')
>>> import modbus
>>> m = modbus.Serial_Master(2, baudrate=9600, parity=PARITY_NONE, tx=26, rx=25, rts=32, serial_mode=SERIAL_MODE_RTU)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'PARITY_NONE' isn't defined
>>> m = modbus.Serial_Master(2, baudrate=9600, tx=26, rx=25, rts=32, serial_mode=SERIAL_MODE_RTU)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'SERIAL_MODE_RTU' isn't defined
>>> m = modbus.Serial_Master(2, baudrate=9600, tx=26, rx=25, rts=32)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: function missing 1 required positional arguments
>>>

I removed in this Test 2 the PARITY_NONE (first error), but showed another error about the SERIAL_MODE_RTU. I removed that too, and finally showed error about the missing arguments.

Just to clarify, the same pins number (tx/rx) used in this test, I used in the mp_modbus test and in a simple UART test communication, and works.

If you have more Ideas, please let me know :)

Well, since you gave me this freedom to give you ideas, I would like to say that, in my opinion, I really think a great idea you continue with the evolution of the pure Python mp_modbus lib (Slave and Master RTU and TCP) to finish him, and, in parallel continue this wrapper of ESP-IDF freemodbus component. I agree that freemodbus is more well tested modbus protocol, but I see advantages to have the own complete modbus lib in pure Python to the MicroPython, like as flexibility to use/changes uasyncio to works with mb_modbus, and not just IDF dependent and/or new network module, easier to bug fixes, and so on. Well, I'm a newbie, but I can help you in the tests to have the mp_modbus more stable - not just for the mp_modbus (that I liked so much), but with the freemodbus too, that wrapper that you are working too - You can count me in.

Thank you in advanced!

eydam-prototyping commented 2 years ago

Hey, I uploaded a new version. the Problem with your RTU-master example was, hat you had no Parameter_Descriptor defined. At least one is required. In the future, I will create an error message, if no Parameter_Descriptor is defined. In your second try you had parity=PARITY_NONE in the constructor for the Serial_Master. You needed parity=modbus.PARITY_NONE, because PARITY_NONE is a member of the modbus-module. The same applies for serial_mode=SERIAL_MODE_RTU, it must be serial_mode=modbus.SERIAL_MODE_RTU. With that it should work.

In the new image, I only tested the TCP master and slave, because I don't have the possibility to test the RTU components at the moment.

The reason why I stopped working on the mp_modbus module is, that there is so much more work to do, that is already done in the freemodbus-lib. There is all the error handling and tcp/rtu connection handling, that has do be done. And you need much more space (flash and ram) on the esp32 for a python module in comparison to a compiled image. And at last, I don't have enough experience with modbus. I simply don't know, what should happen in case an error occures. So simply using a working lib, like the freemodbus-lib seems to be the easiest way.

beyonlo commented 2 years ago

Hello @eydam-prototyping

Hey, I uploaded a new version.

Nice, great news!

the Problem with your RTU-master example was, hat you had no Parameter_Descriptor defined. At least one is required. In the future, I will create an error message, if no Parameter_Descriptor is defined.

Understood.

In your second try you had parity=PARITY_NONE in the constructor for the Serial_Master. You needed parity=modbus.PARITY_NONE, because PARITY_NONE is a member of the modbus-module. The same applies for serial_mode=SERIAL_MODE_RTU, it must be serial_mode=modbus.SERIAL_MODE_RTU. With that it should work.

Great! So, please, fix the example https://github.com/eydam-prototyping/micropython/wiki/Module-modbus#modbus-rtu-master It still not using the modbus. before the PARITY_NONE and so on.

New tests are bellow.

ESP32-01 (RTU-Slave) flollowing this example: https://github.com/eydam-prototyping/micropython/wiki/Module-modbus#example-rtu-slave

>>> import os
>>> os.uname()
(sysname='esp32', nodename='esp32', release='1.17.0', version='v1.17-288-ga906a0b46-dirty on 2022-01-15', machine='ESP32 module (spiram) with ESP32')
>>> import modbus
>>> import ubinascii
>>> s = modbus.Serial_Slave(1,1,9600,tx=25, rx=26)
>>> a_holding_1 = modbus.Register_Area(modbus.HOLDING,1,100)
>>> a_holding_2 = modbus.Register_Area(modbus.HOLDING,200,100)
>>> a_input_1 = modbus.Register_Area(modbus.INPUT,1,100)
>>> a_coil_1 = modbus.Register_Area(modbus.COIL,1,100)
>>> a_discrete_1 = modbus.Register_Area(modbus.DISCRETE,1,100)
>>> s.run()
>>> 

ESP32-02 (RTU-Master) -- ### Test 1 - using this Parameter_Descriptor: https://github.com/eydam-prototyping/micropython/wiki/Module-modbus#parameter-descriptor

>>> import os
>>> os.uname()
(sysname='esp32', nodename='esp32', release='1.17.0', version='v1.17-288-ga906a0b46-dirty on 2022-01-15', machine='ESP32 module (spiram) with ESP32')
>>> import modbus
>>> m = modbus.Serial_Master(1, 9600, modbus.PARITY_NONE, tx=26, rx=25)
>>> pd = modbus.Parameter_Descriptor("U", "V", 1, modbus.HOLDING, 305, 1, modbus.TYPE_U8)
>>> m.run()
>>> pd.read()
E (101292) MB_CONTROLLER_MASTER: mbc_master_get_parameter(83): Master get parameter failure, error=(0x107) (ESP_ERR_TIMEOUT).
E (101292) modbus: Characteristic #0 (U) read fail, err = 0x107 (ESP_ERR_TIMEOUT).
>>> pd.write(123)
E (113022) MB_CONTROLLER_MASTER: mbc_master_set_parameter(144): Master set parameter failure, error=(0x108) (ESP_ERR_INVALID_RESPONSE).
E (113022) modbus: Characteristic #0 (U) write fail, err = 0x108 (ESP_ERR_INVALID_RESPONSE).
>>>

Errors happened in pd.read() and pd.write(123)


ESP32-02 (RTU-Master) -- ### Test 2 - This test I used again the https://github.com/eydam-prototyping/micropython/wiki/Module-modbus#modbus-rtu-master just adding the appropriate tx and rx PINs AND putting the modbus. before PARITY_NONE and SERIAL_MODE_RTU.


>>> import os
>>> os.uname()
(sysname='esp32', nodename='esp32', release='1.17.0', version='v1.17-288-ga906a0b46-dirty on 2022-01-15', machine='ESP32 module (spiram) with ESP32')
>>> import modbus
>>> m = modbus.Serial_Master(1, baudrate=9600, parity=modbus.PARITY_NONE, tx=26, rx=25, rts=32, serial_mode=modbus.SERIAL_MODE_RTU)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: function missing 1 required positional arguments
>>> 

Errors happened: missing 1 argument, what is missing? I just following that example pasted above.


In the new image, I only tested the TCP master and slave, because I don't have the possibility to test the RTU components at the moment.

No problem, I tested just the RTU because in the last time was not exists docs about the TCP Master and Slave.

Bellow tests with TCP:

ESP32-01 (TCP-Slave) using this example: https://github.com/eydam-prototyping/micropython/wiki/Module-modbus#example-tcp-slave

>>> import os
>>> os.uname()
(sysname='esp32', nodename='esp32', release='1.17.0', version='v1.17-288-ga906a0b46-dirty on 2022-01-15', machine='ESP32 module (spiram) with ESP32')
>>> import network2
>>> import modbus
>>> import ubinascii
>>> w = network2.Wifi()
>>> w.active(True)
True
>>> w.connect("XX", "XXX")
>>> print(w.connected)
True
>>> print(w.ip_addr)
192.168.1.3
>>> s = modbus.TCP_Slave(502, w)
>>> a_holding_1 = modbus.Register_Area(modbus.HOLDING,1,100)
>>> a_holding_2 = modbus.Register_Area(modbus.HOLDING,200,100)
>>> a_input_1 = modbus.Register_Area(modbus.INPUT,1,100)
>>> a_coil_1 = modbus.Register_Area(modbus.COIL,1,100)
>>> a_discrete_1 = modbus.Register_Area(modbus.DISCRETE,1,100)
>>> s.run()
>>> 

ESP32-02 (TCP-master) using this example: https://github.com/eydam-prototyping/micropython/wiki/Module-modbus#example-tcp-master

>>> 
>>> import os
>>> os.uname()
(sysname='esp32', nodename='esp32', release='1.17.0', version='v1.17-288-ga906a0b46-dirty on 2022-01-15', machine='ESP32 module (spiram) with ESP32')
>>> import network2
>>> import modbus
>>> import ubinascii
>>> w = network2.Wifi()
>>> w.active(True)
True
>>> w.connect("XX", "XXX")
>>> print(w.connected)
True
>>> print(w.ip_addr)
192.168.1.4
>>> s = modbus.TCP_Master("192.168.1.3", 502, w)
>>> params = [
... modbus.Parameter_Descriptor("P1", "", 1, modbus.HOLDING, 1, 1, modbus.TYPE_U8),
... modbus.Parameter_Descriptor("P2", "", 1, modbus.HOLDING, 2, 1, modbus.TYPE_U16),
... modbus.Parameter_Descriptor("P3", "", 1, modbus.HOLDING, 3, 2, modbus.TYPE_U32),
... modbus.Parameter_Descriptor("P4", "", 1, modbus.HOLDING, 5, 2, modbus.TYPE_FLOAT),
... modbus.Parameter_Descriptor("P5", "", 1, modbus.HOLDING, 7, 10, modbus.TYPE_ASCII),
... ]
>>> m.run()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'm' isn't defined
>>> s.run()
>>> params[0].read()
0
>>> params[0].write(15)
>>> params[0].read()
15
>>> 

Errors happened in the m.run() I think the example is wrong, because do not exists the m variable. So, I tried with s.run(), and it runs.

Question: is those results in the in the params[0].read(), params[0].write(15) and params[0].read() correct?

Ps: after both ESP32 was connect to WiFi, I did ping test from my notebook (that are connected in the same WiFi Access Point) and my notebook ping in both ESP32 with success:


pi@pi:~/esp32$ ping 192.168.1.3
PING 192.168.1.3 (192.168.1.3) 56(84) bytes of data.
64 bytes from 192.168.1.3: icmp_seq=1 ttl=255 time=57.8 ms
64 bytes from 192.168.1.3: icmp_seq=2 ttl=255 time=8.12 ms
^C
--- 192.168.1.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 8.121/32.963/57.805/24.842 ms
pi@pi:~/esp32$ ping 192.168.1.4
PING 192.168.1.4 (192.168.1.4) 56(84) bytes of data.
64 bytes from 192.168.1.4: icmp_seq=1 ttl=255 time=1138 ms
64 bytes from 192.168.1.4: icmp_seq=6 ttl=255 time=50.6 ms
64 bytes from 192.168.1.4: icmp_seq=7 ttl=255 time=146 ms
^C

The reason why I stopped working on the mp_modbus module is, that there is so much more work to do, that is already done in the freemodbus-lib. There is all the error handling and tcp/rtu connection handling, that has do be done. And you need much more space (flash and ram) on the esp32 for a python module in comparison to a compiled image. And at last, I don't have enough experience with modbus. I simply don't know, what should happen in case an error occures. So simply using a working lib, like the freemodbus-lib seems to be the easiest way.

I agree with you! If there is a modbus component on ESP-IDF, well tested, is better to use. I my opinion, is important just to be sure (we can test after) if is possible use this component in async mode with uasyncio, thinking in cooperative tasks with uasyncio :)

Thank you very much!

beyonlo commented 2 years ago

Hi @eydam-prototyping

Any news? :)

Also, did you see my tests above?

Thanks!

eydam-prototyping commented 2 years ago

Hey,

sorry for the long pause, but I had a lot of other work to do...

Thank you for the last tests. I just uploaded a new version. I ran a lot of tests and everything worked for me. I also uploaded a new documentation as pdf-file (in the same directory as the images). From now on the other documentation in the github wiki is no longer correct. I will delete it within the next days.

If you have time, you can try it :)

beyonlo commented 2 years ago

@eydam-prototyping

Hello!

sorry for the long pause, but I had a lot of other work to do...

I'm glad that you back :)

Thank you for the last tests. I just uploaded a new version. I ran a lot of tests and everything worked for me. I also uploaded a new documentation as pdf-file (in the same directory as the images). From now on the other documentation in the github wiki is no longer correct. I will delete it within the next days.

All right. I have read the PDF doc and I would like just to notice you that I found two errors - that I think was just typing error:

On the page 1: "The modbus.TCP_Master is a TCP server.", but the correct is that modbus TCP Master is a TCP Client. The TCP Server is the ModBus Slave, where it listen on a port 502.

On the page 3: "The modbus.TCP_Slave is a TCP client.", but the correct is that modbus TCP Slave is a TCP Server - that will be listening on port 502 (waiting for requests)

If you have time, you can try it :)

I will try to do all tests as soon is possible, in this week :)

I have some questions:

  1. The MicroPython 1.18 was released some days ago. Do you have plans to release this firmware with 1.18?
  2. Will this ModBus lib works with uasyncio (in non-blocking socket of course), like as the pure Python modbus library (mp_modbus) that you are/was developing?
  3. About the network2, will it be always necessary and different than the official MicroPython network? Or in the near future will be possible use the official MicroPython network? I worry about if is possible at some moment have this ModBus module (from espressif using the FreeModbus lib) merged on official MicroPython.

Thank you very much.

eydam-prototyping commented 2 years ago

Hi,

On the page 1: "The modbus.TCP_Master is a TCP server.", but the correct is that modbus TCP Master is a TCP Client. The TCP Server is the ModBus Slave, where it listen on a port 502.

On the page 3: "The modbus.TCP_Slave is a TCP client.", but the correct is that modbus TCP Slave is a TCP Server - that will be listening on port 502 (waiting for requests)

Yes, you are right, I will correct that.

The MicroPython 1.18 was released some days ago. Do you have plans to release this firmware with 1.18?

Yes, but that could take some time. I don't know, what the differences are, but the merging could take some time...

Will this ModBus lib works with uasyncio (in non-blocking socket of course), like as the pure Python modbus library (mp_modbus) that you are/was developing?

I'm not completely sure about that, because it is implemented in C, not in Python. But there are no long bocking times. On the master, you have to wait just a few ms until the slave responds. And on the slave it runs completely in the background, I guess in a separate task (not sure how its implemented in ESP-IDF). So you just have to start the slave and then you can do whatever you want (slave.run() is non blocking). You will receive a callback whenever the slave receives data.

About the network2, will it be always necessary and different than the official MicroPython network? Or in the near future will be possible use the official MicroPython network? I worry about if is possible at some moment have this ModBus module (from espressif using the FreeModbus lib) merged on official MicroPython.

I aim to make it work with the network module, but there are some major changes required. Currently you have to pass the wifi interface to the TCP slave/master, because it is required by the freemodbus lib.

beyonlo commented 2 years ago

Hello!

Will this ModBus lib works with uasyncio (in non-blocking socket of course), like as the pure Python modbus library (mp_modbus) that you are/was developing?

I'm not completely sure about that, because it is implemented in C, not in Python. But there are no long bocking times. On the master, you have to wait just a few ms until the slave responds. And on the slave it runs completely in the background, I guess in a separate task (not sure how its implemented in ESP-IDF). So you just have to start the slave and then you can do whatever you want (slave.run() is non blocking). You will receive a callback whenever the slave receives data.

That's great for the Slave where runs completely in the background - non-blocking :) But is bad for the Master where have to wait a few ms until the slave responds. In my tests that time can be 10ms-50ms (depends of course the RS485/Wifi speed and slave processor speed and so on). An alternative for completely non-blocking on Master (just for the Master), is to use the pure python mb_mdbus ONLY for the ModBus Master RTU/TCP implementations, so is possible to use the asyncio.StreamReader(uart) and asyncio.StreamWriter(uart) on ModBus Master. The StreamReader and StreamWriter are non-blocking right? What do you think about that?

About the network2, will it be always necessary and different than the official MicroPython network? Or in the near future will be possible use the official MicroPython network? I worry about if is possible at some moment have this ModBus module (from espressif using the FreeModbus lib) merged on official MicroPython.

I aim to make it work with the network module, but there are some major changes required. Currently you have to pass the wifi interface to the TCP slave/master, because it is required by the freemodbus lib.

Understood.

I want in the future to put a LAN ethernet on the ESP32-S3. As ESP32-S3 has no MAC controller like as ESP32, I'm thinking to use the very cheap W5500 CI. It is a SPI LAN Ethernet where has the MAC and PHY integrated. That CI already works with STM and MicroPython (https://docs.micropython.org/en/latest/library/network.WIZNET5K.html?highlight=w5500), not sure about ESP32 family. Anyway, will be possible to pass as argument that "LAN ethernet" interface to the freemodbus lib to works on LAN Ethernet too?

Thank you!

eydam-prototyping commented 2 years ago

I just uploaded a new version with the callback method for the slave. So everytime the slave receives a modbus message, it tells you, which register has changed or was read. There is also a new documentation.

That's great for the Slave where runs completely in the background - non-blocking :) But is bad for the Master where have to wait a few ms until the slave responds. In my tests that time can be 10ms-50ms (depends of course the RS485/Wifi speed and slave processor speed and so on). An alternative for completely non-blocking on Master (just for the Master), is to use the pure python mb_mdbus ONLY for the ModBus Master RTU/TCP implementations, so is possible to use the asyncio.StreamReader(uart) and asyncio.StreamWriter(uart) on ModBus Master. The StreamReader and StreamWriter are non-blocking right? What do you think about that?

I think it's not a good idea to maintain different libs for master an slave. But I just took a look at the uasyncio module in micropython and it looks like there is only a python wrapper around the C modbus lib required. So it should be relatively easy to adapt the module. If I have enough time, I will try it this weekend.

I want in the future to put a LAN ethernet on the ESP32-S3. As ESP32-S3 has no MAC controller like as ESP32, I'm thinking to use the very cheap W5500 CI. It is a SPI LAN Ethernet where has the MAC and PHY integrated. That CI already works with STM and MicroPython (https://docs.micropython.org/en/latest/library/network.WIZNET5K.html?highlight=w5500), not sure about ESP32 family. Anyway, will be possible to pass as argument that "LAN ethernet" interface to the freemodbus lib to works on LAN Ethernet too?

I'm not sure. The modbus lib needs an esp_netif instance to run. I don't know, if we can get it, if we don't use the internal MAC. I will take a look at it. Would it be possible to use an ESP32 together with a LAN8720 or RTL8201-Chip?

beyonlo commented 2 years ago

Hey @eydam-prototyping

I just uploaded a new version with the callback method for the slave. So everytime the slave receives a modbus message, it tells you, which register has changed or was read. There is also a new documentation.

Great news about the callback method on the Slave, and the new doc!

I think it's not a good idea to maintain different libs for master an slave. But I just took a look at the uasyncio module in micropython and it looks like there is only a python wrapper around the C modbus lib required. So it should be relatively easy to adapt the module. If I have enough time, I will try it this weekend.

Will be amazing have the asyncronous (non-blocking) FreeModBus lib (Master) working with asyncio! Please, let me know if works :)

I'm not sure. The modbus lib needs an esp_netif instance to run. I don't know, if we can get it, if we don't use the internal MAC. I will take a look at it. Would it be possible to use an ESP32 together with a LAN8720 or RTL8201-Chip?

Yes, is possible to use an ESP32 together the ESP32-S3, just to works ethernet LAN (using the LAN8720 or RTL8201 chip), but that will introduce several problems.

In my opinion, ESP32 + LAN8720 or RTL8201 is great!

ESP32-S3 ------- UART ------ ESP32 ---- (LAN8720 or RTL8201)

So, I have a UART interface between ESP32 and ESP32-S3 where I can use a protocol like as msgpack for example to communicate between both. But add ESP32 just for ethernet will introduce these problems:

My ESP32-S3 will have a web interface (using microdot probably), will have MQTT client, websocket Server, and JSON-RPC. Well, that all protocols/applications can't works over UART if user will access IP via ethernet. So I need, like as Modbus, to put all that protocols/applications in both microcontrollers, and communicate each other via UART (using asyncio.StreamReader() and asyncio.StreamWriter()) . Just as an example, when user will access web interface via WiFi, will access the ESP32-S3, and when user will access web interface via Ethernet LAN, will access the ESP32 chip. So that will needs for everything: modbus, mqtt, websocket server, etc - everything in both microcontrollers.

  1. That is a lot of work and maintenance problems, and more software complexity;
  2. UART is not so speed;

Well, I could use the ESP32 instead ESP-S3 to do WiFi + Ethernet LAN in the same chip, and use ESP32-S3 for the rest, but I would like to use Bluetooth 5 (ESP32 has just 4.2), and I would be free to use another Espressif chip that can be released in the future, like as with WiFi6 feature (ESP32-C6 already has the WiFi6). And using ESP32 for WiFi/BLE just to works Ethernet LAN, I lost all the new features from other chips.

Sorry for long story, but was necessary to show a bit of my scenario!

Would be great if the freemodbus lib can be works with LAN8720/RTL8201 using the ESP32 MAC and with SPI Ethernet controllers, so any ESP32 chip can be used with freemodbus lib on ethernet LAN, not just ESP32.

Ps: I see that Espressif now support officially the SPI ethernet LAN chips (like as w5500). Well, if them support officially that, and they included the freemodbus lib on their SDK, maybe there is a "virtual" esp_netif for SPI LANs Chips :). They can think: people could to use the freemodbus lib (TCP) in ethernet LAN too, not just on WiFi.

Thank you in advance!

eydam-prototyping commented 2 years ago

Hey, I just found out that you can pass None as argument for the netif. So you could start your server with s = modbus.TCP_Slave(502, None). At least it works with WIFI STA. I haven't tested ETH. So that should solve a lot of your problems :)

Edit: I just updated to mp version 1.18

beyonlo commented 2 years ago

Hello!

Hey, I just found out that you can pass None as argument for the netif. So you could start your server with s = modbus.TCP_Slave(502, None). At least it works with WIFI STA. I haven't tested ETH. So that should solve a lot of your problems :)

That's a great news!

Well, I think that the intention to pass as argument the netif is just to TCP Socket Server listen just on a especific network interface (for security reasons). So, maybe when you pass None to the TCP Socket Server it will listen in all network interfaces. That make sense for you? Need to test to check if that is true :)

Edit: I just updated to mp version 1.18

Amazing!

Thank you!

papercodeIN commented 1 year ago

Hi @eydam-prototyping,

I have some silly questions, How can I use your .bin files in Raspberry Pi Pico W, which you have provided in Google Drive ? Could You Please guide me ?

Thanks.