Compare commits
3 Commits
2b4890f052
...
a5da0a61dd
| Author | SHA1 | Date |
|---|---|---|
|
|
a5da0a61dd | |
|
|
b54c73edb1 | |
|
|
2418d4e218 |
|
|
@ -9,47 +9,6 @@
|
||||||
|
|
||||||
LOG_MODULE_REGISTER(modbus_server, LOG_LEVEL_INF);
|
LOG_MODULE_REGISTER(modbus_server, LOG_LEVEL_INF);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Modbus Input Register Addresses.
|
|
||||||
*/
|
|
||||||
enum {
|
|
||||||
/* Valve Control & Status */
|
|
||||||
REG_INPUT_VALVE_STATE_MOVEMENT = 0x0000,
|
|
||||||
REG_INPUT_MOTOR_CURRENT_MA = 0x0001,
|
|
||||||
/* Digital Inputs */
|
|
||||||
REG_INPUT_DIGITAL_INPUTS_STATE = 0x0020,
|
|
||||||
REG_INPUT_BUTTON_EVENTS = 0x0021,
|
|
||||||
/* System Config & Status */
|
|
||||||
REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR = 0x00F0,
|
|
||||||
REG_INPUT_FIRMWARE_VERSION_PATCH = 0x00F1,
|
|
||||||
REG_INPUT_DEVICE_STATUS = 0x00F2,
|
|
||||||
REG_INPUT_UPTIME_SECONDS_LOW = 0x00F3,
|
|
||||||
REG_INPUT_UPTIME_SECONDS_HIGH = 0x00F4,
|
|
||||||
/* Firmware Update */
|
|
||||||
REG_INPUT_FWU_LAST_CHUNK_CRC = 0x0100,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Modbus Holding Register Addresses.
|
|
||||||
*/
|
|
||||||
enum {
|
|
||||||
/* Valve Control */
|
|
||||||
REG_HOLDING_VALVE_COMMAND = 0x0000,
|
|
||||||
REG_HOLDING_MAX_OPENING_TIME_S = 0x0001,
|
|
||||||
REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002,
|
|
||||||
/* Digital Outputs */
|
|
||||||
REG_HOLDING_DIGITAL_OUTPUTS_STATE = 0x0010,
|
|
||||||
/* System Config */
|
|
||||||
REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0,
|
|
||||||
/* Firmware Update */
|
|
||||||
REG_HOLDING_FWU_COMMAND = 0x0100,
|
|
||||||
REG_HOLDING_FWU_CHUNK_OFFSET_LOW = 0x0101,
|
|
||||||
REG_HOLDING_FWU_CHUNK_OFFSET_HIGH = 0x0102,
|
|
||||||
REG_HOLDING_FWU_CHUNK_SIZE = 0x0103,
|
|
||||||
REG_HOLDING_FWU_DATA_BUFFER = 0x0180,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int modbus_iface;
|
static int modbus_iface;
|
||||||
static struct modbus_iface_param server_param = {
|
static struct modbus_iface_param server_param = {
|
||||||
.mode = MODBUS_MODE_RTU,
|
.mode = MODBUS_MODE_RTU,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,47 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modbus Input Register Addresses.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
/* Valve Control & Status */
|
||||||
|
REG_INPUT_VALVE_STATE_MOVEMENT = 0x0000,
|
||||||
|
REG_INPUT_MOTOR_CURRENT_MA = 0x0001,
|
||||||
|
/* Digital Inputs */
|
||||||
|
REG_INPUT_DIGITAL_INPUTS_STATE = 0x0020,
|
||||||
|
REG_INPUT_BUTTON_EVENTS = 0x0021,
|
||||||
|
/* System Config & Status */
|
||||||
|
REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR = 0x00F0,
|
||||||
|
REG_INPUT_FIRMWARE_VERSION_PATCH = 0x00F1,
|
||||||
|
REG_INPUT_DEVICE_STATUS = 0x00F2,
|
||||||
|
REG_INPUT_UPTIME_SECONDS_LOW = 0x00F3,
|
||||||
|
REG_INPUT_UPTIME_SECONDS_HIGH = 0x00F4,
|
||||||
|
/* Firmware Update */
|
||||||
|
REG_INPUT_FWU_LAST_CHUNK_CRC = 0x0100,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modbus Holding Register Addresses.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
/* Valve Control */
|
||||||
|
REG_HOLDING_VALVE_COMMAND = 0x0000,
|
||||||
|
REG_HOLDING_MAX_OPENING_TIME_S = 0x0001,
|
||||||
|
REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002,
|
||||||
|
/* Digital Outputs */
|
||||||
|
REG_HOLDING_DIGITAL_OUTPUTS_STATE = 0x0010,
|
||||||
|
/* System Config */
|
||||||
|
REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0,
|
||||||
|
REG_HOLDING_DEVICE_RESET = 0x00F1,
|
||||||
|
/* Firmware Update */
|
||||||
|
REG_HOLDING_FWU_COMMAND = 0x0100,
|
||||||
|
REG_HOLDING_FWU_CHUNK_OFFSET_LOW = 0x0101,
|
||||||
|
REG_HOLDING_FWU_CHUNK_OFFSET_HIGH = 0x0102,
|
||||||
|
REG_HOLDING_FWU_CHUNK_SIZE = 0x0103,
|
||||||
|
REG_HOLDING_FWU_DATA_BUFFER = 0x0180,
|
||||||
|
};
|
||||||
|
|
||||||
int modbus_server_init(void);
|
int modbus_server_init(void);
|
||||||
int modbus_reconfigure(uint32_t baudrate, uint8_t unit_id);
|
int modbus_reconfigure(uint32_t baudrate, uint8_t unit_id);
|
||||||
uint32_t modbus_get_baudrate(void);
|
uint32_t modbus_get_baudrate(void);
|
||||||
|
|
|
||||||
|
|
@ -54,32 +54,73 @@ def format_uptime(seconds):
|
||||||
|
|
||||||
def poll_status(slave_id, interval):
|
def poll_status(slave_id, interval):
|
||||||
global status_data
|
global status_data
|
||||||
|
reconnect_attempts = 0
|
||||||
|
max_reconnect_attempts = 5
|
||||||
|
reconnect_delay = 1 # seconds
|
||||||
|
|
||||||
while not stop_event.is_set():
|
while not stop_event.is_set():
|
||||||
if update_status["running"]: time.sleep(interval); continue
|
if update_status["running"]:
|
||||||
new_data = {"error": None}
|
time.sleep(interval)
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_data = {}
|
||||||
try:
|
try:
|
||||||
|
if not client.is_connected():
|
||||||
|
reconnect_attempts += 1
|
||||||
|
if reconnect_attempts >= max_reconnect_attempts:
|
||||||
|
new_data["error"] = f"Failed to reconnect after {max_reconnect_attempts} attempts. Exiting."
|
||||||
|
stop_event.set()
|
||||||
|
break
|
||||||
|
|
||||||
|
# Attempt to connect
|
||||||
|
if client.connect():
|
||||||
|
reconnect_attempts = 0
|
||||||
|
new_data["error"] = None # Clear error on successful reconnect
|
||||||
|
else:
|
||||||
|
new_data["error"] = f"Connection lost. Attempting to reconnect ({reconnect_attempts}/{max_reconnect_attempts})..."
|
||||||
|
time.sleep(reconnect_delay)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If connected, try to read data
|
||||||
ir_valve = client.read_input_registers(REG_INPUT_VALVE_STATE_MOVEMENT, count=2, slave=slave_id)
|
ir_valve = client.read_input_registers(REG_INPUT_VALVE_STATE_MOVEMENT, count=2, slave=slave_id)
|
||||||
ir_dig = client.read_input_registers(REG_INPUT_DIGITAL_INPUTS_STATE, count=2, slave=slave_id)
|
ir_dig = client.read_input_registers(REG_INPUT_DIGITAL_INPUTS_STATE, count=2, slave=slave_id)
|
||||||
ir_sys = client.read_input_registers(REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR, count=5, slave=slave_id)
|
ir_sys = client.read_input_registers(REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR, count=5, slave=slave_id)
|
||||||
hr_valve = client.read_holding_registers(REG_HOLDING_MAX_OPENING_TIME_S, count=2, slave=slave_id)
|
hr_valve = client.read_holding_registers(REG_HOLDING_MAX_OPENING_TIME_S, count=2, slave=slave_id)
|
||||||
hr_dig = client.read_holding_registers(REG_HOLDING_DIGITAL_OUTPUTS_STATE, count=1, slave=slave_id)
|
hr_dig = client.read_holding_registers(REG_HOLDING_DIGITAL_OUTPUTS_STATE, count=1, slave=slave_id)
|
||||||
hr_sys = client.read_holding_registers(REG_HOLDING_WATCHDOG_TIMEOUT_S, count=1, slave=slave_id)
|
hr_sys = client.read_holding_registers(REG_HOLDING_WATCHDOG_TIMEOUT_S, count=1, slave=slave_id)
|
||||||
|
|
||||||
for res in [ir_valve, ir_dig, ir_sys, hr_valve, hr_dig, hr_sys]:
|
for res in [ir_valve, ir_dig, ir_sys, hr_valve, hr_dig, hr_sys]:
|
||||||
if res.isError(): raise ModbusException(str(res))
|
if res.isError():
|
||||||
|
raise ModbusException(str(res))
|
||||||
|
|
||||||
valve_state_raw = ir_valve.registers[0]
|
valve_state_raw = ir_valve.registers[0]
|
||||||
movement_map = {0: "Idle", 1: "Opening", 2: "Closing", 3: "Error"}; state_map = {0: "Closed", 1: "Open"}
|
movement_map = {0: "Idle", 1: "Opening", 2: "Closing", 3: "Error"}
|
||||||
new_data["movement"] = movement_map.get(valve_state_raw >> 8, 'Unknown'); new_data["state"] = state_map.get(valve_state_raw & 0xFF, 'Unknown')
|
state_map = {0: "Closed", 1: "Open"}
|
||||||
new_data["motor_current"] = f"{ir_valve.registers[1]} mA"; new_data["open_time"] = f"{hr_valve.registers[0]}s"; new_data["close_time"] = f"{hr_valve.registers[1]}s"
|
new_data["movement"] = movement_map.get(valve_state_raw >> 8, 'Unknown')
|
||||||
new_data["digital_inputs"] = f"0x{ir_dig.registers[0]:04X}"; new_data["button_events"] = f"0x{ir_dig.registers[1]:04X}"; new_data["digital_outputs"] = f"0x{hr_dig.registers[0]:04X}"
|
new_data["state"] = state_map.get(valve_state_raw & 0xFF, 'Unknown')
|
||||||
|
new_data["motor_current"] = f"{ir_valve.registers[1]} mA"
|
||||||
|
new_data["open_time"] = f"{hr_valve.registers[0]}s"
|
||||||
|
new_data["close_time"] = f"{hr_valve.registers[1]}s"
|
||||||
|
new_data["digital_inputs"] = f"0x{ir_dig.registers[0]:04X}"
|
||||||
|
new_data["button_events"] = f"0x{ir_dig.registers[1]:04X}"
|
||||||
|
new_data["digital_outputs"] = f"0x{hr_dig.registers[0]:04X}"
|
||||||
|
|
||||||
fw_major = ir_sys.registers[0] >> 8; fw_minor = ir_sys.registers[0] & 0xFF; fw_patch = ir_sys.registers[1]
|
fw_major = ir_sys.registers[0] >> 8
|
||||||
|
fw_minor = ir_sys.registers[0] & 0xFF
|
||||||
|
fw_patch = ir_sys.registers[1]
|
||||||
uptime_seconds = (ir_sys.registers[4] << 16) | ir_sys.registers[3]
|
uptime_seconds = (ir_sys.registers[4] << 16) | ir_sys.registers[3]
|
||||||
new_data["firmware"] = f"v{fw_major}.{fw_minor}.{fw_patch}"; new_data["device_status"] = "OK" if ir_sys.registers[2] == 0 else "ERROR"
|
new_data["firmware"] = f"v{fw_major}.{fw_minor}.{fw_patch}"
|
||||||
new_data["uptime"] = format_uptime(uptime_seconds); new_data["watchdog"] = f"{hr_sys.registers[0]}s"
|
new_data["device_status"] = "OK" if ir_sys.registers[2] == 0 else "ERROR"
|
||||||
|
new_data["uptime"] = format_uptime(uptime_seconds)
|
||||||
|
new_data["watchdog"] = f"{hr_sys.registers[0]}s"
|
||||||
|
new_data["error"] = None # Clear any previous error on successful read
|
||||||
|
reconnect_attempts = 0 # Reset attempts on successful communication
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
new_data["error"] = f"Error: {e}"
|
new_data["error"] = f"Communication Error: {e}. Closing connection."
|
||||||
with status_lock: status_data = new_data
|
client.close() # Close connection to force reconnect attempt in next loop
|
||||||
|
finally:
|
||||||
|
with status_lock:
|
||||||
|
status_data = new_data
|
||||||
time.sleep(interval)
|
time.sleep(interval)
|
||||||
|
|
||||||
def firmware_update_thread(slave_id, filepath):
|
def firmware_update_thread(slave_id, filepath):
|
||||||
|
|
@ -225,11 +266,11 @@ def main_menu(stdscr, slave_id):
|
||||||
elif selected_option == "Set Watchdog":
|
elif selected_option == "Set Watchdog":
|
||||||
input_mode, input_prompt, input_target_reg = True, "Enter Watchdog Timeout (s): ", REG_HOLDING_WATCHDOG_TIMEOUT_S
|
input_mode, input_prompt, input_target_reg = True, "Enter Watchdog Timeout (s): ", REG_HOLDING_WATCHDOG_TIMEOUT_S
|
||||||
elif selected_option == "Reset Node":
|
elif selected_option == "Reset Node":
|
||||||
|
try:
|
||||||
client.write_register(REG_HOLDING_DEVICE_RESET, 1, slave=slave_id)
|
client.write_register(REG_HOLDING_DEVICE_RESET, 1, slave=slave_id)
|
||||||
message = "-> Sent RESET command"
|
message = "-> Sent RESET command. Node should reboot."
|
||||||
elif selected_option == "Firmware Update":
|
except Exception as e:
|
||||||
client.write_register(REG_HOLDING_DEVICE_RESET, 1, slave=slave_id)
|
message = f"-> Error sending reset: {e}"
|
||||||
message = "-> Sent RESET command"
|
|
||||||
elif selected_option == "Firmware Update":
|
elif selected_option == "Firmware Update":
|
||||||
filepath = file_browser(stdscr)
|
filepath = file_browser(stdscr)
|
||||||
if filepath:
|
if filepath:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue