brainelectronics / micropython-modbus

MicroPython Modbus RTU Slave/Master and TCP Server/Slave library
GNU General Public License v3.0
104 stars 45 forks source link

Trying to implement TCP client on RPi Pico with ETH HAT #57

Closed MartiMan79 closed 1 year ago

MartiMan79 commented 1 year ago

Dear all, I'm trying to realize a cheap solution for dimming LEDs with the onboard PWM. This will be controlled by a PLC over ModbusTCP but I'm struggling to get the WIZnet RP2040-HAT-MicroPython code implemented into this example...

I receive a [Errno 22] EINVAL error and not capable to solve it as I have no experience with coding.

This is the code of the WIZnet HTTP server example that works on my Pico:

from usocket import socket
from machine import Pin,SPI
import network
import time

led = Pin(25, Pin.OUT)

#W5x00 chip init
def w5x00_init():
    spi=SPI(0,2_000_000, mosi=Pin(19),miso=Pin(16),sck=Pin(18))
    nic = network.WIZNET5K(spi,Pin(17),Pin(20)) #spi,cs,reset pin
    nic.active(True)

    #None DHCP
    nic.ifconfig(('192.168.0.210','255.255.255.0','192.168.0.1','8.8.8.8'))

    #DHCP
    #nic.ifconfig('dhcp')
    print('IP address :', nic.ifconfig())

    while not nic.isconnected():
        time.sleep(1)
        print(nic.regs())

def web_page():
    if led.value()==1:
        led_state="ON"
    else:
        led_state="OFF"

    html = """
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Raspberry Pi Pico Web server - WIZnet W5100S</title>
    </head>
    <body>
    <div align="center">
    <H1>Raspberry Pi Pico Web server & WIZnet Ethernet HAT</H1>
    <h2>Control LED</h2>
    PICO LED state: <strong>""" + led_state + """</strong>
    <p><a href="/?led=on"><button class="button">ON</button></a><br>
    </p>
    <p><a href="/?led=off"><button class="button button2">OFF</button></a><br>
    </p>
    </div>
    </body>
    </html>
    """
    return html

def main():
    w5x00_init()
    s = socket()
    s.bind(('192.168.0.210', 502))
    s.listen(5)

    while True:
        conn, addr = s.accept()
        print('Connect from %s' % str(addr))
        request = conn.recv(1024)
        request = str(request)
        #print('Content = %s' % request)
        led_on = request.find('/?led=on')
        led_off = request.find('/?led=off')
        if led_on == 6:
            print("LED ON")
            led.value(1)
        if led_off == 6:
            print("LED OFF")
            led.value(0)
        response = web_page()
        conn.send('HTTP/1.1 200 OK\n')
        conn.send('Connection: close\n')
        conn.send('Content-Type: text/html\n')
        conn.send('Content-Length: %s\n\n' % len(response))
        conn.send(response)
        conn.close()

if __name__ == "__main__":
    main()

This is the code I ended up with combining the 2:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

"""
Main script

Do your stuff here, this file is similar to the loop() function on Arduino

Create a Modbus TCP client (slave) which can be requested for data or set with
specific values by a host device.

The TCP port and IP address can be choosen freely. The register definitions of
the client can be defined by the user.
"""

# system packages
import time

# import modbus client classes
from umodbus.tcp import ModbusTCP
from machine import Pin,SPI

IS_DOCKER_MICROPYTHON = False
try:
    import network
except ImportError:
    IS_DOCKER_MICROPYTHON = True
    import json

# ===============================================
if IS_DOCKER_MICROPYTHON is False:
    # connect to a network

    spi = SPI(0,2_000_000, mosi=Pin(19),miso=Pin(16),sck=Pin(18))
    station = network.WIZNET5K(spi,Pin(17),Pin(20)) #spi,cs,reset pin
    if station.active() and station.isconnected():
        station.disconnect()
        time.sleep(1)
    station.active(False)
    time.sleep(1)
    station.active(True)

    while True:
        print('Waiting for connection...')
        if station.isconnected():
            print('Connected.')
            print(station.ifconfig())
            break
        time.sleep(2)
#
# ===============================================
# TCP Slave setup
tcp_port = 502              # port to listen to

