15 Commits

Author SHA1 Message Date
Kozmotronik
a06f58f42b Merge pull request 'hotfix-v0.1.0' (#8) from hotfix-v0.1.0 into v0.1
Reviewed-on: https://kozmotronik.nohost.me/gitea/KozmotronikTech/relay_chn/pulls/8
2025-02-21 17:55:55 +03:00
349868a148 Fix movement transition issue.
When transitioning the movements directly the channel should be stopped first.
2025-02-21 17:52:19 +03:00
102918fdc3 Fix: the scheduled FREE command disrupts the current command. 2025-02-21 17:09:59 +03:00
03fcb344bb Improve esp timer wrapper function.
* Remove unnecessary esp_timer checks.
* Remove the unnecessary relay_chn_invalidate_inertia_timer function.
* Change the relay_chn_start_inertia_timer function as relay_chn_start_esp_timer_once and modify the function so that it be a generic esp timer start function.
2025-02-21 16:52:09 +03:00
0917abba31 Fix warning: comparison is always true. 2025-02-21 16:23:22 +03:00
4ed116b1a9 Change the channels' starting state to FREE. 2025-02-21 16:21:51 +03:00
63b0c31908 Optimize event loop queue size depending on channel count. 2025-02-21 16:19:04 +03:00
5e6e0c9a0e Fix the fatal pin mapping issue. 2025-02-21 16:18:16 +03:00
dcb5453522 Resolve unseen conflicts. 2025-02-14 17:01:22 +03:00
feb1f4ac81 Update docs for the state_listener_manager API. 2025-02-14 15:12:37 +03:00
069363205a Rename inertia timer to distinguish timers.
Rename timer member and relevant functions for the purpose they used,
in order to distinguish between timers.
2025-02-14 15:00:02 +03:00
d4fdff949a Rename inertia timer to distinguish timers. 2025-02-14 14:50:35 +03:00
dc2dcfec7d Add support for addressing all relay channels. 2025-02-12 16:50:00 +03:00
8517993358 Update file name according to the extension guide. 2025-02-12 08:56:51 +03:00
e21bfb5b26 Update the root gitignore to ignore the build and unity-app. 2025-02-12 08:44:38 +03:00
5 changed files with 248 additions and 48 deletions

5
.gitignore vendored
View File

@@ -100,3 +100,8 @@ CTestTestfile.cmake
_deps _deps
CMakeUserPresets.json CMakeUserPresets.json
# Build directory
build
# unity-app directory
unity-app

5
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"files.associations": {
"relay_chn.h": "c"
}
}

View File

