305 lines
9.2 KiB
C
305 lines
9.2 KiB
C
/**
|
|
* @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 <zephyr/device.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/settings/settings.h>
|
|
#include <lib/valve.h>
|
|
#include <lib/vnd7050aj.h>
|
|
|
|
#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, CONFIG_LOG_VALVE_LEVEL);
|
|
|
|
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;
|
|
static uint16_t end_current_threshold_close_ma = 10;
|
|
static uint16_t obstacle_threshold_open_ma = CONFIG_VALVE_OBSTACLE_THRESHOLD_OPEN_MA;
|
|
static uint16_t obstacle_threshold_close_ma = CONFIG_VALVE_OBSTACLE_THRESHOLD_CLOSE_MA;
|
|
static struct k_work_delayable valve_work;
|
|
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);
|
|
valve_current_open_callback(current_ma);
|
|
if (current_ma > obstacle_threshold_open_ma) {
|
|
LOG_ERR(
|
|
"Obstacle detected during opening (current: %d mA), stopping motor.",
|
|
current_ma);
|
|
current_movement = VALVE_MOVEMENT_ERROR;
|
|
valve_stop();
|
|
return;
|
|
} else if (current_ma > end_current_threshold_open_ma) {
|
|
k_work_schedule(&valve_work, VALVE_CURRENT_CHECK_INTERVAL);
|
|
return;
|
|
}
|
|
LOG_DBG("Valve finished opening");
|
|
} else if (current_movement == VALVE_MOVEMENT_CLOSING) {
|
|
vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, ¤t_ma);
|
|
valve_current_close_callback(current_ma);
|
|
if (current_ma > obstacle_threshold_close_ma) {
|
|
LOG_ERR(
|
|
"Obstacle detected during closing (current: %d mA), stopping motor.",
|
|
current_ma);
|
|
current_movement = VALVE_MOVEMENT_ERROR;
|
|
valve_stop();
|
|
return;
|
|
} else if (current_ma > end_current_threshold_close_ma) {
|
|
k_work_schedule(&valve_work, VALVE_CURRENT_CHECK_INTERVAL);
|
|
return;
|
|
}
|
|
current_state = VALVE_STATE_CLOSED;
|
|
LOG_DBG("Valve finished closing");
|
|
}
|
|
current_movement = VALVE_MOVEMENT_IDLE;
|
|
|
|
valve_stop();
|
|
}
|
|
|
|
/**
|
|
* @brief Timer handler for valve movement timeouts.
|
|
*
|
|
* This function is called when the maximum allowed time for valve movement
|
|
* (opening or closing) has been reached. It stops the valve motor, cancels
|
|
* any pending end-position checks, and sets the movement status to error.
|
|
*
|
|
* @param timer Pointer to the k_timer instance that expired.
|
|
*/
|
|
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_OPEN;
|
|
current_movement = VALVE_MOVEMENT_IDLE;
|
|
}
|
|
|
|
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));
|
|
settings_load_one("valve/obstacle_threshold_open",
|
|
&obstacle_threshold_open_ma,
|
|
sizeof(obstacle_threshold_open_ma));
|
|
settings_load_one("valve/obstacle_threshold_close",
|
|
&obstacle_threshold_close_ma,
|
|
sizeof(obstacle_threshold_close_ma));
|
|
|
|
LOG_INF("Valve initialized: max_open=%us, max_close=%us, end_curr_open=%umA, "
|
|
"end_curr_close=%umA, obs_open=%umA, obs_close=%umA",
|
|
max_opening_time_s,
|
|
max_closing_time_s,
|
|
end_current_threshold_open_ma,
|
|
end_current_threshold_close_ma,
|
|
obstacle_threshold_open_ma,
|
|
obstacle_threshold_close_ma);
|
|
valve_close();
|
|
return 0;
|
|
}
|
|
|
|
void valve_open(void)
|
|
{
|
|
LOG_DBG("Opening valve");
|
|
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; /* Security: assume valve open as soon as it starts opening */
|
|
current_movement = VALVE_MOVEMENT_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, VALVE_INITIAL_CURRENT_CHECK_INTERVAL);
|
|
}
|
|
|
|
void valve_close(void)
|
|
{
|
|
LOG_DBG("Closing valve");
|
|
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_INITIAL_CURRENT_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;
|
|
}
|
|
|
|
void valve_set_obstacle_threshold_open(uint16_t current_ma)
|
|
{
|
|
obstacle_threshold_open_ma = current_ma;
|
|
settings_save_one("valve/obstacle_threshold_open",
|
|
&obstacle_threshold_open_ma,
|
|
sizeof(obstacle_threshold_open_ma));
|
|
}
|
|
|
|
void valve_set_obstacle_threshold_close(uint16_t current_ma)
|
|
{
|
|
obstacle_threshold_close_ma = current_ma;
|
|
settings_save_one("valve/obstacle_threshold_close",
|
|
&obstacle_threshold_close_ma,
|
|
sizeof(obstacle_threshold_close_ma));
|
|
}
|
|
|
|
uint16_t valve_get_obstacle_threshold_open(void)
|
|
{
|
|
return obstacle_threshold_open_ma;
|
|
}
|
|
|
|
uint16_t valve_get_obstacle_threshold_close(void)
|
|
{
|
|
return obstacle_threshold_close_ma;
|
|
}
|
|
|
|
__weak void valve_current_open_callback(int current_ma)
|
|
{
|
|
LOG_DBG("Open current callback: %d mA", current_ma);
|
|
}
|
|
|
|
__weak void valve_current_close_callback(int current_ma)
|
|
{
|
|
LOG_DBG("Close current callback: %d mA", current_ma);
|
|
}
|