if IS_DOCKER_MICROPYTHON:
    local_ip = '192.168.0.210'     # static Docker IP address
else:
    # set IP address of the MicroPython device explicitly
    # local_ip = '192.168.4.1'    # IP address
    # or get it from the system after a connection to the network has been made
    local_ip = station.ifconfig()[0]

# ModbusTCP can get TCP requests from a host device to provide/set data
client = ModbusTCP()
is_bound = False

# check whether client has been bound to an IP and port
is_bound = client.get_bound_status()

if not is_bound:
    client.bind(local_ip=local_ip, local_port=tcp_port)

def my_holding_register_set_cb(reg_type, address, val):
    print('Custom callback, called on setting {} at {} to: {}'.
          format(reg_type, address, val))

def my_holding_register_get_cb(reg_type, address, val):
    print('Custom callback, called on getting {} at {}, currently: {}'.
          format(reg_type, address, val))

    print('Custom callback, called on getting {} at {}, currently: {}'.
          format(reg_type, address, val))

    # any operation should be as short as possible to avoid response timeouts
    new_val = val[0] + 1

    # It would be also possible to read the latest ADC value at this time
    # adc = machine.ADC(12)     # check MicroPython port specific syntax
    # new_val = adc.read()

    client.set_ireg(address=address, value=new_val)
    print('Incremented current value by +1 before sending response')

def reset_data_registers_cb(reg_type, address, val):
    # usage of global isn't great, but okay for an example
    global client
    global register_definitions

    print('Resetting register data to default values ...')
    client.setup_registers(registers=register_definitions)
    print('Default values restored')

# commond slave register setup, to be used with the Master example above
register_definitions = {
    "HREGS": {
        "EXAMPLE_HREG": {
            "register": 93,
            "len": 1,
            "val": 19
        }
    }
}

# alternatively the register definitions can also be loaded from a JSON file
# this is always done if Docker is used for testing purpose in order to keep
# the client registers in sync with the test registers
if IS_DOCKER_MICROPYTHON:
    with open('registers/example.json', 'r') as file:
        register_definitions = json.load(file)

# add callbacks for different Modbus functions
# each register can have a different callback
# coils and holding register support callbacks for set and get
register_definitions['HREGS']['EXAMPLE_HREG']['on_set_cb'] = \
    my_holding_register_set_cb
register_definitions['HREGS']['EXAMPLE_HREG']['on_get_cb'] = \
    my_holding_register_get_cb

print('Setting up registers ...')
# use the defined values of each register type provided by register_definitions
client.setup_registers(registers=register_definitions)
# alternatively use dummy default values (True for bool regs, 999 otherwise)
# client.setup_registers(registers=register_definitions, use_default_vals=True)
print('Register setup done')

print('Serving as TCP client on {}:{}'.format(local_ip, tcp_port))

while True:
    try:
        result = client.process()
    except KeyboardInterrupt:
        print('KeyboardInterrupt, stopping TCP client...')
        break
    except Exception as e:
        print('Exception during execution: {}'.format(e))

print("Finished providing/accepting data as client")

PLC Engineer exploring new fields is thanking you all in advance!

beyonlo commented 1 year ago

@MartiMan79 I think that your problem is just about the network and not about the ModBus.

I just changed your second pasted code and put that function (def w5x00_init():) that start the network and call it for wait to config the ETH, like as works in your HTTP example. After I put that fixed IP local_ip = '192.168.0.210'. I think this code below can works - I do not test it because I have no RPi Pico.

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

"""
Main script

Do your stuff here, this file is similar to the loop() function on Arduino

Create a Modbus TCP client (slave) which can be requested for data or set with
specific values by a host device.

The TCP port and IP address can be choosen freely. The register definitions of
the client can be defined by the user.
"""

# system packages
import time

# import modbus client classes
from umodbus.tcp import ModbusTCP
from machine import Pin,SPI

IS_DOCKER_MICROPYTHON = False
try:
    import network
except ImportError:
    IS_DOCKER_MICROPYTHON = True
    import json

