From 269e9e88a15b80f5e988a95b9c4de1ea077c6e56 Mon Sep 17 00:00:00 2001 From: Eduard Iten Date: Tue, 1 Jul 2025 18:15:54 +0200 Subject: [PATCH] feat(valve): Implement safe virtual valve control - Implement virtual valve logic with time-based movement simulation. - The valve state is now set to 'OPEN' immediately when the opening process starts, ensuring a safe and correct state representation. - The state is only set to 'CLOSED' after the closing process has finished. - Add persistence for max opening and closing times. --- software/apps/slave_node/src/main.c | 100 +++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/software/apps/slave_node/src/main.c b/software/apps/slave_node/src/main.c index b8045e6..b088e99 100644 --- a/software/apps/slave_node/src/main.c +++ b/software/apps/slave_node/src/main.c @@ -22,6 +22,8 @@ LOG_MODULE_REGISTER(mbs_sample, LOG_LEVEL_INF); #define APP_VERSION_PATCH 0 enum { + REG_INPUT_VALVE_STATE_MOVEMENT = 0x0000, + REG_INPUT_MOTOR_CURRENT_MA = 0x0001, REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR = 0x00F0, REG_INPUT_FIRMWARE_VERSION_PATCH = 0x00F1, REG_INPUT_DEVICE_STATUS = 0x00F2, @@ -30,13 +32,33 @@ enum { }; enum { - REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0, + REG_HOLDING_VALVE_COMMAND = 0x0000, + REG_HOLDING_MAX_OPENING_TIME_S = 0x0001, + REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002, + REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0, }; +enum valve_state { + VALVE_STATE_CLOSED, + VALVE_STATE_OPEN, +}; +enum valve_movement { + VALVE_MOVEMENT_IDLE, + VALVE_MOVEMENT_OPENING, + VALVE_MOVEMENT_CLOSING, + VALVE_MOVEMENT_ERROR, +}; + +static enum valve_state current_state = VALVE_STATE_CLOSED; +static enum valve_movement current_movement = VALVE_MOVEMENT_IDLE; +static uint16_t max_opening_time_s = 60; +static uint16_t max_closing_time_s = 60; static uint16_t watchdog_timeout_s; static int modbus_iface; +static struct k_work_delayable valve_work; + static struct modbus_iface_param server_param = { .mode = MODBUS_MODE_RTU, .server = { @@ -49,6 +71,17 @@ static struct modbus_iface_param server_param = { }, }; +static void valve_work_handler(struct k_work *work) +{ + if (current_movement == VALVE_MOVEMENT_OPENING) { + LOG_INF("Virtual valve finished opening"); + } else if (current_movement == VALVE_MOVEMENT_CLOSING) { + current_state = VALVE_STATE_CLOSED; + LOG_INF("Virtual valve finished closing"); + } + current_movement = VALVE_MOVEMENT_IDLE; +} + static int coil_rd(uint16_t addr, bool *state) { @@ -66,6 +99,12 @@ static int coil_wr(uint16_t addr, bool state) static int holding_reg_rd(uint16_t addr, uint16_t *reg) { switch (addr) { + case REG_HOLDING_MAX_OPENING_TIME_S: + *reg = max_opening_time_s; + break; + case REG_HOLDING_MAX_CLOSING_TIME_S: + *reg = max_closing_time_s; + break; case REG_HOLDING_WATCHDOG_TIMEOUT_S: *reg = watchdog_timeout_s; break; @@ -81,6 +120,34 @@ static int holding_reg_rd(uint16_t addr, uint16_t *reg) static int holding_reg_wr(uint16_t addr, uint16_t reg) { switch (addr) { + case REG_HOLDING_VALVE_COMMAND: + if (reg == 1) { /* Open */ + if (current_state == VALVE_STATE_CLOSED) { + current_state = VALVE_STATE_OPEN; + current_movement = VALVE_MOVEMENT_OPENING; + LOG_INF("Virtual valve opening..."); + k_work_schedule(&valve_work, K_MSEC(max_opening_time_s * 1000 * 0.9)); + } + } else if (reg == 2) { /* Close */ + if (current_state == VALVE_STATE_OPEN) { + current_movement = VALVE_MOVEMENT_CLOSING; + LOG_INF("Virtual valve closing..."); + k_work_schedule(&valve_work, K_MSEC(max_closing_time_s * 1000 * 0.9)); + } + } else if (reg == 0) { /* Stop */ + k_work_cancel_delayable(&valve_work); + current_movement = VALVE_MOVEMENT_IDLE; + LOG_INF("Virtual valve movement stopped"); + } + break; + case REG_HOLDING_MAX_OPENING_TIME_S: + max_opening_time_s = reg; + settings_save_one("valve/max_open_time", &max_opening_time_s, sizeof(max_opening_time_s)); + break; + case REG_HOLDING_MAX_CLOSING_TIME_S: + max_closing_time_s = reg; + settings_save_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s)); + break; case REG_HOLDING_WATCHDOG_TIMEOUT_S: watchdog_timeout_s = reg; break; @@ -97,6 +164,12 @@ static int input_reg_rd(uint16_t addr, uint16_t *reg) uint32_t uptime_s = k_uptime_get_32() / 1000; switch (addr) { + case REG_INPUT_VALVE_STATE_MOVEMENT: + *reg = (current_movement << 8) | (current_state & 0xFF); + break; + case REG_INPUT_MOTOR_CURRENT_MA: + *reg = 50; /* Dummy value */ + break; case REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR: *reg = (APP_VERSION_MAJOR << 8) | (APP_VERSION_MINOR & 0xFF); break; @@ -165,7 +238,7 @@ uint8_t modbus_get_unit_id(void) return server_param.server.unit_id; } -static int settings_modbus_load(const char *name, size_t len, +static int settings_load_cb(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { const char *next; @@ -189,10 +262,29 @@ static int settings_modbus_load(const char *name, size_t len, return 0; } + if (settings_name_steq(name, "max_open_time", &next) && !next) { + rc = read_cb(cb_arg, &max_opening_time_s, sizeof(max_opening_time_s)); + if (rc < 0) { + return rc; + } + LOG_INF("Loaded valve/max_open_time: %u", max_opening_time_s); + return 0; + } + + if (settings_name_steq(name, "max_close_time", &next) && !next) { + rc = read_cb(cb_arg, &max_closing_time_s, sizeof(max_closing_time_s)); + if (rc < 0) { + return rc; + } + LOG_INF("Loaded valve/max_close_time: %u", max_closing_time_s); + return 0; + } + return -ENOENT; } -SETTINGS_STATIC_HANDLER_DEFINE(modbus, "modbus", NULL, settings_modbus_load, NULL, NULL); +SETTINGS_STATIC_HANDLER_DEFINE(modbus, "modbus", NULL, settings_load_cb, NULL, NULL); +SETTINGS_STATIC_HANDLER_DEFINE(valve, "valve", NULL, settings_load_cb, NULL, NULL); static int init_modbus_server(void) @@ -214,6 +306,8 @@ int main(void) { LOG_INF("Starting APP"); + k_work_init_delayable(&valve_work, valve_work_handler); + if (settings_subsys_init()) { LOG_ERR("Failed to initialize settings subsystem"); }