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

Implementing GPIO pins in TCP client mode on Pico #61

Closed MartiMan79 closed 1 year ago

MartiMan79 commented 1 year ago

Description

Finally I got the communication running but I'm really stuck with how to transfer the ModbusTCP received values (register) to the GPIO pins... After days of searching the web I find no examples at all regarding what I'm trying to do here. I know this is not an issue with the library but maybe useful for others in the future as documentation.

So my apologies in advance for such a rookie question.

Reproduction steps

I will receive multiple 0-100% values (HREG) in the registry from my s7-1200 PLC and want to control onboard PWMs accordingly as a cheap way to dim LED lighting in my house.

Per example, how do I get the value of this register entry:

"EXAMPLE_HREG": {
            "register": 1,
            "len": 1,
            "val": 19,
            "description": "Example HREGS register, Hregs (setter+getter) [0, 65535]",
            "range": "",
            "unit": ""
        }

as a usable value inside the rest of the code? Let's say in here, where the 32000 is:

def PWM_init():
    LED_BUILTIN = 25 # For Raspberry Pi Pico

    pwm_led = PWM(Pin(LED_BUILTIN, mode=Pin.OUT)) # Attach PWM object on the LED pin

    # Settings
    pwm_led.freq(1000)

    while True:
        pwm_led.duty_u16(32000)

MicroPython version

v1.19.1

MicroPython board

Raspberry Pico

Relevant log output

No response

User code

No response

Additional informations

No response

MartiMan79 commented 1 year ago

P.S.: My S7-1200 is now communicating perfectly with the Pico! Just getting the registers connected to the GPIO is the last step...

Would love to support your work, so please let me know how to donate (Wenn möglich natürlich, mit Grüssen aus Belgien). Especially if you compare the S7 module prices to the Pico 🤣

brainelectronics commented 1 year ago

Good morning again @MartiMan79

May you have a look at this example function It's a simple callback executed whenever a new register value is set.

You could use your pin as global and update it on every HREG change for example. Not 100% sure with the code, had no hardware or second screen available on the way to work

pwm_led = None

# ... other setup and instructions here 

def PWM_init():
    global pwm_led

    LED_BUILTIN = 25 # For Raspberry Pi Pico

    pwm_led = PWM(Pin(LED_BUILTIN, mode=Pin.OUT)) # Attach PWM object on the LED pin

    # Settings
    pwm_led.freq(1000)

def my_holding_register_set_cb(reg_type, address, val):
    global pwm_pin

    print('Custom callback, called on setting {} at {} to: {}'.
          format(reg_type, address, val))
    pwm_led.duty_u16(val[0])

# register that callback for a specific register
register_definitions['HREGS']['EXAMPLE_HREG']['on_set_cb'] = my_holding_register_set_cb

# ... other code

client.setup_registers(registers=register_definitions)

With those callbacks at hand you could also setup the pins via register callbacks or the duty Cycle. So many new ideas in my head ...

Regarding funding. Danke für die Blumen und Grüße nach Belgien. I'm happier to see others using, contributing, staring this lib (and my other repos) than having one more coffee

MartiMan79 commented 1 year ago

Got it all working, awesome guys! Well this was worth more than just a coffee! So thanks, really! Now I can dim the whole house by Siri commands and HMI 🤣 (ioBroker on RPi 4B)

Thought I'd share the solution here...

RPi Pico with WIZnet ethernet HAT 5100s as 10ch dimmer over Modbus TCP

Download the latest firmware (I used v1.19.1 (2022-06-18) .uf2 from https://micropython.org/download/W5100S_EVB_PICO/

ModbusTCP_Server.py

#!/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, PWM

import network
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')

    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__))

pwm1 = None

def PWM_init():
    global pwm1, pwm2, pwm3, pwm4, pwm5, pwm6, pwm7, pwm8, pwm9, pwm10

    pwm1 = PWM(Pin(6)) # Attach PWM object    
    pwm2 = PWM(Pin(7)) # Attach PWM object
    pwm3 = PWM(Pin(8)) # Attach PWM object
    pwm4 = PWM(Pin(9)) # Attach PWM object
    pwm5 = PWM(Pin(10)) # Attach PWM object
    pwm6 = PWM(Pin(11)) # Attach PWM object
    pwm7 = PWM(Pin(12)) # Attach PWM object
    pwm8 = PWM(Pin(13)) # Attach PWM object
    pwm9 = PWM(Pin(14)) # Attach PWM object
    pwm10 = PWM(Pin(15)) # Attach PWM object

    # Settings
    #pwm1.freq(1000) #Standard is 1000hZ

# ===============================================
# connect to a network
w5x00_init()
PWM_init()
# ===============================================
# TCP Slave setup
tcp_port = 502              # port to listen to

local_ip = '192.168.0.210'

client = ModbusTCP()
is_bound = False

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))

    pwm1.duty_u16(int (val[0] * 65534/100))
    pwm2.duty_u16(int (val[1] * 65534/100))
    pwm3.duty_u16(int (val[2] * 65534/100))
    pwm4.duty_u16(int (val[3] * 65534/100))
    pwm5.duty_u16(int (val[4] * 65534/100))

    pwm6.duty_u16(int (val[5] * 65534/100))
    pwm7.duty_u16(int (val[6] * 65534/100))
    pwm8.duty_u16(int (val[7] * 65534/100))
    pwm9.duty_u16(int (val[8] * 65534/100))
    pwm10.duty_u16(int (val[9] * 65534/100))

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

   new_val = val[0] + 1

    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):
    global client
    global register_definitions

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

with open('registers/example.json', 'r') as file:
        register_definitions = json.load(file)

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 ...')
client.setup_registers(registers=register_definitions)
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")

example.json

{
        "HREGS": {
        "EXAMPLE_HREG": {
            "register": 0,
            "len": 10,
            "val": [50, 50, 50, 50, 50, 50, 50, 50, 50, 50],
            "description": "Example HREGS register, Hregs (setter+getter) [0, 65535]",
            "range": "",
            "unit": ""
        }
    }
}