#W5x00 chip init
def w5x00_init():
    spi=SPI(0,2_000_000, mosi=Pin(19),miso=Pin(16),sck=Pin(18))
    nic = network.WIZNET5K(spi,Pin(17),Pin(20)) #spi,cs,reset pin
    nic.active(True)

    #None DHCP
    nic.ifconfig(('192.168.0.210','255.255.255.0','192.168.0.1','8.8.8.8'))

    #DHCP
    #nic.ifconfig('dhcp')
    print('IP address :', nic.ifconfig())

    while not nic.isconnected():
        time.sleep(1)
        print(nic.regs())

# ===============================================
if IS_DOCKER_MICROPYTHON is False:
    # connect to a network
    w5x00_init()
#
# ===============================================
# TCP Slave setup
tcp_port = 502              # port to listen to

if IS_DOCKER_MICROPYTHON:
    local_ip = '192.168.0.210'     # static Docker IP address
else:
    # set IP address of the MicroPython device explicitly
    # local_ip = '192.168.4.1'    # IP address
    # or get it from the system after a connection to the network has been made
    local_ip = '192.168.0.210'

# ModbusTCP can get TCP requests from a host device to provide/set data
client = ModbusTCP()
is_bound = False

# check whether client has been bound to an IP and port
is_bound = client.get_bound_status()

if not is_bound:
    client.bind(local_ip=local_ip, local_port=tcp_port)

def my_holding_register_set_cb(reg_type, address, val):
    print('Custom callback, called on setting {} at {} to: {}'.
          format(reg_type, address, val))

def my_holding_register_get_cb(reg_type, address, val):
    print('Custom callback, called on getting {} at {}, currently: {}'.
          format(reg_type, address, val))

    print('Custom callback, called on getting {} at {}, currently: {}'.
          format(reg_type, address, val))

    # any operation should be as short as possible to avoid response timeouts
    new_val = val[0] + 1

    # It would be also possible to read the latest ADC value at this time
    # adc = machine.ADC(12)     # check MicroPython port specific syntax
    # new_val = adc.read()

    client.set_ireg(address=address, value=new_val)
    print('Incremented current value by +1 before sending response')

def reset_data_registers_cb(reg_type, address, val):
    # usage of global isn't great, but okay for an example
    global client
    global register_definitions

    print('Resetting register data to default values ...')
    client.setup_registers(registers=register_definitions)
    print('Default values restored')

# commond slave register setup, to be used with the Master example above
register_definitions = {
    "HREGS": {
        "EXAMPLE_HREG": {
            "register": 93,
            "len": 1,
            "val": 19
        }
    }
}

# alternatively the register definitions can also be loaded from a JSON file
# this is always done if Docker is used for testing purpose in order to keep
# the client registers in sync with the test registers
if IS_DOCKER_MICROPYTHON:
    with open('registers/example.json', 'r') as file:
        register_definitions = json.load(file)

# add callbacks for different Modbus functions
# each register can have a different callback
# coils and holding register support callbacks for set and get
register_definitions['HREGS']['EXAMPLE_HREG']['on_set_cb'] = \
    my_holding_register_set_cb
register_definitions['HREGS']['EXAMPLE_HREG']['on_get_cb'] = \
    my_holding_register_get_cb

print('Setting up registers ...')
# use the defined values of each register type provided by register_definitions
client.setup_registers(registers=register_definitions)
# alternatively use dummy default values (True for bool regs, 999 otherwise)
# client.setup_registers(registers=register_definitions, use_default_vals=True)
print('Register setup done')

print('Serving as TCP client on {}:{}'.format(local_ip, tcp_port))

while True:
    try:
        result = client.process()
    except KeyboardInterrupt:
        print('KeyboardInterrupt, stopping TCP client...')
        break
    except Exception as e:
        print('Exception during execution: {}'.format(e))

print("Finished providing/accepting data as client")
MartiMan79 commented 1 year ago

@beyonlo Thanks for your effort and your approach makes sense but sadly still the same error although I get a "connected to" status in Hercules. When I disconnect, it gives me a 10061 error upon 2nd connect...

MartiMan79 commented 1 year ago

This is my log from the shell when running without the "except Exception as e:"

IP address : ('192.168.0.210', '255.255.255.0', '192.168.0.1', '8.8.8.8')
Client is bound
Setting up registers ...
Register setup done
Serving as TCP client on 192.168.0.210:502
Traceback (most recent call last):
  File "<stdin>", line 152, in <module>
  File "/lib/umodbus/modbus.py", line 64, in process
  File "/lib/umodbus/tcp.py", line 416, in get_request
  File "/lib/umodbus/tcp.py", line 325, in _accept_request
