/** * @file valve.c * @brief Implementation of the motorized valve control library. * * This file contains the logic for controlling a motorized valve using a * VND7050AJ high-side driver. It uses a delayed work item to handle the * safety timeouts for opening and closing operations. */ #include #include #include #include #include #include #include #define VND_NODE DT_ALIAS(vnd7050aj) #if !DT_NODE_HAS_STATUS(VND_NODE, okay) #error VND7050AJ node is not defined or enabled #endif const struct device *vnd7050aj_dev = DEVICE_DT_GET(VND_NODE); LOG_MODULE_REGISTER(valve, LOG_LEVEL_INF); 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 uint16_t end_current_threshold_open_ma = 10; // Default value for open static uint16_t end_current_threshold_close_ma = 10; // Default value for close static struct k_work_delayable valve_work; // Work item for scheduling valve movement timeouts static struct k_timer movement_timer; /** * @brief Work handler for end position checks of the valve. * * 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; if (current_movement == VALVE_MOVEMENT_OPENING) { vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_OPEN, ¤t_ma); if (current_ma > end_current_threshold_open_ma) { 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 > end_current_threshold_close_ma) { 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); } 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; } 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)); settings_load_one("valve/end_current_threshold_open", &end_current_threshold_open_ma, sizeof(end_current_threshold_open_ma)); settings_load_one("valve/end_current_threshold_close", &end_current_threshold_close_ma, sizeof(end_current_threshold_close_ma)); LOG_INF("Valve initialized: max_open=%us, max_close=%us, end_curr_open=%umA, " "end_curr_close=%umA", max_opening_time_s, max_closing_time_s, end_current_threshold_open_ma, end_current_threshold_close_ma); valve_close(); return 0; } 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 */ if (max_opening_time_s > 0) { k_timer_start(&movement_timer, K_SECONDS(max_opening_time_s), K_NO_WAIT); } k_work_schedule(&valve_work, K_MSEC(100)); } 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); if (max_closing_time_s > 0) { 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); } 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; } enum valve_state valve_get_state(void) { return current_state; } enum valve_movement valve_get_movement(void) { return current_movement; } 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)); } void valve_set_end_current_threshold_open(uint16_t current_ma) { end_current_threshold_open_ma = current_ma; settings_save_one("valve/end_current_threshold_open", &end_current_threshold_open_ma, sizeof(end_current_threshold_open_ma)); } void valve_set_end_current_threshold_close(uint16_t current_ma) { end_current_threshold_close_ma = current_ma; settings_save_one("valve/end_current_threshold_close", &end_current_threshold_close_ma, sizeof(end_current_threshold_close_ma)); } 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; } uint16_t valve_get_end_current_threshold_open(void) { return end_current_threshold_open_ma; } uint16_t valve_get_end_current_threshold_close(void) { return end_current_threshold_close_ma; } 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_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; }