/* * SPDX-FileCopyrightText: 2025 Kozmotronik Tech * * SPDX-License-Identifier: MIT */ #include "esp_check.h" #include "relay_chn.h" #include "relay_chn_priv_types.h" #include "relay_chn_core.h" #include "relay_chn_ctl.h" #include "relay_chn_output.h" #if CONFIG_RELAY_CHN_ENABLE_NVS #include "relay_chn_nvs.h" #endif static const char *TAG = "RELAY_CHN_CTL"; static relay_chn_ctl_t s_chn_ctls[CONFIG_RELAY_CHN_COUNT]; esp_err_t relay_chn_ctl_init(relay_chn_output_t *outputs, relay_chn_run_info_t *run_infos) { // Initialize all relay channels esp_err_t ret; for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { relay_chn_ctl_t* chn_ctl = &s_chn_ctls[i]; relay_chn_output_t* output = &outputs[i]; relay_chn_run_info_t* run_info = &run_infos[i]; chn_ctl->id = i; chn_ctl->state = RELAY_CHN_STATE_IDLE; chn_ctl->pending_cmd = RELAY_CHN_CMD_NONE; chn_ctl->output = output; chn_ctl->run_info = run_info; #if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT uint16_t run_limit_sec = CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC; #if CONFIG_RELAY_CHN_ENABLE_NVS // Load run limit value from NVS ret = relay_chn_nvs_get_run_limit(chn_ctl->id, &run_limit_sec, CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to load run limit from NVS for #%d with error: %s", i, esp_err_to_name(ret)); #endif chn_ctl->run_limit_sec = run_limit_sec; ret = relay_chn_init_run_limit_timer(chn_ctl); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to initialize run limit timer"); #endif ret = relay_chn_init_timer(chn_ctl); // Create direction change inertia timer ESP_RETURN_ON_ERROR(ret, TAG, "Failed to create relay channel timer for channel %d", i); } return ESP_OK; } void relay_chn_ctl_deinit() { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { relay_chn_ctl_t* chn_ctl = &s_chn_ctls[i]; if (chn_ctl->inertia_timer != NULL) { esp_timer_delete(chn_ctl->inertia_timer); chn_ctl->inertia_timer = NULL; } #if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT if (chn_ctl->run_limit_timer != NULL) { esp_timer_delete(chn_ctl->run_limit_timer); chn_ctl->run_limit_timer = NULL; } #endif } } relay_chn_state_t relay_chn_ctl_get_state(uint8_t chn_id) { return relay_chn_is_channel_id_valid(chn_id) ? s_chn_ctls[chn_id].state : RELAY_CHN_STATE_UNDEFINED; } esp_err_t relay_chn_ctl_get_state_all(relay_chn_state_t *states) { ESP_RETURN_ON_FALSE(states != NULL, ESP_ERR_INVALID_ARG, TAG, "states cannot be NULL"); for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { relay_chn_state_t *dest_state = &states[i]; if (dest_state == NULL) { ESP_LOGW(TAG, "get_state_all: States have been copied until channel %d since states[%d] is NULL", i, i); break; } *dest_state = s_chn_ctls[i].state; } return ESP_OK; } char *relay_chn_ctl_get_state_str(uint8_t chn_id) { return relay_chn_is_channel_id_valid(chn_id) ? relay_chn_state_to_str(s_chn_ctls[chn_id].state) : relay_chn_state_to_str(RELAY_CHN_STATE_UNDEFINED); } static void relay_chn_ctl_issue_cmd_on_all_channels(relay_chn_cmd_t cmd) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { relay_chn_issue_cmd(&s_chn_ctls[i], cmd); } } void relay_chn_ctl_run_forward(uint8_t chn_id) { if (relay_chn_is_channel_id_valid(chn_id)) relay_chn_issue_cmd(&s_chn_ctls[chn_id], RELAY_CHN_CMD_FORWARD); } void relay_chn_ctl_run_forward_all() { relay_chn_ctl_issue_cmd_on_all_channels(RELAY_CHN_CMD_FORWARD); } void relay_chn_ctl_run_reverse(uint8_t chn_id) { if (relay_chn_is_channel_id_valid(chn_id)) relay_chn_issue_cmd(&s_chn_ctls[chn_id], RELAY_CHN_CMD_REVERSE); } void relay_chn_ctl_run_reverse_all() { relay_chn_ctl_issue_cmd_on_all_channels(RELAY_CHN_CMD_REVERSE); } void relay_chn_ctl_stop(uint8_t chn_id) { if (relay_chn_is_channel_id_valid(chn_id)) relay_chn_issue_cmd(&s_chn_ctls[chn_id], RELAY_CHN_CMD_STOP); } void relay_chn_ctl_stop_all() { relay_chn_ctl_issue_cmd_on_all_channels(RELAY_CHN_CMD_STOP); } void relay_chn_ctl_flip_direction(uint8_t chn_id) { if (relay_chn_is_channel_id_valid(chn_id)) relay_chn_issue_cmd(&s_chn_ctls[chn_id], RELAY_CHN_CMD_FLIP); } void relay_chn_ctl_flip_direction_all() { relay_chn_ctl_issue_cmd_on_all_channels(RELAY_CHN_CMD_FLIP); } relay_chn_direction_t relay_chn_ctl_get_direction(uint8_t chn_id) { return relay_chn_is_channel_id_valid(chn_id) ? relay_chn_output_get_direction(s_chn_ctls[chn_id].output) : RELAY_CHN_DIRECTION_DEFAULT; } esp_err_t relay_chn_ctl_get_direction_all(relay_chn_direction_t *directions) { ESP_RETURN_ON_FALSE(directions != NULL, ESP_ERR_INVALID_ARG, TAG, "directions cannot be NULL"); for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { relay_chn_direction_t *dest_direction = &directions[i]; if (dest_direction == NULL) { ESP_LOGW(TAG, "get_direction_all: Directions have been copied until channel %d since directions[%d] is NULL", i, i); break; } *dest_direction = relay_chn_output_get_direction(s_chn_ctls[i].output); } return ESP_OK; } #if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT uint16_t relay_chn_ctl_get_run_limit(uint8_t chn_id) { return relay_chn_is_channel_id_valid(chn_id) ? s_chn_ctls[chn_id].run_limit_sec : 0; } esp_err_t relay_chn_ctl_get_run_limit_all(uint16_t *limits_sec) { ESP_RETURN_ON_FALSE(limits_sec != NULL, ESP_ERR_INVALID_ARG, TAG, "limits_sec cannot be NULL"); for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { uint16_t *dest_limit_sec = &limits_sec[i]; if (dest_limit_sec == NULL) { ESP_LOGW(TAG, "get_run_limit_all: Run limits have been copied until channel %d since limits_sec[%d] is NULL", i, i); break; } *dest_limit_sec = s_chn_ctls[i].run_limit_sec; } return ESP_OK; } static void relay_chn_ctl_set_run_limit_common(uint8_t chn_id, uint16_t limit_sec) { // Check for boundaries if (limit_sec > CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC) limit_sec = CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC; else if (limit_sec < CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC) limit_sec = CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC; s_chn_ctls[chn_id].run_limit_sec = limit_sec; #if CONFIG_RELAY_CHN_ENABLE_NVS relay_chn_nvs_set_run_limit(chn_id, limit_sec); #endif } void relay_chn_ctl_set_run_limit(uint8_t chn_id, uint16_t limit_sec) { if (!relay_chn_is_channel_id_valid(chn_id)) { ESP_LOGE(TAG, "set_run_limit: Invalid channel ID: %d", chn_id); return; } relay_chn_ctl_set_run_limit_common(chn_id, limit_sec); } esp_err_t relay_chn_ctl_set_run_limit_all(uint16_t *limits_sec) { ESP_RETURN_ON_FALSE(limits_sec != NULL, ESP_ERR_INVALID_ARG, TAG, "limits_sec cannot be NULL"); for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { uint16_t *src_limit_sec = &limits_sec[i]; if (src_limit_sec == NULL) { ESP_LOGW(TAG, "set_run_limit_all: Run limits have been set until channel %d since limits_sec[%d] is NULL", i, i); break; } relay_chn_ctl_set_run_limit_common(i, *src_limit_sec); } return ESP_OK; } esp_err_t relay_chn_ctl_set_run_limit_all_with(uint16_t limit_sec) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { relay_chn_ctl_set_run_limit_common(i, limit_sec); } return ESP_OK; } #endif relay_chn_ctl_t *relay_chn_ctl_get(uint8_t chn_id) { return relay_chn_is_channel_id_valid(chn_id) ? &s_chn_ctls[chn_id] : NULL; } relay_chn_ctl_t *relay_chn_ctl_get_all(void) { return s_chn_ctls; }