OSError: [Errno 22] EINVAL

So it looks like the error gets triggered from the modbus lib while running:

while True:
    try:
        result = client.process()
beyonlo commented 1 year ago

@MartiMan79 Now looks like that network is working, and ModBus TCP Slave is running.

File "/lib/umodbus/tcp.py", line 325, in _accept_request set the socket timeout: https://github.com/brainelectronics/micropython-modbus/blob/develop/umodbus/tcp.py#L325

Unfortunately from this point I have no knowledge to check this issue. But I'm sure that @brainelectronics will check this problem.

Anyway, please, tell us what is the MicroPython version and ModBus version that you are using - that is a good information.

ModBus version:

>>> from umodbus import version
>>> print('Running ModBus version: {}'.format(version.__version__))

MicroPython version:

>>> import os
>>> os.uname()
brainelectronics commented 1 year ago

Hi @MartiMan79, welcome to new world of embedded Python :) Let's find the root cause of the issue together 💪

Could you check whether this is really 100% the code you are using? I'm looking for the location/print which reports Client is bound, I yet don't see it in your provided code.

EINVAL is related to the binding status of the device in a sense. Together with your report

@beyonlo Thanks for your effort and your approach makes sense but sadly still the same error although I get a "connected to" status in Hercules. When I disconnect, it gives me a 10061 error upon 2nd connect...

it makes sense. It might be that there are requests sent to the MicroPython device by the Hercules software you are using, but the connection has already been terminated (socket is closed) and on the second request it might try to reuse that, already closed, connection. May you can provide more details you get from the Hercules software? Searching quickly for the PLC + 10061 error I found this report

I've cleaned up your code a little bit and removed unnecessary data and functions. Also a big thank you to @beyonlo for the support so far. Additionally this code now prints the used MicroPython Modbus version, MicroPython system info and details on the binding status.

Could you finally check or confirm that you are using port 502 in Hercules to communicate with the MicroPython device?

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

# system packages
import time
import os

# import modbus client classes
from umodbus.tcp import ModbusTCP
from umodbus import version
from machine import Pin, SPI

import network

# W5x00 chip init
def w5x00_init():
    spi = SPI(0, 2_000_000, mosi=Pin(19), miso=Pin(16), sck=Pin(18))
    nic = network.WIZNET5K(spi, Pin(17), Pin(20))   # spi, cs, reset pin
    nic.active(True)

    # None DHCP
    nic.ifconfig(('192.168.0.210', '255.255.255.0', '192.168.0.1', '8.8.8.8'))

    # DHCP
    # nic.ifconfig('dhcp')

    while True:
        print('Waiting for network connection...')
        if nic.isconnected():
            print('Connected to network.')
            print(nic.ifconfig())
            break
        print(nic.regs())
        time.sleep(2)

    print('Network connection established')
    print('IP address: ', nic.ifconfig())

print("MicroPython infos: {}".format(os.uname()))
print("Used micropthon-modbus version: {}".format(version.__version__))

# ===============================================
# connect to a network
w5x00_init()

# ===============================================
# TCP Slave setup
tcp_port = 502              # port to listen to

# set IP address of the MicroPython device explicitly
local_ip = '192.168.0.210'
# or get it from the system after a connection to the network has been made
# local_ip = nic.ifconfig()[0]

# ModbusTCP can get TCP requests from a host device to provide/set data
client = ModbusTCP()
is_bound = False

# check whether client has been bound to an IP and port
is_bound = client.get_bound_status()
print('Initial IP and port binding status: {}'.format(is_bound))

if not is_bound:
    client.bind(local_ip=local_ip, local_port=tcp_port)
    print('Now bound to address {} and port {}'.format(local_ip, tcp_port))

def my_holding_register_set_cb(reg_type, address, val):
    print('Custom callback, called on setting {} at {} to: {}'.
          format(reg_type, address, val))

