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

144 lines
5.1 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
creation date: 2024-08-22
modification date: Thursday 22nd August 2024 09:03:26
tags:
---
# 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 `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
```python
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
```python
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.