Obsidian/.archive/3-99 Research/7 BeagleBone/2 pymodbus On the BeagleBone.md

5.1 KiB
Executable File
Raw Permalink Blame History

creation date modification date tags
2024-08-22 Thursday 22nd August 2024 09:03:26

Preamble

Setting up a virtual python environment (venv)

What are we doing?

Doing the beginning pymodbus tutorial on the bone

Why are we doing it?

To get it communicating with ARCADE

Who cares?

Me DGC, RL

First, pymodbus tutorial.

I'm going to try to do the pymodbus tutorial just communicating over ethernet. Try to set up a client and a server, and then I'll put one of those on the beaglebone.

A brief detour: Setting up Neovim for Python

Got this done locally! Here are some notes from ChatGPT working through with it.

Client-Server Communication with pymodbus:

  • Objective: Set up a Modbus client-server communication where the client sends a sine wave value, and the server multiplies it by 100 before sending it back.

Server:

  • Registers Setup: The server uses a ModbusSequentialDataBlock to hold the values written by the client.
  • Context: The context in pymodbus acts as a structure to hold different blocks of Modbus memory, such as holding registers, input registers, etc.
  • Port Binding: Originally used port 502 (privileged port), but switched to port 5020 to avoid permission issues when running without admin/root privileges.
  • Issue Fix: Run with sudo for privileged ports or switch to non-privileged ports (above 1024) like 5020 for development.

Client:

  • Sine Wave Generation: The client calculates a sine wave based on the current timestamp and writes it to the Modbus server.
  • Scaling Issue: The sine wave was initially scaled by 10,000, resulting in out-of-range values for 16-bit registers (valid range: 065535).
  • Solution 1 (Clipping): Clamp the sine wave to ensure values remain within 065535 to avoid struct errors.
  • Solution 2 (Shift & Scale): Shift the sine wave by adding 1 to make all values positive, and then scale the sine wave to fit the 065535 range.

Key Notes:

  • Addressing Error: The struct.error occurred because the sine wave value multiplied by 10000 was out of the valid range for a 16-bit unsigned integer.
  • Fix: Use either clipping or a shift-and-scale approach to keep the sine wave within the Modbus register's valid range.
  • Read Registers: The Modbus read_holding_registers() function requires integer arguments for the address and count, with an optional slave ID.
  • Structure: Always check that values passed to Modbus functions (like write_register) are within the range expected by the protocol (e.g., 0 to 65535 for 16-bit registers).

Let me know if you'd like to expand on any points!

Here are the scripts

Synchronous TCP Client

import asyncio
import numpy as np
import time
import pymodbus.client as ModbusClient
from pymodbus import (
        ExceptionResponse,
        FramerType,
        ModbusException,
        pymodbus_apply_logging_config
)

# GLOBAL VARIABLES
client = "127.0.0.1" #wherever the client lives
server = "127.0.0.1" # wherever the beaglebone lives

#pymodbus_apply_logging_config("DEBUG")
print("Setting up...")
client = ModbusClient.ModbusTcpClient(
    server,
    port = 5020
)

print("Connecting to server...")
client.connect()
assert client.connected


i=0

while True:
    print("                      ")
    print("______________________")
    print("NEW LOOP!")

    # Create some sine wave value
    sine_value = np.sin(i)+1
    print("Current sin() value is: ", sine_value)
    i+=0.1

    # Write the value to the server
    print("Writing to slave...")
    client.write_register(0, int(sine_value * 10000), 1)
    print("done!")
    
    #time.sleep(1)

    # Read value back from the server
    print("Reading register from server...")
    response = client.read_holding_registers(0, 1)
    if response.isError():
        print("Error reading from the server!")
    else:
        modified_value = response.registers[0]
        print("Response from the server: ", modified_value/10000)

    time.sleep(1)

Synchronous Server Client

from pymodbus.server import StartTcpServer
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.datastore import ModbusSequentialDataBlock

import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

# GLOBAL VARIABLES
client_ip = "127.0.0.1" 
server_ip = "127.0.0.1"
port = 5020

# Setup the datastore
store = ModbusSlaveContext(
    di=ModbusSequentialDataBlock(0, [0]*100), #direct inputs (sensor statuses...)
    co=ModbusSequentialDataBlock(0, [0]*100), #coils (think of relay positions, interal logic)
    hr=ModbusSequentialDataBlock(0, [0]*100), #holding registers (internal memory)
    ir=ModbusSequentialDataBlock(0, [0]*100)) #input registers (sensor values...)
context = ModbusServerContext(slaves=store, single=True)

# Start the server
def run_server():
    StartTcpServer(context=context, address=(client_ip,port))

if __name__ == "__main__":
    run_server()

Putting it on the BeagleBone

I'm putting the server on the bone Need to get virtual environment set up on the bone.... This is too annoying to conquer today.