def my_holding_register_get_cb(reg_type, address, val):
    print('Custom callback, called on getting {} at {}, currently: {}'.
          format(reg_type, address, val))

    print('Custom callback, called on getting {} at {}, currently: {}'.
          format(reg_type, address, val))

    # any operation should be as short as possible to avoid response timeouts
    new_val = val[0] + 1

    # It would be also possible to read the latest ADC value at this time
    # adc = machine.ADC(12)     # check MicroPython port specific syntax
    # new_val = adc.read()

    client.set_ireg(address=address, value=new_val)
    print('Incremented current value by +1 before sending response')

def reset_data_registers_cb(reg_type, address, val):
    # usage of global isn't great, but okay for an example
    global client
    global register_definitions

    print('Resetting register data to default values ...')
    client.setup_registers(registers=register_definitions)
    print('Default values restored')

# commond slave register setup, to be used with the Master example above
register_definitions = {
    "HREGS": {
        "EXAMPLE_HREG": {
            "register": 93,
            "len": 1,
            "val": 19
        }
    }
}

# add callbacks for different Modbus functions
# each register can have a different callback
# coils and holding register support callbacks for set and get
register_definitions['HREGS']['EXAMPLE_HREG']['on_set_cb'] = \
    my_holding_register_set_cb
register_definitions['HREGS']['EXAMPLE_HREG']['on_get_cb'] = \
    my_holding_register_get_cb

print('Setting up registers ...')
# use the defined values of each register type provided by register_definitions
client.setup_registers(registers=register_definitions)
# alternatively use dummy default values (True for bool regs, 999 otherwise)
# client.setup_registers(registers=register_definitions, use_default_vals=True)
print('Register setup done')

print('Serving as TCP client on {}:{}'.format(local_ip, tcp_port))

while True:
    try:
        result = client.process()
    except KeyboardInterrupt:
        print('KeyboardInterrupt, stopping TCP client...')
        break
    except Exception as e:
        print('Exception during execution: {}'.format(e))

print("Finished providing/accepting data as client")
MartiMan79 commented 1 year ago

@brainelectronics Thanks for your reply!

Hi @MartiMan79, welcome to new world of embedded Python :) Let's find the root cause of the issue together 💪

Awesome, really hope to get this working!

Could you check whether this is really 100% the code you are using? I'm looking for the location/print which reports Client is bound, I yet don't see it in your provided code.

EINVAL is related to the binding status of the device in a sense. Together with your report

After getting the adjusted code from beyonlo I added some prints for debugging, as I said I'm really new to this ;)

it makes sense. It might be that there are requests sent to the MicroPython device by the Hercules software you are using, but the connection has already been terminated (socket is closed) and on the second request it might try to reuse that, already closed, connection. May you can provide more details you get from the Hercules software? Searching quickly for the PLC + 10061 error I found this report

The hercules software I'm using is testing software:

image

Is there anything else you can recommend using to simulate and debug Modbus communication? I downloaded ModbusMaster32 from www.modbus.pl and it gives a read error time-out after being connected. I also got wireshark for if things get really messy :)

ModbusMaster gives me this:

TX: [0x00][0x01][0x00][0x00][0x00][0x06][0x01][0x03][0x00][0x00][0x00][0x64]
RX: 
TX: [0x00][0x01][0x00][0x00][0x00][0x06][0x01][0x03][0x00][0x00][0x00][0x64]
RX: 
TX: [0x00][0x01][0x00][0x00][0x00][0x06][0x01][0x03][0x00][0x00][0x00][0x64]
RX: 
TX: [0x00][0x01][0x00][0x00][0x00][0x06][0x01][0x03][0x00][0x00][0x00][0x64]
RX: 

From my understanding, the client should run error free even when there is no active master available (connected). Or at least that's how I'm used working with Modbus in the automation world. Also you have to close and open the connection at any given time as the PLC uses sequential connections so it has to close client 1 before connecting to 2, etc...

I've cleaned up your code a little bit and removed unnecessary data and functions. Also a big thank you to @beyonlo for the support so far. Additionally this code now prints the used MicroPython Modbus version, MicroPython system info and details on the binding status.

Thanks for your effort! Running the code without any master connected still gives me the following error: (Again I uncommented the following lines as the shell errors are repetitive and will delete all the prior "printed" info from the buffer)

     #except Exception as e:
     #   print('Exception during execution: {}'.format(e))
