diff --git a/software/lib/valve/valve.c b/software/lib/valve/valve.c index ac9523c..ae5e3ea 100644 --- a/software/lib/valve/valve.c +++ b/software/lib/valve/valve.c @@ -7,13 +7,13 @@ * safety timeouts for opening and closing operations. */ -#include #include #include #include #include #include #include +#include #define VND_NODE DT_ALIAS(vnd7050aj) #if !DT_NODE_HAS_STATUS(VND_NODE, okay) @@ -27,140 +27,170 @@ static enum valve_state current_state = VALVE_STATE_OPEN; static enum valve_movement current_movement = VALVE_MOVEMENT_IDLE; static uint16_t max_opening_time_s = 10; static uint16_t max_closing_time_s = 10; -static uint32_t movement_start_time = 0; -static struct k_work_delayable - valve_work; // Work item for scheduling valve movement timeouts +static struct k_work_delayable valve_work; // Work item for scheduling valve movement timeouts +static struct k_timer movement_timer; /** - * @brief Work handler for valve movement timeouts. + * @brief Work handler for end position checks of the valve. * - * This function is executed when the valve's movement timer expires. - * It stops the motor to prevent damage and updates the valve's state. + * This function is called periodically to check if the valve has reached its + * end position. It reads the current load on the motor and determines if the + * valve has reached its target position. * * @param work Pointer to the k_work item. */ -static void valve_work_handler(struct k_work *work) { - int current_ma = 0; - uint32_t now; - now = k_uptime_get_32(); - if (current_movement == VALVE_MOVEMENT_OPENING) { - if (now - movement_start_time > max_opening_time_s * 1000) { - LOG_WRN("Valve opening timeout reached, stopping motor."); - current_movement = VALVE_MOVEMENT_ERROR; - goto work_handler_finish; - } - vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_OPEN, ¤t_ma); - if (current_ma > 10) { - k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); - return; - } - LOG_INF("Valve finished opening"); - } else if (current_movement == VALVE_MOVEMENT_CLOSING) { - if (now - movement_start_time > max_closing_time_s * 1000) { - LOG_WRN("Valve closing timeout reached, stopping motor."); - current_movement = VALVE_MOVEMENT_ERROR; - goto work_handler_finish; - } - vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, - ¤t_ma); - if (current_ma > 10) { - k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); - return; - } - current_state = VALVE_STATE_CLOSED; - LOG_INF("Valve finished closing"); - } - current_movement = VALVE_MOVEMENT_IDLE; -work_handler_finish: - vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, false); - vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, false); - LOG_INF("Valve work handler finished. Current state: %d, Movement: %d", - current_state, current_movement); +static void valve_work_handler(struct k_work *work) +{ + int current_ma = 0; + + if (current_movement == VALVE_MOVEMENT_OPENING) { + vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_OPEN, ¤t_ma); + if (current_ma > 10) { + k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); + return; + } + LOG_INF("Valve finished opening"); + } else if (current_movement == VALVE_MOVEMENT_CLOSING) { + vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, ¤t_ma); + if (current_ma > 10) { + k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); + return; + } + current_state = VALVE_STATE_CLOSED; + LOG_INF("Valve finished closing"); + } + current_movement = VALVE_MOVEMENT_IDLE; + + // Reset the movement timer + k_timer_stop(&movement_timer); + + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, false); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, false); } -int valve_init(void) { - if (!device_is_ready(vnd7050aj_dev)) { - LOG_ERR("VND7050AJ device is not ready"); - return -ENODEV; - } - k_work_init_delayable(&valve_work, valve_work_handler); - settings_load_one("valve/max_open_time", &max_opening_time_s, - sizeof(max_opening_time_s)); - settings_load_one("valve/max_close_time", &max_closing_time_s, - sizeof(max_closing_time_s)); - - LOG_INF("Valve initialized: max_open=%us, max_close=%us", max_opening_time_s, - max_closing_time_s); - valve_close(); - return 0; +void movement_timeout_handler(struct k_timer *timer) +{ + // Stop the end position check if the timer expires + k_work_cancel_delayable(&valve_work); + if (current_movement == VALVE_MOVEMENT_OPENING) { + LOG_WRN("Valve opening timeout reached, stopping motor."); + current_movement = VALVE_MOVEMENT_ERROR; + } else if (current_movement == VALVE_MOVEMENT_CLOSING) { + LOG_WRN("Valve closing timeout reached, stopping motor."); + current_movement = VALVE_MOVEMENT_ERROR; + } + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, false); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, false); + current_state = VALVE_STATE_CLOSED; } -void valve_open(void) { - vnd7050aj_reset_fault(vnd7050aj_dev); - vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, false); - vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, true); - current_state = VALVE_STATE_OPEN; - current_movement = VALVE_MOVEMENT_OPENING; /* Security: assume valve open as - soons as it starts opening */ - movement_start_time = k_uptime_get_32(); - k_work_schedule(&valve_work, K_MSEC(100)); +int valve_init(void) +{ + if (!device_is_ready(vnd7050aj_dev)) { + LOG_ERR("VND7050AJ device is not ready"); + return -ENODEV; + } + + k_work_init_delayable(&valve_work, valve_work_handler); + k_timer_init(&movement_timer, movement_timeout_handler, NULL); + + settings_load_one("valve/max_open_time", &max_opening_time_s, sizeof(max_opening_time_s)); + settings_load_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s)); + + LOG_INF("Valve initialized: max_open=%us, max_close=%us", + max_opening_time_s, + max_closing_time_s); + valve_close(); + return 0; } -void valve_close(void) { - vnd7050aj_reset_fault(vnd7050aj_dev); - vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, false); - vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, true); - movement_start_time = k_uptime_get_32(); - current_movement = VALVE_MOVEMENT_CLOSING; - k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); +void valve_open(void) +{ + vnd7050aj_reset_fault(vnd7050aj_dev); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, false); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, true); + current_state = VALVE_STATE_OPEN; + current_movement = VALVE_MOVEMENT_OPENING; /* Security: assume valve open as + soons as it starts opening */ + k_timer_start(&movement_timer, K_SECONDS(max_opening_time_s), K_NO_WAIT); + k_work_schedule(&valve_work, K_MSEC(100)); } -void valve_stop(void) { - k_work_cancel_delayable(&valve_work); - vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, false); - vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, false); - current_movement = VALVE_MOVEMENT_IDLE; +void valve_close(void) +{ + vnd7050aj_reset_fault(vnd7050aj_dev); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, false); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, true); + k_timer_start(&movement_timer, K_SECONDS(max_closing_time_s), K_NO_WAIT); + current_movement = VALVE_MOVEMENT_CLOSING; + k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); } -enum valve_state valve_get_state(void) { return current_state; } -enum valve_movement valve_get_movement(void) { return current_movement; } -uint16_t valve_get_motor_current(void) { - return (current_movement != VALVE_MOVEMENT_IDLE) ? 150 : 10; +void valve_stop(void) +{ + k_work_cancel_delayable(&valve_work); + k_timer_stop(&movement_timer); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, false); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, false); + current_movement = VALVE_MOVEMENT_IDLE; } -void valve_set_max_open_time(uint16_t seconds) { - max_opening_time_s = seconds; - settings_save_one("valve/max_open_time", &max_opening_time_s, - sizeof(max_opening_time_s)); +enum valve_state valve_get_state(void) +{ + return current_state; } -void valve_set_max_close_time(uint16_t seconds) { - max_closing_time_s = seconds; - settings_save_one("valve/max_close_time", &max_closing_time_s, - sizeof(max_closing_time_s)); +enum valve_movement valve_get_movement(void) +{ + return current_movement; } -uint16_t valve_get_max_open_time(void) { return max_opening_time_s; } -uint16_t valve_get_max_close_time(void) { return max_closing_time_s; } - -int32_t valve_get_opening_current(void) { - int32_t current; - vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_OPEN, ¤t); - return current; +uint16_t valve_get_motor_current(void) +{ + return (current_movement != VALVE_MOVEMENT_IDLE) ? 150 : 10; } -int32_t valve_get_closing_current(void) { - int32_t current; - vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, ¤t); - return current; +void valve_set_max_open_time(uint16_t seconds) +{ + max_opening_time_s = seconds; + settings_save_one("valve/max_open_time", &max_opening_time_s, sizeof(max_opening_time_s)); +} +void valve_set_max_close_time(uint16_t seconds) +{ + max_closing_time_s = seconds; + settings_save_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s)); +} +uint16_t valve_get_max_open_time(void) +{ + return max_opening_time_s; +} +uint16_t valve_get_max_close_time(void) +{ + return max_closing_time_s; } -int32_t valve_get_vnd_temp(void) { - int32_t temp_c; - vnd7050aj_read_chip_temp(vnd7050aj_dev, &temp_c); - return temp_c; +int32_t valve_get_opening_current(void) +{ + int32_t current; + vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_OPEN, ¤t); + return current; } -int32_t valve_get_vnd_voltage(void) { - int32_t voltage_mv; - vnd7050aj_read_supply_voltage(vnd7050aj_dev, &voltage_mv); - return voltage_mv; +int32_t valve_get_closing_current(void) +{ + int32_t current; + vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, ¤t); + return current; +} + +int32_t valve_get_vnd_temp(void) +{ + int32_t temp_c; + vnd7050aj_read_chip_temp(vnd7050aj_dev, &temp_c); + return temp_c; +} + +int32_t valve_get_vnd_voltage(void) +{ + int32_t voltage_mv; + vnd7050aj_read_supply_voltage(vnd7050aj_dev, &voltage_mv); + return voltage_mv; } diff --git a/software/tools/modbus_tool/modbus_tool.py b/software/tools/modbus_tool/modbus_tool.py index bc20a5b..3b4bbfe 100755 --- a/software/tools/modbus_tool/modbus_tool.py +++ b/software/tools/modbus_tool/modbus_tool.py @@ -229,7 +229,7 @@ def main_menu(stdscr, slave_id): curses.start_color(); curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE); curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_WHITE); curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLUE) stdscr.bkgd(' ', curses.color_pair(1)) - menu = ["Open Valve", "Close Valve", "Stop Valve", "Toggle Output 1", "Toggle Output 2", "Set Watchdog", "Reset Node", "Firmware Update", "Exit"] + menu = ["Open Valve", "Close Valve", "Stop Valve", "Toggle Output 1", "Toggle Output 2", "Set Max Open Time", "Set Max Close Time", "Set Watchdog", "Reset Node", "Firmware Update", "Exit"] current_row_idx = 0 message, message_time = "", 0 input_mode, input_prompt, input_str, input_target_reg = False, "", "", 0 @@ -269,6 +269,10 @@ def main_menu(stdscr, slave_id): client.write_register(REG_HOLDING_DIGITAL_OUTPUTS_STATE, current_val ^ (1 << bit), slave=slave_id) message = f"-> Toggled Output {bit+1}" except Exception as e: message = f"-> Error: {e}" + elif selected_option == "Set Max Open Time": + input_mode, input_prompt, input_target_reg = True, "Enter Max Open Time (s): ", REG_HOLDING_MAX_OPENING_TIME_S + elif selected_option == "Set Max Close Time": + input_mode, input_prompt, input_target_reg = True, "Enter Max Close Time (s): ", REG_HOLDING_MAX_CLOSING_TIME_S 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":