@@ -23,11 +23,14 @@
#include "esp_err.h" #include "esp_err.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include <stdint.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#define RELAY_CHN_ID_ALL CONFIG_RELAY_CHN_COUNT ///< Special ID to address all channels
/** /**
* @brief Enumeration for relay channel direction. * @brief Enumeration for relay channel direction.
*/ */
@@ -58,6 +61,20 @@ enum relay_chn_state_enum {
*/ */
typedef enum relay_chn_state_enum relay_chn_state_t; typedef enum relay_chn_state_enum relay_chn_state_t;
/**
* @brief Relay channel state change listener.
*
* An optional interface to listen to the channel state change events.
* The listeners SHOULD be implemented as light functions and SHOULD NOT contain
* any blocking calls. Otherwise the relay_chn module would not function properly
* since it is designed as event driven.
*
* @param chn_id The ID of the channel whose state has changed.
* @param old_state The old state of the channel.
* @param new_state The new state of the channel.
*/
typedef void (*relay_chn_state_listener_t)(uint8_t chn_id, relay_chn_state_t old_state, relay_chn_state_t new_state);
/** /**
* @brief Create and initialize relay channels. * @brief Create and initialize relay channels.
@@ -74,6 +91,26 @@ typedef enum relay_chn_state_enum relay_chn_state_t;
*/ */
esp_err_t relay_chn_create(const gpio_num_t* gpio_map, uint8_t gpio_count); esp_err_t relay_chn_create(const gpio_num_t* gpio_map, uint8_t gpio_count);
/**
* @brief Register a channel state change listener.
*
* @param listener A function that implements relay_chn_state_listener_t interface.
*
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NO_MEM: No enough memory
* - ESP_FAIL: General failure
*/
esp_err_t relay_chn_register_listener(relay_chn_state_listener_t listener);
/**
* @brief Unregister a channel state change listener.
*
* @param listener A function that implements relay_chn_state_listener_t interface.
*/
void relay_chn_unregister_listener(relay_chn_state_listener_t listener);
/** /**
* @brief Get the state of the specified relay channel. * @brief Get the state of the specified relay channel.
* *
@@ -100,6 +137,14 @@ relay_chn_state_t relay_chn_get_state(uint8_t chn_id);
*/ */
char *relay_chn_get_state_str(uint8_t chn_id); char *relay_chn_get_state_str(uint8_t chn_id);
/**
* @brief Return the text presentation of an state.
*
* @param state A state with type of relay_chn_state_t.
* @return char* The text presentation of the state. "UNKNOWN" if the state is not known.
*/
char *relay_chn_state_str(relay_chn_state_t state);
/** /**
* @brief Runs the relay channel in the forward direction. * @brief Runs the relay channel in the forward direction.
* *

View File

@@ -13,6 +13,7 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include "esp_err.h" #include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_task.h" #include "esp_task.h"
@@ -23,12 +24,11 @@
#include "relay_chn.h" #include "relay_chn.h"
#include "sdkconfig.h" #include "sdkconfig.h"
// TODO: on_state change API si ekle
#define RELAY_CHN_OPPOSITE_INERTIA_MS CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS #define RELAY_CHN_OPPOSITE_INERTIA_MS CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS
#define RELAY_CHN_COUNT CONFIG_RELAY_CHN_COUNT #define RELAY_CHN_COUNT CONFIG_RELAY_CHN_COUNT
const char* TAG = "relay_chn"; static const char *TAG = "relay_chn";
ESP_EVENT_DEFINE_BASE(RELAY_CHN_CMD_EVENT); ESP_EVENT_DEFINE_BASE(RELAY_CHN_CMD_EVENT);
@@ -82,9 +82,17 @@ typedef struct relay_chn_type {
relay_chn_run_info_t run_info; ///< Runtime information of the relay channel. relay_chn_run_info_t run_info; ///< Runtime information of the relay channel.
relay_chn_output_t output; ///< Output configuration of the relay channel. relay_chn_output_t output; ///< Output configuration of the relay channel.
relay_chn_cmd_t pending_cmd; ///< The command that is pending to be issued relay_chn_cmd_t pending_cmd; ///< The command that is pending to be issued
esp_timer_handle_t timer; ///< Timer to handle the opposite direction inertia time. esp_timer_handle_t inertia_timer; ///< Timer to handle the opposite direction inertia time.
} relay_chn_t; } relay_chn_t;
/**
* @brief Structure to manage the state change listeners.
*/
struct relay_chn_state_listener_manager_type {
uint8_t listener_count; ///< The number of registered listeners.
relay_chn_state_listener_t *listeners; ///< The list that holds references to the registered listeners.
} relay_chn_state_listener_manager;
static relay_chn_t relay_channels[RELAY_CHN_COUNT]; static relay_chn_t relay_channels[RELAY_CHN_COUNT];
static esp_event_loop_handle_t relay_chn_event_loop; static esp_event_loop_handle_t relay_chn_event_loop;
@@ -154,7 +162,7 @@ static esp_err_t relay_chn_init_timer(relay_chn_t *relay_chn)
.arg = &relay_chn->id, .arg = &relay_chn->id,
.name = timer_name .name = timer_name
}; };
return esp_timer_create(&timer_args, &relay_chn->timer); return esp_timer_create(&timer_args, &relay_chn->inertia_timer);
} }
/** /**
@@ -172,7 +180,7 @@ static bool relay_chn_is_gpio_valid(gpio_num_t gpio)
static esp_err_t relay_chn_create_event_loop() static esp_err_t relay_chn_create_event_loop()
{ {
esp_event_loop_args_t loop_args = { esp_event_loop_args_t loop_args = {
.queue_size = 10, .queue_size = RELAY_CHN_COUNT * 8,
.task_name = "relay_chn_event_loop", .task_name = "relay_chn_event_loop",
.task_priority = ESP_TASKD_EVENT_PRIO - 1, .task_priority = ESP_TASKD_EVENT_PRIO - 1,
.task_stack_size = 2048, .task_stack_size = 2048,
@@ -204,8 +212,9 @@ esp_err_t relay_chn_create(const gpio_num_t* gpio_map, uint8_t gpio_count)
esp_err_t ret; esp_err_t ret;
for (int i = 0; i < RELAY_CHN_COUNT; i++) { for (int i = 0; i < RELAY_CHN_COUNT; i++) {
gpio_num_t forward_pin = gpio_map[i]; int gpio_index = i << 1; // gpio_index = i * 2
gpio_num_t reverse_pin = gpio_map[i+1]; gpio_num_t forward_pin = gpio_map[gpio_index];
gpio_num_t reverse_pin = gpio_map[gpio_index + 1];
// Check if the GPIOs are valid // Check if the GPIOs are valid
if (!relay_chn_is_gpio_valid(forward_pin)) { if (!relay_chn_is_gpio_valid(forward_pin)) {
ESP_LOGE(TAG, "Invalid GPIO pin number: %d", forward_pin); ESP_LOGE(TAG, "Invalid GPIO pin number: %d", forward_pin);
@@ -235,7 +244,7 @@ esp_err_t relay_chn_create(const gpio_num_t* gpio_map, uint8_t gpio_count)
relay_chn->output.forward_pin = forward_pin; relay_chn->output.forward_pin = forward_pin;
relay_chn->output.reverse_pin = reverse_pin; relay_chn->output.reverse_pin = reverse_pin;
relay_chn->output.direction = RELAY_CHN_DIRECTION_DEFAULT; relay_chn->output.direction = RELAY_CHN_DIRECTION_DEFAULT;
relay_chn->state = RELAY_CHN_STATE_STOPPED; relay_chn->state = RELAY_CHN_STATE_FREE;
relay_chn->pending_cmd = RELAY_CHN_CMD_NONE; relay_chn->pending_cmd = RELAY_CHN_CMD_NONE;
relay_chn->run_info.last_run_cmd = RELAY_CHN_CMD_NONE; relay_chn->run_info.last_run_cmd = RELAY_CHN_CMD_NONE;
ret |= relay_chn_init_timer(relay_chn);// Create direction change inertia timer ret |= relay_chn_init_timer(relay_chn);// Create direction change inertia timer
@@ -248,9 +257,86 @@ esp_err_t relay_chn_create(const gpio_num_t* gpio_map, uint8_t gpio_count)
// Create relay channel command event loop // Create relay channel command event loop
ret |= relay_chn_create_event_loop(); ret |= relay_chn_create_event_loop();
// Init the state listener manager
relay_chn_state_listener_manager.listeners = malloc(sizeof(relay_chn_state_listener_t*));
if (relay_chn_state_listener_manager.listeners == NULL) {
ESP_LOGE(TAG, "Failed to initialize memory for the listeners!");
ret = ESP_ERR_NO_MEM;
}
return ret; return ret;
} }
static int relay_chn_listener_index(relay_chn_state_listener_t listener)
{
for (int i = 0; i < relay_chn_state_listener_manager.listener_count; i++) {
if (relay_chn_state_listener_manager.listeners[i] == listener) {
// This is the listener to unregister. Check if it is in the middle
ESP_LOGD(TAG, "relay_chn_listener_index: Listener %p; found at index %d.", listener, i);
return i;
}
}
return -1;
}
esp_err_t relay_chn_register_listener(relay_chn_state_listener_t listener)
{
if (listener == NULL) {
ESP_LOGE(TAG, "relay_chn_register_listener: A NULL listener given.");
return ESP_ERR_INVALID_ARG;
}
if (relay_chn_listener_index(listener) > -1) {
ESP_LOGD(TAG, "relay_chn_register_listener: The listener %p is already registered.", listener);
return ESP_OK;
}
ESP_LOGD(TAG, "relay_chn_register_listener: Register listener: %p", listener);
relay_chn_state_listener_manager.listeners[relay_chn_state_listener_manager.listener_count] = listener;
// Update listener count
relay_chn_state_listener_manager.listener_count++;
return ESP_OK;
}
void relay_chn_unregister_listener(relay_chn_state_listener_t listener)
{
if (listener == NULL) {
ESP_LOGD(TAG, "relay_chn_unregister_listener: A NULL listener given, nothing to do.");
return;
}
// Search the listener in the listeners list and get its index if exists
int i = relay_chn_listener_index(listener);
if (i == -1) {
ESP_LOGD(TAG, "relay_chn_unregister_listener: %p is not registered already.", listener);
return;
}
uint8_t max_index = relay_chn_state_listener_manager.listener_count - 1;
// Check whether the listener's index is in the middle
if (i == max_index) {
// free(&relay_chn_state_listener_manager.listeners[i]);
relay_chn_state_listener_manager.listeners[i] = NULL;
}
else {
// It is in the middle, so align the next elements in the list and then free the last empty pointer
// Align the next elements
uint8_t num_of_elements = max_index - i;
relay_chn_state_listener_t *pnext = NULL;
// (i + j): current index; (i + j + 1): next index
for (uint8_t j = 0; j < num_of_elements; j++) {
uint8_t current_index = i + j;
uint8_t next_index = current_index + 1;
pnext = &relay_chn_state_listener_manager.listeners[next_index];
relay_chn_state_listener_manager.listeners[current_index] = *pnext;
}
// free(&relay_chn_state_listener_manager.listeners[max_index]); // Free the last element
relay_chn_state_listener_manager.listeners[max_index] = NULL; // Free the last element
}
// Decrease listener count
relay_chn_state_listener_manager.listener_count--;
}
/** /**
* @brief Check channel ID validity * @brief Check channel ID validity
* *
@@ -260,7 +346,7 @@ esp_err_t relay_chn_create(const gpio_num_t* gpio_map, uint8_t gpio_count)
*/ */
static bool relay_chn_is_channel_id_valid(uint8_t chn_id) static bool relay_chn_is_channel_id_valid(uint8_t chn_id)
{ {
bool valid = chn_id >= 0 && chn_id < RELAY_CHN_COUNT; bool valid = (chn_id < RELAY_CHN_COUNT) || chn_id == RELAY_CHN_ID_ALL;
if (!valid) { if (!valid) {
ESP_LOGE(TAG, "Invalid channel ID: %d", chn_id); ESP_LOGE(TAG, "Invalid channel ID: %d", chn_id);
} }
@@ -280,19 +366,35 @@ static void relay_chn_dispatch_cmd(relay_chn_t *relay_chn, relay_chn_cmd_t cmd)
sizeof(relay_chn->id), portMAX_DELAY); sizeof(relay_chn->id), portMAX_DELAY);
} }
static esp_err_t relay_chn_invalidate_timer(relay_chn_t *relay_chn)
static esp_err_t relay_chn_start_esp_timer_once(esp_timer_handle_t esp_timer, uint32_t time_ms)
{ {
if (esp_timer_is_active(relay_chn->timer)) { // Invalidate the channel's timer if it is active
return esp_timer_stop(relay_chn->timer); esp_err_t ret = esp_timer_start_once(esp_timer, time_ms * 1000);
if (ret == ESP_ERR_INVALID_STATE) {
// This timer is already running, stop the timer first
ret = esp_timer_stop(esp_timer);
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
return ret;
}
ret = esp_timer_start_once(esp_timer, time_ms * 1000);
} }
return ESP_OK; return ESP_OK;
} }
static esp_err_t relay_chn_start_timer(relay_chn_t *relay_chn, uint32_t time_ms) static void relay_chn_update_state(relay_chn_t *relay_chn, relay_chn_state_t new_state)
{ {
// Invalidate the channel's timer if it is active relay_chn_state_t old = relay_chn->state;
relay_chn_invalidate_timer(relay_chn); relay_chn->state = new_state;
return esp_timer_start_once(relay_chn->timer, time_ms * 1000); for (uint8_t i = 0; i < relay_chn_state_listener_manager.listener_count; i++) {
relay_chn_state_listener_t listener = relay_chn_state_listener_manager.listeners[i];
if (listener == NULL) {
relay_chn_state_listener_manager.listener_count -= 1;
ESP_LOGD(TAG, "relay_chn_update_state: A listener is NULL at index: %u", i);
}
// Emit the state change to the listeners
listener(relay_chn->id, old, new_state);
}
} }
/** /**
@@ -344,8 +446,14 @@ static void relay_chn_issue_cmd(relay_chn_t* relay_chn, relay_chn_cmd_t cmd)
break; break;
case RELAY_CHN_STATE_STOPPED: case RELAY_CHN_STATE_STOPPED:
if (relay_chn->run_info.last_run_cmd == cmd) { if (relay_chn->run_info.last_run_cmd == cmd || relay_chn->run_info.last_run_cmd == RELAY_CHN_CMD_NONE) {
// If the last run command is the same as the current command, run the command immediately // Since the state is STOPPED, the inertia timer should be running and must be invalidated
// with the pending FREE command
esp_timer_stop(relay_chn->inertia_timer);
relay_chn->pending_cmd = RELAY_CHN_CMD_NONE;
// If this is the first run or the last run command is the same as the current command,
// run the command immediately
relay_chn_dispatch_cmd(relay_chn, cmd); relay_chn_dispatch_cmd(relay_chn, cmd);
} }
else { else {
@@ -355,11 +463,11 @@ static void relay_chn_issue_cmd(relay_chn_t* relay_chn, relay_chn_cmd_t cmd)
uint32_t inertia_time_ms = RELAY_CHN_OPPOSITE_INERTIA_MS - inertia_time_passed_ms; uint32_t inertia_time_ms = RELAY_CHN_OPPOSITE_INERTIA_MS - inertia_time_passed_ms;
if (inertia_time_ms > 0) { if (inertia_time_ms > 0) {
relay_chn->pending_cmd = cmd; relay_chn->pending_cmd = cmd;
relay_chn->state = cmd == RELAY_CHN_CMD_FORWARD relay_chn_state_t new_state = cmd == RELAY_CHN_CMD_FORWARD
? RELAY_CHN_STATE_FORWARD_PENDING ? RELAY_CHN_STATE_FORWARD_PENDING : RELAY_CHN_STATE_REVERSE_PENDING;
: RELAY_CHN_STATE_REVERSE_PENDING; relay_chn_update_state(relay_chn, new_state);
// If the time passed is less than the opposite inertia time, wait for the remaining time // If the time passed is less than the opposite inertia time, wait for the remaining time
relay_chn_start_timer(relay_chn, inertia_time_ms); relay_chn_start_esp_timer_once(relay_chn->inertia_timer, inertia_time_ms);
} }
else { else {
// If the time passed is more than the opposite inertia time, run the command immediately // If the time passed is more than the opposite inertia time, run the command immediately
@@ -376,16 +484,21 @@ static void relay_chn_issue_cmd(relay_chn_t* relay_chn, relay_chn_cmd_t cmd)
relay_chn_dispatch_cmd(relay_chn, cmd); relay_chn_dispatch_cmd(relay_chn, cmd);
return; return;
} }
if (relay_chn->run_info.last_run_cmd == cmd) { if (relay_chn->run_info.last_run_cmd == cmd) {
// If the last run command is the same as the current command, do nothing // If the last run command is the same as the current command, do nothing
return; return;
} }
// Stop the channel first before the schedule
relay_chn_dispatch_cmd(relay_chn, RELAY_CHN_CMD_STOP);
// If the last run command is different from the current command, wait for the opposite inertia time // If the last run command is different from the current command, wait for the opposite inertia time
relay_chn->pending_cmd = cmd; relay_chn->pending_cmd = cmd;
relay_chn->state = cmd == RELAY_CHN_CMD_FORWARD ? RELAY_CHN_STATE_FORWARD_PENDING : RELAY_CHN_STATE_REVERSE_PENDING; relay_chn_state_t new_state = cmd == RELAY_CHN_CMD_FORWARD
relay_chn_start_timer(relay_chn, RELAY_CHN_OPPOSITE_INERTIA_MS); ? RELAY_CHN_STATE_FORWARD_PENDING : RELAY_CHN_STATE_REVERSE_PENDING;
relay_chn_update_state(relay_chn, new_state);
relay_chn_start_esp_timer_once(relay_chn->inertia_timer, RELAY_CHN_OPPOSITE_INERTIA_MS);
break; break;
default: ESP_LOGD(TAG, "relay_chn_evaluate: Unknown relay channel state!"); default: ESP_LOGD(TAG, "relay_chn_evaluate: Unknown relay channel state!");
@@ -406,21 +519,13 @@ char *relay_chn_get_state_str(uint8_t chn_id)
if (!relay_chn_is_channel_id_valid(chn_id)) { if (!relay_chn_is_channel_id_valid(chn_id)) {
return "INVALID"; return "INVALID";
} }
switch (relay_channels[chn_id].state) { return relay_chn_state_str(relay_channels[chn_id].state);
case RELAY_CHN_STATE_FREE: }
return "FREE";
case RELAY_CHN_STATE_STOPPED: static void relay_chn_issue_cmd_on_all_channels(relay_chn_cmd_t cmd)
return "STOPPED"; {
case RELAY_CHN_STATE_FORWARD: for (int i = 0; i < RELAY_CHN_COUNT; i++) {
return "FORWARD"; relay_chn_issue_cmd(&relay_channels[i], cmd);
case RELAY_CHN_STATE_REVERSE:
return "REVERSE";
case RELAY_CHN_STATE_FORWARD_PENDING:
return "FORWARD_PENDING";
case RELAY_CHN_STATE_REVERSE_PENDING:
return "REVERSE_PENDING";
default:
return "UNKNOWN";
} }
} }
@@ -428,6 +533,11 @@ void relay_chn_run_forward(uint8_t chn_id)
{ {
if (!relay_chn_is_channel_id_valid(chn_id)) return; if (!relay_chn_is_channel_id_valid(chn_id)) return;
if (chn_id == RELAY_CHN_ID_ALL) {
relay_chn_issue_cmd_on_all_channels(RELAY_CHN_CMD_FORWARD);
return;
}
relay_chn_t* relay_chn = &relay_channels[chn_id]; relay_chn_t* relay_chn = &relay_channels[chn_id];
relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_FORWARD); relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_FORWARD);
} }
@@ -436,6 +546,11 @@ void relay_chn_run_reverse(uint8_t chn_id)
{ {
if (!relay_chn_is_channel_id_valid(chn_id)) return; if (!relay_chn_is_channel_id_valid(chn_id)) return;
if (chn_id == RELAY_CHN_ID_ALL) {
relay_chn_issue_cmd_on_all_channels(RELAY_CHN_CMD_REVERSE);
return;
}
relay_chn_t* relay_chn = &relay_channels[chn_id]; relay_chn_t* relay_chn = &relay_channels[chn_id];
relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_REVERSE); relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_REVERSE);
} }
@@ -444,6 +559,11 @@ void relay_chn_stop(uint8_t chn_id)
{ {
if (!relay_chn_is_channel_id_valid(chn_id)) return; if (!relay_chn_is_channel_id_valid(chn_id)) return;
if (chn_id == RELAY_CHN_ID_ALL) {
relay_chn_issue_cmd_on_all_channels(RELAY_CHN_CMD_STOP);
return;
}
relay_chn_t* relay_chn = &relay_channels[chn_id]; relay_chn_t* relay_chn = &relay_channels[chn_id];
relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_STOP); relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_STOP);
} }
@@ -452,6 +572,11 @@ void relay_chn_flip_direction(uint8_t chn_id)
{ {
if (!relay_chn_is_channel_id_valid(chn_id)) return; if (!relay_chn_is_channel_id_valid(chn_id)) return;
if (chn_id == RELAY_CHN_ID_ALL) {
relay_chn_issue_cmd_on_all_channels(RELAY_CHN_CMD_FLIP);
return;
}
relay_chn_t* relay_chn = &relay_channels[chn_id]; relay_chn_t* relay_chn = &relay_channels[chn_id];
relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_FLIP); relay_chn_issue_cmd(relay_chn, RELAY_CHN_CMD_FLIP);
} }
@@ -470,12 +595,12 @@ static void relay_chn_execute_stop(relay_chn_t *relay_chn)
{ {
gpio_set_level(relay_chn->output.forward_pin, 0); gpio_set_level(relay_chn->output.forward_pin, 0);
gpio_set_level(relay_chn->output.reverse_pin, 0); gpio_set_level(relay_chn->output.reverse_pin, 0);
relay_chn->state = RELAY_CHN_STATE_STOPPED; relay_chn_update_state(relay_chn, RELAY_CHN_STATE_STOPPED);
// If there is any pending command, cancel it since the STOP command is issued right after it // If there is any pending command, cancel it since the STOP command is issued right after it
relay_chn->pending_cmd = RELAY_CHN_CMD_NONE; relay_chn->pending_cmd = RELAY_CHN_CMD_NONE;
// Invalidate the channel's timer if it is active // Invalidate the channel's timer if it is active
relay_chn_invalidate_timer(relay_chn); esp_timer_stop(relay_chn->inertia_timer);
// If the channel was running, schedule a free command for the channel // If the channel was running, schedule a free command for the channel
relay_chn_cmd_t last_run_cmd = relay_chn->run_info.last_run_cmd; relay_chn_cmd_t last_run_cmd = relay_chn->run_info.last_run_cmd;
@@ -484,7 +609,7 @@ static void relay_chn_execute_stop(relay_chn_t *relay_chn)
relay_chn->run_info.last_run_cmd_time_ms = esp_timer_get_time() / 1000; relay_chn->run_info.last_run_cmd_time_ms = esp_timer_get_time() / 1000;
// Schedule a free command for the channel // Schedule a free command for the channel
relay_chn->pending_cmd = RELAY_CHN_CMD_FREE; relay_chn->pending_cmd = RELAY_CHN_CMD_FREE;
relay_chn_start_timer(relay_chn, RELAY_CHN_OPPOSITE_INERTIA_MS); relay_chn_start_esp_timer_once(relay_chn->inertia_timer, RELAY_CHN_OPPOSITE_INERTIA_MS);
} else { } else {
// If the channel was not running, issue a free command immediately // If the channel was not running, issue a free command immediately
relay_chn_dispatch_cmd(relay_chn, RELAY_CHN_CMD_FREE); relay_chn_dispatch_cmd(relay_chn, RELAY_CHN_CMD_FREE);
@@ -495,16 +620,16 @@ static void relay_chn_execute_forward(relay_chn_t *relay_chn)
{ {
gpio_set_level(relay_chn->output.reverse_pin, 0); gpio_set_level(relay_chn->output.reverse_pin, 0);
gpio_set_level(relay_chn->output.forward_pin, 1); gpio_set_level(relay_chn->output.forward_pin, 1);
relay_chn->state = RELAY_CHN_STATE_FORWARD;
relay_chn->run_info.last_run_cmd = RELAY_CHN_CMD_FORWARD; relay_chn->run_info.last_run_cmd = RELAY_CHN_CMD_FORWARD;
relay_chn_update_state(relay_chn, RELAY_CHN_STATE_FORWARD);
} }
static void relay_chn_execute_reverse(relay_chn_t *relay_chn) static void relay_chn_execute_reverse(relay_chn_t *relay_chn)
{ {
gpio_set_level(relay_chn->output.forward_pin, 0); gpio_set_level(relay_chn->output.forward_pin, 0);
gpio_set_level(relay_chn->output.reverse_pin, 1); gpio_set_level(relay_chn->output.reverse_pin, 1);
relay_chn->state = RELAY_CHN_STATE_REVERSE;
relay_chn->run_info.last_run_cmd = RELAY_CHN_CMD_REVERSE; relay_chn->run_info.last_run_cmd = RELAY_CHN_CMD_REVERSE;
relay_chn_update_state(relay_chn, RELAY_CHN_STATE_REVERSE);
} }
static void relay_chn_execute_flip(relay_chn_t *relay_chn) static void relay_chn_execute_flip(relay_chn_t *relay_chn)
@@ -519,15 +644,15 @@ static void relay_chn_execute_flip(relay_chn_t *relay_chn)
: RELAY_CHN_DIRECTION_DEFAULT; : RELAY_CHN_DIRECTION_DEFAULT;
// Set an inertia on the channel to prevent any immediate movement // Set an inertia on the channel to prevent any immediate movement
relay_chn->pending_cmd = RELAY_CHN_CMD_FREE; relay_chn->pending_cmd = RELAY_CHN_CMD_FREE;
relay_chn_start_timer(relay_chn, RELAY_CHN_OPPOSITE_INERTIA_MS); relay_chn_start_esp_timer_once(relay_chn->inertia_timer, RELAY_CHN_OPPOSITE_INERTIA_MS);
} }
void relay_chn_execute_free(relay_chn_t *relay_chn) void relay_chn_execute_free(relay_chn_t *relay_chn)
{ {
relay_chn->state = RELAY_CHN_STATE_FREE;
relay_chn->pending_cmd = RELAY_CHN_CMD_NONE; relay_chn->pending_cmd = RELAY_CHN_CMD_NONE;
// Invalidate the channel's timer if it is active // Invalidate the channel's timer if it is active
relay_chn_invalidate_timer(relay_chn); esp_timer_stop(relay_chn->inertia_timer);
relay_chn_update_state(relay_chn, RELAY_CHN_STATE_FREE);
} }
static void relay_chn_event_handler(void* handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) static void relay_chn_event_handler(void* handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
@@ -577,4 +702,24 @@ static char *relay_chn_cmd_str(relay_chn_cmd_t cmd)
} }
} }
char *relay_chn_state_str(relay_chn_state_t state)
{
switch (state) {
case RELAY_CHN_STATE_FREE:
return "FREE";
case RELAY_CHN_STATE_STOPPED:
return "STOPPED";
case RELAY_CHN_STATE_FORWARD:
return "FORWARD";
case RELAY_CHN_STATE_REVERSE:
return "REVERSE";
case RELAY_CHN_STATE_FORWARD_PENDING:
return "FORWARD_PENDING";
case RELAY_CHN_STATE_REVERSE_PENDING:
return "REVERSE_PENDING";
default:
return "UNKNOWN";
}
}
/// @} /// @}