MicroPython infos: (sysname='rp2', nodename='rp2', release='1.19.1', version='c77e0bd2f on 2022-11-09 (GNU 9.2.1 MinSizeRel)', machine='W5100S-EVB-Pico with RP2040')
Used micropthon-modbus version: 2.3.2
Waiting for network connection...
Wiz CREG:
  0000: 03 c0 a8 00 01 ff ff ff 00 02 57 2b 34 61 9a c0
  0010: a8 00 d2 00 00 00 00 07 d0 07 55 55 00 00 00 00
  0020: 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00
  0030: 40 00 00 00 00 00 00 00 00 00 ff ff 06 81 0a 00
  0040: 00 00 00 00 00 01 00 40 00 00 00 00 00 07 d0 00
  0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0060: 00 00 00 0c 00 00 00 00 00 00 00 00 00 00 00 00
  0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0080: 51 00 0a 16 00 00 00 00 00 00 00 00 00 00 00 00
Wiz SREG[0]:
  0000: 00 00 00 00 00 00 ff ff ff ff ff ff 00 00 00 00
  0010: 00 00 00 00 00 00 80 00 00 00 00 00 00 00 02 02
  0020: 08 00 00 00 00 00 00 00 00 00 00 00 ff 40 00 00
Wiz SREG[1]:
  0000: 00 00 00 00 00 00 ff ff ff ff ff ff 00 00 00 00
  0010: 00 00 00 00 00 00 80 00 00 00 00 00 00 00 02 02
  0020: 08 00 00 00 00 00 00 00 00 00 00 00 ff 40 00 00
Wiz SREG[2]:
  0000: 00 00 00 00 00 00 ff ff ff ff ff ff 00 00 00 00
  0010: 00 00 00 00 00 00 80 00 00 00 00 00 00 00 02 02
  0020: 08 00 00 00 00 00 00 00 00 00 00 00 ff 40 00 00
Wiz SREG[3]:
  0000: 00 00 00 00 00 00 ff ff ff ff ff ff 00 00 00 00
  0010: 00 00 00 00 00 00 80 00 00 00 00 00 00 00 02 02
  0020: 08 00 00 00 00 00 00 00 00 00 00 00 ff 40 00 00
None
Waiting for network connection...
Wiz CREG:
  0000: 03 c0 a8 00 01 ff ff ff 00 02 57 2b 34 61 9a c0
  0010: a8 00 d2 00 00 00 00 07 d0 07 55 55 00 00 00 00
  0020: 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00
  0030: 40 00 00 00 00 00 00 00 00 00 ff ff 06 85 0a 00
  0040: 00 00 00 00 00 01 00 40 00 00 00 00 00 07 d0 00
  0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0060: 00 00 00 0c 00 00 00 00 00 00 00 00 00 00 00 00
  0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0080: 51 00 58 ad 00 00 00 00 00 00 00 00 00 00 00 00
Wiz SREG[0]:
  0000: 00 00 00 00 00 00 ff ff ff ff ff ff 00 00 00 00
  0010: 00 00 00 00 00 00 80 00 00 00 00 00 00 00 02 02
  0020: 08 00 00 00 00 00 00 00 00 00 00 00 ff 40 00 00
Wiz SREG[1]:
  0000: 00 00 00 00 00 00 ff ff ff ff ff ff 00 00 00 00
  0010: 00 00 00 00 00 00 80 00 00 00 00 00 00 00 02 02
  0020: 08 00 00 00 00 00 00 00 00 00 00 00 ff 40 00 00
Wiz SREG[2]:
  0000: 00 00 00 00 00 00 ff ff ff ff ff ff 00 00 00 00
  0010: 00 00 00 00 00 00 80 00 00 00 00 00 00 00 02 02
  0020: 08 00 00 00 00 00 00 00 00 00 00 00 ff 40 00 00
Wiz SREG[3]:
  0000: 00 00 00 00 00 00 ff ff ff ff ff ff 00 00 00 00
  0010: 00 00 00 00 00 00 80 00 00 00 00 00 00 00 02 02
  0020: 08 00 00 00 00 00 00 00 00 00 00 00 ff 40 00 00
