5.1 KiB
Executable File
5.1 KiB
Executable File
| creation date | modification date | tags |
|---|---|---|
| 2024-08-22 | Thursday 22nd August 2024 09:03:26 |
Preamble
Notable Links
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
ModbusSequentialDataBlockto hold the values written by the client. - Context: The
contextin 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 port5020to avoid permission issues when running without admin/root privileges. - Issue Fix: Run with
sudofor privileged ports or switch to non-privileged ports (above 1024) like5020for 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: 0–65535).
- Solution 1 (Clipping): Clamp the sine wave to ensure values remain within
0–65535to avoid struct errors. - Solution 2 (Shift & Scale): Shift the sine wave by adding
1to make all values positive, and then scale the sine wave to fit the0–65535range.
Key Notes:
- Addressing Error: The
struct.erroroccurred because the sine wave value multiplied by10000was 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.