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.
This commit is contained in:
Eduard Iten 2025-07-01 18:15:54 +02:00
parent 8cab3eecc1
commit 269e9e88a1
1 changed files with 97 additions and 3 deletions

View File

@ -22,6 +22,8 @@ LOG_MODULE_REGISTER(mbs_sample, LOG_LEVEL_INF);
#define APP_VERSION_PATCH 0 #define APP_VERSION_PATCH 0
enum { enum {
REG_INPUT_VALVE_STATE_MOVEMENT = 0x0000,
REG_INPUT_MOTOR_CURRENT_MA = 0x0001,
REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR = 0x00F0, REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR = 0x00F0,
REG_INPUT_FIRMWARE_VERSION_PATCH = 0x00F1, REG_INPUT_FIRMWARE_VERSION_PATCH = 0x00F1,
REG_INPUT_DEVICE_STATUS = 0x00F2, REG_INPUT_DEVICE_STATUS = 0x00F2,
@ -30,13 +32,33 @@ enum {
}; };
enum { enum {
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, 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 uint16_t watchdog_timeout_s;
static int modbus_iface; static int modbus_iface;
static struct k_work_delayable valve_work;
static struct modbus_iface_param server_param = { static struct modbus_iface_param server_param = {
.mode = MODBUS_MODE_RTU, .mode = MODBUS_MODE_RTU,
.server = { .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) 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) static int holding_reg_rd(uint16_t addr, uint16_t *reg)
{ {
switch (addr) { 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: case REG_HOLDING_WATCHDOG_TIMEOUT_S:
*reg = watchdog_timeout_s; *reg = watchdog_timeout_s;
break; 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) static int holding_reg_wr(uint16_t addr, uint16_t reg)
{ {
switch (addr) { 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: case REG_HOLDING_WATCHDOG_TIMEOUT_S:
watchdog_timeout_s = reg; watchdog_timeout_s = reg;
break; 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; uint32_t uptime_s = k_uptime_get_32() / 1000;
switch (addr) { 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: case REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR:
*reg = (APP_VERSION_MAJOR << 8) | (APP_VERSION_MINOR & 0xFF); *reg = (APP_VERSION_MAJOR << 8) | (APP_VERSION_MINOR & 0xFF);
break; break;
@ -165,7 +238,7 @@ uint8_t modbus_get_unit_id(void)
return server_param.server.unit_id; 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) settings_read_cb read_cb, void *cb_arg)
{ {
const char *next; const char *next;
@ -189,10 +262,29 @@ static int settings_modbus_load(const char *name, size_t len,
return 0; 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; 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) static int init_modbus_server(void)
@ -214,6 +306,8 @@ int main(void)
{ {
LOG_INF("Starting APP"); LOG_INF("Starting APP");
k_work_init_delayable(&valve_work, valve_work_handler);
if (settings_subsys_init()) { if (settings_subsys_init()) {
LOG_ERR("Failed to initialize settings subsystem"); LOG_ERR("Failed to initialize settings subsystem");
} }