None
Waiting for network connection...
Connected to network.
('192.168.0.210', '255.255.255.0', '192.168.0.1', '8.8.8.8')
Network connection established
IP address:  ('192.168.0.210', '255.255.255.0', '192.168.0.1', '8.8.8.8')
Initial IP and port binding status: False
Now bound to address 192.168.0.210 and port 502
Setting up registers ...
Register setup done
Serving as TCP client on 192.168.0.210:502
Traceback (most recent call last):
  File "<stdin>", line 133, in <module>
  File "/lib/umodbus/modbus.py", line 64, in process
  File "/lib/umodbus/tcp.py", line 416, in get_request
  File "/lib/umodbus/tcp.py", line 325, in _accept_request
OSError: [Errno 22] EINVAL
MartiMan79 commented 1 year ago

Well, I made progress... As I am using the WIZnet Ethernet HAT, I saw on their site there were some issues with the FW. I downloaded the FW from https://micropython.org/resources/firmware/W5100S_EVB_PICO-20220618-v1.19.1.uf2 And it works! At least I'm getting a "Modbus exeption 02 - ILLEGAL DATA ADDRESS" error from Modbus Master tester.

But the shell now gives me:

MicroPython infos: (sysname='rp2', nodename='rp2', release='1.19.1', version='v1.19.1 on 2022-06-18 (GNU 11.2.0 MinSizeRel)', machine='W5100S-EVB-Pico with RP2040')
Used micropthon-modbus version: 2.3.3
Waiting for network connection...
Wiz CREG:
  0000: 03 00 00 00 00 00 00 00 00 02 57 2b 34 61 9a 00
  0010: 00 00 00 00 00 00 00 07 d0 07 55 55 00 00 00 00
  0020: 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00
  0030: 40 00 00 00 00 00 00 00 00 00 ff ff 06 81 0a 00
  0040: 00 00 00 00 00 01 00 40 00 00 00 00 00 07 d0 00
Wiz SREG[0]:
  0000: 44 00 00 42 c0 03 ff ff ff ff ff ff 00 00 00 00
  0010: 00 00 05 c0 00 00 80 00 00 00 00 00 00 00 02 02
  0020: 08 00 01 5e 01 5e 00 00 00 00 00 00 ff 40 00 00
Wiz SREG[1]:
  0000: 00 00 00 00 00 00 ff ff ff ff ff ff 00 00 00 00
  0010: 00 00 00 00 00 00 80 00 00 00 00 00 00 00 02 02
  0020: 08 00 00 00 00 00 00 00 00 00 00 00 ff 40 00 00
Wiz SREG[2]:
  0000: 00 00 00 00 00 00 ff ff ff ff ff ff 00 00 00 00
  0010: 00 00 00 00 00 00 80 00 00 00 00 00 00 00 02 02
  0020: 08 00 00 00 00 00 00 00 00 00 00 00 ff 40 00 00
Wiz SREG[3]:
  0000: 00 00 00 00 00 00 ff ff ff ff ff ff 00 00 00 00
  0010: 00 00 00 00 00 00 80 00 00 00 00 00 00 00 02 02
  0020: 08 00 00 00 00 00 00 00 00 00 00 00 ff 40 00 00
None
Waiting for network connection...
Connected to network.
('192.168.0.210', '255.255.255.0', '192.168.0.1', '8.8.8.8')
Network connection established
IP address:  ('192.168.0.210', '255.255.255.0', '192.168.0.1', '8.8.8.8')
Initial IP and port binding status: False
Now bound to address 192.168.0.210 and port 502
Setting up registers ...
Register setup done
Serving as TCP client on 192.168.0.210:502

Although I have no idea how to route a value from the registry to the physical gpio pin, I'm making progress.

MartiMan79 commented 1 year ago

As I am able to read and write the registry through the json file, I'm closing this issue. Apparently it's a bug in the WIZnet FW and my issue had nothing to do with the umodbus library.

Now I will need to find a way to extract the values from the registry and send them to the GPIO pins which might be my next question...

brainelectronics commented 1 year ago

Good morning @MartiMan79 I'm happy that you found and solved the issue! I'm currently quite busy finding a new daytime job, so sorry for the delay in communication. Anyway, I'll update the docs and place a new section with troubleshooting FAQ so others don't have to struggle the same way in case they use that HAT.

Regarding #61 I'll place a comment as well to assist you with a solution 🙂