Compare commits

...

3 Commits

3 changed files with 100 additions and 59 deletions

View File

@ -9,47 +9,6 @@
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 struct modbus_iface_param server_param = {
.mode = MODBUS_MODE_RTU,

View File

@ -3,6 +3,47 @@
#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_reconfigure(uint32_t baudrate, uint8_t unit_id);
uint32_t modbus_get_baudrate(void);

View File

@ -54,33 +54,74 @@ def format_uptime(seconds):
def poll_status(slave_id, interval):
global status_data
reconnect_attempts = 0
max_reconnect_attempts = 5
reconnect_delay = 1 # seconds
while not stop_event.is_set():
if update_status["running"]: time.sleep(interval); continue
new_data = {"error": None}
if update_status["running"]:
time.sleep(interval)
continue
new_data = {}
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_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)
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_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]:
if res.isError(): raise ModbusException(str(res))
if res.isError():
raise ModbusException(str(res))
valve_state_raw = ir_valve.registers[0]
movement_map = {0: "Idle", 1: "Opening", 2: "Closing", 3: "Error"}; state_map = {0: "Closed", 1: "Open"}
new_data["movement"] = movement_map.get(valve_state_raw >> 8, 'Unknown'); 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}"
movement_map = {0: "Idle", 1: "Opening", 2: "Closing", 3: "Error"}
state_map = {0: "Closed", 1: "Open"}
new_data["movement"] = movement_map.get(valve_state_raw >> 8, 'Unknown')
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]
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["uptime"] = format_uptime(uptime_seconds); new_data["watchdog"] = f"{hr_sys.registers[0]}s"
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["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:
new_data["error"] = f"Error: {e}"
with status_lock: status_data = new_data
time.sleep(interval)
new_data["error"] = f"Communication Error: {e}. Closing connection."
client.close() # Close connection to force reconnect attempt in next loop
finally:
with status_lock:
status_data = new_data
time.sleep(interval)
def firmware_update_thread(slave_id, filepath):
global update_status
@ -225,11 +266,11 @@ def main_menu(stdscr, slave_id):
elif selected_option == "Set Watchdog":
input_mode, input_prompt, input_target_reg = True, "Enter Watchdog Timeout (s): ", REG_HOLDING_WATCHDOG_TIMEOUT_S
elif selected_option == "Reset Node":
client.write_register(REG_HOLDING_DEVICE_RESET, 1, slave=slave_id)
message = "-> Sent RESET command"
elif selected_option == "Firmware Update":
client.write_register(REG_HOLDING_DEVICE_RESET, 1, slave=slave_id)
message = "-> Sent RESET command"
try:
client.write_register(REG_HOLDING_DEVICE_RESET, 1, slave=slave_id)
message = "-> Sent RESET command. Node should reboot."
except Exception as e:
message = f"-> Error sending reset: {e}"
elif selected_option == "Firmware Update":
filepath = file_browser(stdscr)
if filepath: