release-1.0.0 #39

Merged
ismail merged 78 commits from release-1.0.0 into main 2025-09-13 10:55:49 +02:00
34 changed files with 731 additions and 162 deletions
Showing only changes of commit 6a4872f194 - Show all commits

View File

@@ -23,4 +23,4 @@ endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${include_dirs}
PRIV_INCLUDE_DIRS ${priv_include_dirs}
REQUIRES driver esp_timer esp_event nvs_flash)
REQUIRES driver esp_timer nvs_flash)

32
Kconfig
View File

@@ -17,6 +17,12 @@ menu "Relay Channel Driver Configuration"
help
Number of relay channels between 1 and 8.
config RELAY_CHN_ENABLE_RUN_LIMIT
bool "Enable run limit for channels"
default n
help
Enable run limit for channels as an extra layer of output protection.
config RELAY_CHN_ENABLE_TILTING
bool "Enable tilting on relay channels"
default n
@@ -62,3 +68,29 @@ menu "Relay Channel NVS Storage Configuration"
configuration. Make sure the name is exactly the same as label defined
in the relevant partition table.
endmenu
menu "Relay Channel Run Limit Configuration"
depends on RELAY_CHN_ENABLE_RUN_LIMIT
config RELAY_CHN_RUN_LIMIT_MIN_SEC
int "Minimum run limit in seconds"
range 1 60
default 10
help
Minimum run limit in seconds for channels.
config RELAY_CHN_RUN_LIMIT_MAX_SEC
int "Maximum run limit in seconds"
range 60 3600
default 600
help
Maximum run limit in seconds for channels.
config RELAY_CHN_RUN_LIMIT_DEFAULT_SEC
int "Default run limit in seconds"
range 10 3600
default 60
help
Default run limit in seconds for channels.
endmenu

View File

@@ -13,17 +13,21 @@ An ESP-IDF component for controlling relay channels, specifically designed for d
- State monitoring and reporting
- Optional sensitivty adjustable tilting feature
- Optional NVS storage for persistent configuration
- Optional configurable run limit protection
## Description
Each relay channel consists of 2 output relays controlled by 2 GPIO pins. The component provides APIs to control these relay pairs while ensuring safe operation, particularly for driving bipolar motors. To prevent mechanical strain on the motor, the component automatically manages direction changes with a configurable inertia delay, protecting it from abrupt reversals. Hence, the component handles all the required timing between the movement transitions automatically to ensure reliable operation.
The run limit feature provides an additional layer of protection by automatically stopping channels after a configurable time period. This is particularly useful for motor-driven applications where continuous operation beyond a certain duration could cause damage or safety issues. Each channel can have its own run limit setting, and when enabled, the component will automatically stop the channel once it has been running for the specified duration.
It also provides an optional tilting interface per channel base. Tilting makes a channel move with a specific pattern moving with small steps at a time. Tilting is specifically designed for controlling some types of curtains that need to be adjusted to let enter specific amount of day light.
Since it operates on relays, the switching frequency is limited to 10Hz which complies with the most of the general purpose relays' requirements. The minimum frequency is 2Hz and the duty cycle is about 10% in all ranges.
Another optional feature is NVS storage, which saves the configuration permanently across reboots of the device. These configurations are:
- Direction
- Run limit duration
- Tilt sensitivity
- Last tilt position
@@ -33,9 +37,16 @@ Configure the component through menuconfig under "Relay Channel Driver Configura
- `CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS`: Time to wait before changing direction (200-1500ms, default: 800ms)
- `CONFIG_RELAY_CHN_COUNT`: Number of relay channels (1-8, default: 1)
- `CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT`: Enable run limit protection (default: n)
- `CONFIG_RELAY_CHN_ENABLE_TILTING`: Enable tilting interface on all channels. (default: n)
- `CONFIG_RELAY_CHN_ENABLE_NVS`: Enable persistent storage in NVS (default: n)
When run limit is enabled (`CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT`), the following configuration options become available:
- `CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC`: Minimum allowed run limit duration (1-60s, default: 10s)
- `CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC`: Maximum allowed run limit duration (60-3600s, default: 600s)
- `CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC`: Default run limit duration for channels (10-3600s, default: 60s)
When NVS storage is enabled (`CONFIG_RELAY_CHN_ENABLE_NVS`), additional configuration options become available:
- `CONFIG_RELAY_CHN_NVS_NAMESPACE`: NVS namespace for storing relay channel data (default: "relay_chn")
@@ -225,7 +236,37 @@ relay_chn_direction_t direction = relay_chn_get_direction(0);
/* The listener is same for multi mode */
```
### 4. Tilting Interface (if enabled)
### 4. Run Limit Control (if enabled)
For single mode:
```c
// Assuming CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT is enabled
// Get current run limit (in seconds)
uint16_t limit = relay_chn_get_run_limit();
// Set new run limit (in seconds)
relay_chn_set_run_limit(120); // Set to 120 seconds
```
For multi mode:
```c
// Assuming CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT is enabled
// Get run limit for channel #0 (in seconds)
uint16_t limit = relay_chn_get_run_limit(0);
// Set new run limit for specific channels (in seconds)
relay_chn_set_run_limit(0, 120); // Set channel #0 to 120 seconds
relay_chn_set_run_limit(1, 180); // Set channel #1 to 180 seconds
relay_chn_set_run_limit(RELAY_CHN_ID_ALL, 90); // Set all channels to 90 seconds
```
> [!NOTE]
> When a channel reaches its run limit, it will automatically stop. The run limit timer is reset whenever the channel starts running in either direction.
### 5. Tilting Interface (if enabled)
For single mode:

View File

@@ -9,9 +9,6 @@
* To prevent mechanical strain on the motor, the component automatically manages direction changes
* with a configurable inertia delay, protecting it from abrupt reversals.
* The STOP command overrides any other command and clears the pending command if any.
*
* The module internally uses a custom esp event loop to handle relay commands serially to ensure
* reliability and prevent conflict operations. Also, the esp timer is used to manage the direction change inertia.
*/
#pragma once
@@ -146,6 +143,33 @@ void relay_chn_flip_direction(uint8_t chn_id);
*/
relay_chn_direction_t relay_chn_get_direction(uint8_t chn_id);
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
/**
* @brief Get the run limit for the specified channel
*
* @param chn_id The ID of the relay channel to query.
*
* @return The run limit value for the relevant channel if the channel ID is valid.
* 0 if the channel ID is invalid.
*/
uint16_t relay_chn_get_run_limit(uint8_t chn_id);
/**
* @brief Set the run limit for the specified channel
*
* Sets the time limit in seconds for the specified channel. It will not proceed
* if the channel ID is invalid.
* If the time_sec value is lesser than the CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC,
* the value will be set to CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC.
* If the time_sec value is greater than the CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC,
* the value will be set to CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC.
*
* @param chn_id The ID of the relay channel to query.
* @param time_sec The run limit time in seconds.
*/
void relay_chn_set_run_limit(uint8_t chn_id, uint16_t time_sec);
#endif // RELAY_CHN_ENABLE_RUN_LIMIT == 1
#if CONFIG_RELAY_CHN_ENABLE_TILTING == 1
@@ -275,6 +299,29 @@ void relay_chn_flip_direction(void);
*/
relay_chn_direction_t relay_chn_get_direction(void);
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
/**
* @brief Get the run limit for the channel
*
* @return The run limit value for the channel.
*/
uint16_t relay_chn_get_run_limit(void);
/**
* @brief Set the run limit for the channel
*
* Sets the time limit in seconds for the channel. It will not proceed
* if the channel ID is invalid.
* If the time_sec value is lesser than the CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC,
* the value will be set to CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC.
* If the time_sec value is greater than the CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC,
* the value will be set to CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC.
*
* @param time_sec The run limit time in seconds.
*/
void relay_chn_set_run_limit(uint16_t time_sec);
#endif // RELAY_CHN_ENABLE_RUN_LIMIT == 1
#if CONFIG_RELAY_CHN_ENABLE_TILTING == 1

View File

@@ -101,6 +101,36 @@ static inline relay_chn_direction_t relay_chn_get_direction(uint8_t chn_id)
return relay_chn_ctl_get_direction(chn_id);
}
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
/**
* @brief Get the run limit for the specified channel
*
* @param chn_id The ID of the relay channel to query.
*
* @return The run limit value for the relevant channel if the channel ID is valid.
* 0 if the channel ID is invalid.
*/
extern uint16_t relay_chn_ctl_get_run_limit(uint8_t chn_id);
/**
* @brief Set the run limit for the specified channel
*
* @param chn_id The ID of the relay channel to query.
* @param time_sec The run limit time in seconds.
*/
extern void relay_chn_ctl_set_run_limit(uint8_t chn_id, uint16_t time_sec);
static inline uint16_t relay_chn_get_run_limit(uint8_t chn_id)
{
return relay_chn_ctl_get_run_limit(chn_id);
}
static inline void relay_chn_set_run_limit(uint8_t chn_id, uint16_t time_sec)
{
relay_chn_ctl_set_run_limit(chn_id, time_sec);
}
#endif // RELAY_CHN_ENABLE_RUN_LIMIT == 1
#else
/**
@@ -179,6 +209,32 @@ static inline relay_chn_direction_t relay_chn_get_direction(void)
return relay_chn_ctl_get_direction();
}
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
/**
* @brief Get the run limit for the channel
*
* @return The run limit value for the channel.
*/
extern uint16_t relay_chn_ctl_get_run_limit(void);
/**
* @brief Set the run limit for the channel
*
* @param time_sec The run limit time in seconds.
*/
extern void relay_chn_ctl_set_run_limit(uint16_t time_sec);
static inline uint16_t relay_chn_get_run_limit(void)
{
return relay_chn_ctl_get_run_limit();
}
static inline void relay_chn_set_run_limit(uint16_t time_sec)
{
relay_chn_ctl_set_run_limit(time_sec);
}
#endif // RELAY_CHN_ENABLE_RUN_LIMIT == 1
#endif // RELAY_CHN_COUNT > 1
#ifdef __cplusplus

View File

@@ -15,6 +15,7 @@ extern "C" {
#define RELAY_CHN_COUNT CONFIG_RELAY_CHN_COUNT
#define RELAY_CHN_ENABLE_TILTING CONFIG_RELAY_CHN_ENABLE_TILTING
#define RELAY_CHN_ENABLE_NVS CONFIG_RELAY_CHN_ENABLE_NVS
#define RELAY_CHN_ENABLE_RUN_LIMIT CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT
#if RELAY_CHN_ENABLE_NVS == 1
#define RELAY_CHN_NVS_NAMESPACE CONFIG_RELAY_CHN_NVS_NAMESPACE
@@ -28,6 +29,12 @@ extern "C" {
#define RELAY_CHN_ID_ALL RELAY_CHN_COUNT /*!< Special ID to address all channels */
#endif
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
#define RELAY_CHN_RUN_LIMIT_MIN_SEC CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC
#define RELAY_CHN_RUN_LIMIT_MAX_SEC CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC
#define RELAY_CHN_RUN_LIMIT_DEFAULT_SEC CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -8,8 +8,6 @@
#include "esp_err.h"
#include "esp_log.h"
#include "esp_event_base.h"
#include "esp_event.h"
#include "esp_timer.h"
#include "relay_chn_defs.h"
#include "relay_chn_types.h"
@@ -19,9 +17,6 @@
extern "C" {
#endif
/// Event base used by *_core, *_ctl, and *_tilt modules.
ESP_EVENT_DECLARE_BASE(RELAY_CHN_CMD_EVENT);
/**
* @brief Initializes the relay channel timer.
*
@@ -33,6 +28,20 @@ ESP_EVENT_DECLARE_BASE(RELAY_CHN_CMD_EVENT);
*/
esp_err_t relay_chn_init_timer(relay_chn_ctl_t *chn_ctl);
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
/**
* @brief Initializes the relay channel run limit timer.
*
* This function creates a timer for the relay channel to handle run time limit.
* Required by *_ctl_* module.
*
* @param chn_ctl Pointer to the relay channel control structure.
*
* @return esp_err_t ESP_OK on success, or an error code on failure.
*/
esp_err_t relay_chn_init_run_limit_timer(relay_chn_ctl_t *chn_ctl);
#endif // RELAY_CHN_ENABLE_RUN_LIMIT
/**
* @brief Issues a command to the relay channel.
*
@@ -45,7 +54,7 @@ esp_err_t relay_chn_init_timer(relay_chn_ctl_t *chn_ctl);
void relay_chn_issue_cmd(relay_chn_ctl_t* chn_ctl, relay_chn_cmd_t cmd);
/**
* @brief Dispatches a relay channel command to the event loop.
* @brief Dispatches a relay channel command.
*
* @param chn_ctl Pointer to the relay channel control structure.
* @param cmd The command to dispatch.
@@ -104,11 +113,6 @@ char *relay_chn_state_str(relay_chn_state_t state);
bool relay_chn_is_channel_id_valid(uint8_t chn_id);
#endif // RELAY_CHN_COUNT > 1
#if RELAY_CHN_ENABLE_TILTING == 1
/// Relay channel event loop handle declaration for *_tilt module.
extern esp_event_loop_handle_t relay_chn_event_loop;
#endif // RELAY_CHN_ENABLE_TILTING
#ifdef __cplusplus
}
#endif

View File

@@ -45,6 +45,26 @@ esp_err_t relay_chn_nvs_set_direction(uint8_t ch, relay_chn_direction_t directio
*/
esp_err_t relay_chn_nvs_get_direction(uint8_t ch, relay_chn_direction_t *direction);
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
/**
* @brief Store relay channel run limit in NVS.
*
* @param[in] ch Channel number.
* @param[in] direction Run limit value to store.
* @return ESP_OK on success, error code otherwise.
*/
esp_err_t relay_chn_nvs_set_run_limit(uint8_t ch, uint16_t time_sec);
/**
* @brief Retrieve relay channel run limit from NVS.
*
* @param[in] ch Channel number.
* @param[out] direction Pointer to store retrieved run limit value.
* @return ESP_OK on success, error code otherwise.
*/
esp_err_t relay_chn_nvs_get_run_limit(uint8_t ch, uint16_t *time_sec);
#endif // RELAY_CHN_ENABLE_RUN_LIMIT == 1
#ifdef RELAY_CHN_ENABLE_TILTING
/**
* @brief Store tilt sensitivity in NVS.

View File

@@ -68,6 +68,10 @@ typedef struct {
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 */
esp_timer_handle_t inertia_timer; /*!< Timer to handle the opposite direction inertia time */
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
esp_timer_handle_t run_limit_timer; /*!< Timer to handle the run limit */
uint16_t run_limit_sec; /*!< Run limit in seconds */
#endif
#if RELAY_CHN_ENABLE_TILTING == 1
relay_chn_tilt_ctl_t *tilt_ctl; /*!< Pointer to the tilt control structure if tilting is enabled */
#endif

View File

@@ -15,7 +15,7 @@ extern "C" {
/**
* @brief Initialize relay channel tilt controls.
*
* Sets up tilt functionality for relay channels including timers and event handlers.
* Sets up tilt functionality for relay channels including timers.
* Must be called before using any other tilt functions.
*
* @param[in] chn_ctls Array of relay channel control structures.
@@ -27,7 +27,7 @@ esp_err_t relay_chn_tilt_init(relay_chn_ctl_t *chn_ctls);
/**
* @brief Deinitialize relay channel tilt controls.
*
* Cleans up tilt resources including timers and event handlers.
* Cleans up tilt resources including timers.
* Should be called when tilt functionality is no longer needed.
*/
void relay_chn_tilt_deinit(void);

View File

@@ -10,7 +10,7 @@ if [[ -z "$IDF_PATH" ]]; then
fi
# ==== 2. Valid Modes and Defaults ====
valid_test_tags=("core" "tilt" "listener" "all" "relay_chn" "nvs")
valid_test_tags=("core" "tilt" "listener" "all" "relay_chn" "nvs" "run_limit")
arg_tag="all" # Default to 'all' if no tag specified
arg_clean=false
arg_log=false
@@ -24,7 +24,7 @@ print_help() {
echo "This script builds and runs tests for the relay_chn component using QEMU."
echo ""
echo "Arguments:"
echo " -t, --tag [relay_chn|core|tilt|listener|nvs|all] Specify which test tag to run."
echo " -t, --tag [relay_chn|core|tilt|listener|nvs|run_limit|all] Specify which test tag to run."
echo ""
echo " If no tag is specified, it defaults to 'all'."
echo ""

View File

@@ -25,8 +25,6 @@
static const char *TAG = "RELAY_CHN_CORE";
ESP_EVENT_DEFINE_BASE(RELAY_CHN_CMD_EVENT);
// Structure to hold a listener entry in the linked list.
typedef struct relay_chn_listener_entry_type {
@@ -37,14 +35,29 @@ typedef struct relay_chn_listener_entry_type {
// The list that holds references to the registered listeners.
static List_t relay_chn_listener_list;
// Define the event loop for global access both for this module and tilt module.
esp_event_loop_handle_t relay_chn_event_loop = NULL;
// Private function declarations
// Event handler for the relay channel command event
static void relay_chn_event_handler(void* handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data);
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
/*
* Run limit timer callback immediately dispatches a STOP command for the
* relevant channel as soon as the run limit time times out
*/
static void relay_chn_run_limit_timer_cb(void* arg)
{
relay_chn_ctl_t* chn_ctl = (relay_chn_ctl_t*) arg;
relay_chn_dispatch_cmd(chn_ctl, RELAY_CHN_CMD_STOP);
}
esp_err_t relay_chn_init_run_limit_timer(relay_chn_ctl_t *chn_ctl)
{
char timer_name[32];
snprintf(timer_name, sizeof(timer_name), "ch_%d_rlimit_timer", chn_ctl->id);
esp_timer_create_args_t timer_args = {
.callback = relay_chn_run_limit_timer_cb,
.arg = chn_ctl,
.name = timer_name
};
return esp_timer_create(&timer_args, &chn_ctl->run_limit_timer);
}
#endif
// Timer callback function for relay channel direction change inertia.
static void relay_chn_timer_cb(void* arg)
@@ -72,24 +85,6 @@ esp_err_t relay_chn_init_timer(relay_chn_ctl_t *chn_ctl)
return esp_timer_create(&timer_args, &chn_ctl->inertia_timer);
}
static esp_err_t relay_chn_create_event_loop()
{
esp_event_loop_args_t loop_args = {
.queue_size = RELAY_CHN_COUNT * 8,
.task_name = "relay_chn_event_loop",
.task_priority = ESP_TASKD_EVENT_PRIO - 1,
.task_stack_size = 2048,
.task_core_id = tskNO_AFFINITY
};
esp_err_t ret = esp_event_loop_create(&loop_args, &relay_chn_event_loop);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to create event loop for relay channel");
ret = esp_event_handler_register_with(relay_chn_event_loop,
RELAY_CHN_CMD_EVENT,
ESP_EVENT_ANY_ID,
relay_chn_event_handler, NULL);
return ret;
}
esp_err_t relay_chn_create(const uint8_t* gpio_map, uint8_t gpio_count)
{
ESP_RETURN_ON_FALSE(gpio_map != NULL, ESP_ERR_INVALID_ARG, TAG, "gpio_map cannot be NULL");
@@ -119,10 +114,6 @@ esp_err_t relay_chn_create(const uint8_t* gpio_map, uint8_t gpio_count)
ret = relay_chn_ctl_init(outputs, run_infos);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to initialize relay channel control");
// Create relay channel command event loop
ret = relay_chn_create_event_loop();
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to create relay channel event loop");
#if RELAY_CHN_ENABLE_TILTING == 1
// Initialize the tilt feature
#if RELAY_CHN_COUNT > 1
@@ -152,10 +143,6 @@ void relay_chn_destroy(void)
relay_chn_nvs_deinit();
#endif
// Destroy the event loop
esp_event_loop_delete(relay_chn_event_loop);
relay_chn_event_loop = NULL;
// Free the listeners
while (listCURRENT_LIST_LENGTH(&relay_chn_listener_list) > 0) {
ListItem_t *pxItem = listGET_HEAD_ENTRY(&relay_chn_listener_list);
@@ -239,31 +226,6 @@ void relay_chn_unregister_listener(relay_chn_state_listener_t listener)
}
// Dispatch relay channel command to its event loop
void relay_chn_dispatch_cmd(relay_chn_ctl_t *chn_ctl, relay_chn_cmd_t cmd) {
if (cmd == RELAY_CHN_CMD_NONE) {
return;
}
// Since the event_loop library creates a deep copy of the event data,
// and we need to pass the pointer of the relevant channel, here we need
// to pass the pointer to the pointer of the channel (&chn_ctl) so that
// the pointer value is preserved in the event data.
esp_event_post_to(relay_chn_event_loop,
RELAY_CHN_CMD_EVENT,
cmd,
&chn_ctl,
sizeof(chn_ctl),
portMAX_DELAY);
#if RELAY_CHN_ENABLE_TILTING == 1
// Reset the tilt counter when the command is either FORWARD or REVERSE
if (cmd == RELAY_CHN_CMD_FORWARD || cmd == RELAY_CHN_CMD_REVERSE) {
relay_chn_tilt_reset_count(chn_ctl->tilt_ctl);
}
#endif
}
esp_err_t relay_chn_start_esp_timer_once(esp_timer_handle_t esp_timer, uint32_t time_ms)
{
esp_err_t ret = esp_timer_start_once(esp_timer, time_ms * 1000);
@@ -454,6 +416,14 @@ bool relay_chn_is_channel_id_valid(uint8_t chn_id)
#endif // RELAY_CHN_COUNT > 1
static void relay_chn_execute_idle(relay_chn_ctl_t *chn_ctl)
{
chn_ctl->pending_cmd = RELAY_CHN_CMD_NONE;
// Invalidate the channel's timer if it is active
esp_timer_stop(chn_ctl->inertia_timer);
relay_chn_update_state(chn_ctl, RELAY_CHN_STATE_IDLE);
}
static void relay_chn_execute_stop(relay_chn_ctl_t *chn_ctl)
{
if (relay_chn_output_stop(chn_ctl->output) != ESP_OK) {
@@ -467,6 +437,10 @@ static void relay_chn_execute_stop(relay_chn_ctl_t *chn_ctl)
// Invalidate the channel's timer if it is active
esp_timer_stop(chn_ctl->inertia_timer);
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
esp_timer_stop(chn_ctl->run_limit_timer);
#endif
// Save the last run time only if the previous state was either STATE FORWARD
// or STATE_REVERSE. Then schedule a free command.
if (previous_state == RELAY_CHN_STATE_FORWARD || previous_state == RELAY_CHN_STATE_REVERSE) {
@@ -477,7 +451,8 @@ static void relay_chn_execute_stop(relay_chn_ctl_t *chn_ctl)
relay_chn_start_esp_timer_once(chn_ctl->inertia_timer, RELAY_CHN_OPPOSITE_INERTIA_MS);
} else {
// If the channel was not running one of the run or fwd, issue a free command immediately
relay_chn_dispatch_cmd(chn_ctl, RELAY_CHN_CMD_IDLE);
// relay_chn_dispatch_cmd(chn_ctl, RELAY_CHN_CMD_IDLE);
relay_chn_execute_idle(chn_ctl);
}
}
@@ -489,6 +464,10 @@ static void relay_chn_execute_forward(relay_chn_ctl_t *chn_ctl)
}
relay_chn_run_info_set_last_run_cmd(chn_ctl->run_info, RELAY_CHN_CMD_FORWARD);
relay_chn_update_state(chn_ctl, RELAY_CHN_STATE_FORWARD);
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
relay_chn_start_esp_timer_once(chn_ctl->run_limit_timer, chn_ctl->run_limit_sec * 1000);
#endif
}
static void relay_chn_execute_reverse(relay_chn_ctl_t *chn_ctl)
@@ -499,6 +478,10 @@ static void relay_chn_execute_reverse(relay_chn_ctl_t *chn_ctl)
}
relay_chn_run_info_set_last_run_cmd(chn_ctl->run_info, RELAY_CHN_CMD_REVERSE);
relay_chn_update_state(chn_ctl, RELAY_CHN_STATE_REVERSE);
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
relay_chn_start_esp_timer_once(chn_ctl->run_limit_timer, chn_ctl->run_limit_sec * 1000);
#endif
}
static void relay_chn_execute_flip(relay_chn_ctl_t *chn_ctl)
@@ -509,21 +492,11 @@ static void relay_chn_execute_flip(relay_chn_ctl_t *chn_ctl)
relay_chn_start_esp_timer_once(chn_ctl->inertia_timer, RELAY_CHN_OPPOSITE_INERTIA_MS);
}
void relay_chn_execute_idle(relay_chn_ctl_t *chn_ctl)
{
chn_ctl->pending_cmd = RELAY_CHN_CMD_NONE;
// Invalidate the channel's timer if it is active
esp_timer_stop(chn_ctl->inertia_timer);
relay_chn_update_state(chn_ctl, RELAY_CHN_STATE_IDLE);
}
// Dispatch relay channel command
void relay_chn_dispatch_cmd(relay_chn_ctl_t *chn_ctl, relay_chn_cmd_t cmd) {
ESP_LOGD(TAG, "relay_chn_dispatch_cmd: Command: %d", cmd);
static void relay_chn_event_handler(void* handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
relay_chn_ctl_t* chn_ctl = *(relay_chn_ctl_t**) event_data;
ESP_RETURN_VOID_ON_FALSE(chn_ctl != NULL, TAG, "event_data is NULL");
ESP_LOGD(TAG, "relay_chn_event_handler: Command: %s", relay_chn_cmd_str(event_id));
switch (event_id) {
switch (cmd) {
case RELAY_CHN_CMD_STOP:
relay_chn_execute_stop(chn_ctl);
break;
@@ -542,6 +515,13 @@ static void relay_chn_event_handler(void* handler_arg, esp_event_base_t event_ba
default:
ESP_LOGD(TAG, "Unknown relay channel command!");
}
#if RELAY_CHN_ENABLE_TILTING == 1
// Reset the tilt counter when the command is either FORWARD or REVERSE
if (cmd == RELAY_CHN_CMD_FORWARD || cmd == RELAY_CHN_CMD_REVERSE) {
relay_chn_tilt_reset_count(chn_ctl->tilt_ctl);
}
#endif
}
char *relay_chn_cmd_str(relay_chn_cmd_t cmd)

View File

@@ -10,6 +10,10 @@
#include "relay_chn_ctl.h"
#include "relay_chn_output.h"
#if RELAY_CHN_ENABLE_NVS == 1
#include "relay_chn_nvs.h"
#endif
static const char *TAG = "RELAY_CHN_CTL";
static relay_chn_ctl_t chn_ctls[RELAY_CHN_COUNT];
@@ -30,6 +34,19 @@ esp_err_t relay_chn_ctl_init(relay_chn_output_t *outputs, relay_chn_run_info_t *
chn_ctl->output = output;
chn_ctl->run_info = run_info;
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
uint16_t run_limit_sec = RELAY_CHN_RUN_LIMIT_DEFAULT_SEC;
#if RELAY_CHN_ENABLE_NVS == 1
// Load run limit value from NVS
ret = relay_chn_nvs_get_run_limit(chn_ctl->id, &run_limit_sec);
if (ret != ESP_OK && ret != ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to load run limit from NVS for channel %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);
}
@@ -44,6 +61,12 @@ void relay_chn_ctl_deinit()
esp_timer_delete(chn_ctl->inertia_timer);
chn_ctl->inertia_timer = NULL;
}
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
if (chn_ctl->run_limit_timer != NULL) {
esp_timer_delete(chn_ctl->run_limit_timer);
chn_ctl->run_limit_timer = NULL;
}
#endif
}
}
@@ -123,6 +146,37 @@ relay_chn_direction_t relay_chn_ctl_get_direction(uint8_t chn_id)
return relay_chn_output_get_direction(chn_ctl->output);
}
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
uint16_t relay_chn_ctl_get_run_limit(uint8_t chn_id)
{
if (!relay_chn_is_channel_id_valid(chn_id) || chn_id == RELAY_CHN_ID_ALL) {
ESP_LOGE(TAG, "get_run_limit: Invalid channel ID: %d", chn_id);
return 0;
}
return chn_ctls[chn_id].run_limit_sec;
}
void relay_chn_ctl_set_run_limit(uint8_t chn_id, uint16_t time_sec)
{
if (!relay_chn_is_channel_id_valid(chn_id) || chn_id == RELAY_CHN_ID_ALL) {
ESP_LOGE(TAG, "set_run_limit: Invalid channel ID: %d", chn_id);
return;
}
// Check for boundaries
if (time_sec > RELAY_CHN_RUN_LIMIT_MAX_SEC)
time_sec = RELAY_CHN_RUN_LIMIT_MAX_SEC;
else if (time_sec < RELAY_CHN_RUN_LIMIT_MIN_SEC)
time_sec = RELAY_CHN_RUN_LIMIT_MIN_SEC;
chn_ctls[chn_id].run_limit_sec = time_sec;
#if RELAY_CHN_ENABLE_NVS == 1
relay_chn_nvs_set_run_limit(chn_id, time_sec);
#endif
}
#endif
relay_chn_ctl_t *relay_chn_ctl_get(uint8_t chn_id)
{
if (!relay_chn_is_channel_id_valid(chn_id)) {

View File

@@ -4,11 +4,17 @@
* SPDX-License-Identifier: MIT
*/
#include "esp_check.h"
#include "relay_chn_priv_types.h"
#include "relay_chn_core.h"
#include "relay_chn_ctl.h"
#include "relay_chn_output.h"
#if RELAY_CHN_ENABLE_NVS == 1
#include "relay_chn_nvs.h"
#endif
static const char *TAG = "RELAY_CHN_CTL";
static relay_chn_ctl_t chn_ctl;
@@ -21,6 +27,20 @@ esp_err_t relay_chn_ctl_init(relay_chn_output_t *output, relay_chn_run_info_t *r
chn_ctl.pending_cmd = RELAY_CHN_CMD_NONE;
chn_ctl.output = output;
chn_ctl.run_info = run_info;
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
uint16_t run_limit_sec = RELAY_CHN_RUN_LIMIT_DEFAULT_SEC;
esp_err_t ret;
#if RELAY_CHN_ENABLE_NVS == 1
// Load run limit value from NVS
ret = relay_chn_nvs_get_run_limit(chn_ctl.id, &run_limit_sec);
if (ret != ESP_OK && ret != ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to load run limit from NVS with error: %s", 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
return relay_chn_init_timer(&chn_ctl); // Create direction change inertia timer
}
@@ -31,6 +51,12 @@ void relay_chn_ctl_deinit()
esp_timer_delete(chn_ctl.inertia_timer);
chn_ctl.inertia_timer = NULL;
}
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
if (chn_ctl.run_limit_timer != NULL) {
esp_timer_delete(chn_ctl.run_limit_timer);
chn_ctl.run_limit_timer = NULL;
}
#endif
}
/* relay_chn APIs */
@@ -68,6 +94,28 @@ relay_chn_direction_t relay_chn_ctl_get_direction()
{
return relay_chn_output_get_direction(chn_ctl.output);
}
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
uint16_t relay_chn_ctl_get_run_limit()
{
return chn_ctl.run_limit_sec;
}
void relay_chn_ctl_set_run_limit(uint16_t time_sec)
{
// Check for boundaries
if (time_sec > RELAY_CHN_RUN_LIMIT_MAX_SEC)
time_sec = RELAY_CHN_RUN_LIMIT_MAX_SEC;
else if (time_sec < RELAY_CHN_RUN_LIMIT_MIN_SEC)
time_sec = RELAY_CHN_RUN_LIMIT_MIN_SEC;
chn_ctl.run_limit_sec = time_sec;
#if RELAY_CHN_ENABLE_NVS == 1
relay_chn_nvs_set_run_limit(chn_ctl.id, time_sec);
#endif
}
#endif
/* relay_chn APIs */
relay_chn_ctl_t *relay_chn_ctl_get()

View File

@@ -8,6 +8,9 @@
#include "relay_chn_nvs.h"
#define RELAY_CHN_KEY_DIR "dir" /*!< Direction key */
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
#define RELAY_CHN_KEY_RLIM(ch) "rlim_%d" /*!< Run limit key */
#endif
#ifdef RELAY_CHN_ENABLE_TILTING
#define RELAY_CHN_KEY_TSENS(ch) "tsens_%d" /*!< Tilt sensitivity key */
#define RELAY_CHN_KEY_TCNT(ch) "tcnt_%d" /*!< Tilt count key */
@@ -69,6 +72,22 @@ esp_err_t relay_chn_nvs_get_direction(uint8_t ch, relay_chn_direction_t *directi
return ESP_OK;
}
#if RELAY_CHN_ENABLE_RUN_LIMIT == 1
esp_err_t relay_chn_nvs_set_run_limit(uint8_t ch, uint16_t time_sec)
{
esp_err_t ret = nvs_set_u16(relay_chn_nvs, RELAY_CHN_KEY_RLIM(ch), time_sec);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to set run limit for channel %d", ch);
return nvs_commit(relay_chn_nvs);
}
esp_err_t relay_chn_nvs_get_run_limit(uint8_t ch, uint16_t *time_sec)
{
ESP_RETURN_ON_FALSE(time_sec != NULL, ESP_ERR_INVALID_ARG, TAG, "Run limit value pointer is NULL");
return nvs_get_u16(relay_chn_nvs, RELAY_CHN_KEY_RLIM(ch), time_sec);
}
#endif // RELAY_CHN_ENABLE_RUN_LIMIT == 1
#ifdef RELAY_CHN_ENABLE_TILTING
esp_err_t relay_chn_nvs_set_tilt_sensitivity(uint8_t ch, uint8_t sensitivity)
{

View File

@@ -39,8 +39,6 @@ static const char *TAG = "RELAY_CHN_TILT";
* 100 / (RELAY_CHN_TILT_RUN_MAX_MS - RELAY_CHN_TILT_RUN_MIN_MS) )
/**@}*/
ESP_EVENT_DEFINE_BASE(RELAY_CHN_TILT_CMD_EVENT_BASE);
/// @brief Tilt steps.
typedef enum {
@@ -78,21 +76,6 @@ static relay_chn_tilt_ctl_t tilt_ctl;
#endif
esp_err_t relay_chn_tilt_dispatch_cmd(relay_chn_tilt_ctl_t *tilt_ctl, relay_chn_tilt_cmd_t cmd)
{
if (cmd == RELAY_CHN_TILT_CMD_NONE) return ESP_ERR_INVALID_ARG;
// Since the event_loop library creates a deep copy of the event data,
// and we need to pass the pointer of the relevant tilt control, here we need
// to pass the pointer to the pointer of the tilt_control (&tilt_ctl) so that
// the pointer value is preserved in the event data.
return esp_event_post_to(relay_chn_event_loop,
RELAY_CHN_TILT_CMD_EVENT_BASE,
cmd,
&tilt_ctl,
sizeof(tilt_ctl), portMAX_DELAY);
}
// Returns the required timing before tilting depending on the last run.
static uint32_t relay_chn_tilt_get_required_timing_before_tilting(relay_chn_tilt_ctl_t *tilt_ctl, relay_chn_tilt_cmd_t cmd)
{
@@ -561,13 +544,11 @@ static void relay_chn_tilt_execute_pause(relay_chn_tilt_ctl_t *tilt_ctl)
tilt_ctl->step = RELAY_CHN_TILT_STEP_MOVE;
}
static void relay_chn_tilt_event_handler(void *handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
esp_err_t relay_chn_tilt_dispatch_cmd(relay_chn_tilt_ctl_t *tilt_ctl, relay_chn_tilt_cmd_t cmd)
{
relay_chn_tilt_ctl_t* tilt_ctl = *(relay_chn_tilt_ctl_t**) event_data;
ESP_RETURN_VOID_ON_FALSE(tilt_ctl != NULL, TAG, "event_data is NULL");
ESP_LOGD(TAG, "relay_chn_event_handler: Command: %s", relay_chn_cmd_str(event_id));
ESP_LOGD(TAG, "relay_chn_tilt_dispatch_cmd: Command: %d", cmd);
switch(event_id) {
switch(cmd) {
case RELAY_CHN_TILT_CMD_STOP:
relay_chn_tilt_execute_stop(tilt_ctl);
break;
@@ -582,8 +563,9 @@ static void relay_chn_tilt_event_handler(void *handler_arg, esp_event_base_t eve
relay_chn_update_state(tilt_ctl->chn_ctl, RELAY_CHN_STATE_TILT_REVERSE);
break;
default:
ESP_LOGW(TAG, "Unexpected relay channel tilt command: %ld!", event_id);
ESP_LOGW(TAG, "Unexpected relay channel tilt command: %d!", cmd);
}
return ESP_OK;
}
// Timer callback for the relay_chn_tilt_control_t::tilt_timer
@@ -693,8 +675,10 @@ esp_err_t relay_chn_tilt_init(relay_chn_ctl_t *chn_ctls)
sensitivity = RELAY_CHN_TILT_DEFAULT_SENSITIVITY;
tilt_count = 0;
#endif // RELAY_CHN_ENABLE_NVS == 1
relay_chn_tilt_ctl_init(&tilt_ctls[i], &chn_ctls[i], tilt_count, sensitivity);
ret = relay_chn_tilt_ctl_init(&tilt_ctls[i], &chn_ctls[i], tilt_count, sensitivity);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to init tilt control for channel %d", i);
}
return ESP_OK;
#else
sensitivity = RELAY_CHN_TILT_DEFAULT_SENSITIVITY;
tilt_count = 0;
@@ -704,13 +688,8 @@ esp_err_t relay_chn_tilt_init(relay_chn_ctl_t *chn_ctls)
ret = relay_chn_tilt_load_tilt_count(0, &tilt_count);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to load tilt count for channel %d", 0);
#endif // RELAY_CHN_ENABLE_NVS == 1
relay_chn_tilt_ctl_init(&tilt_ctl, chn_ctls, tilt_count, sensitivity);
return relay_chn_tilt_ctl_init(&tilt_ctl, chn_ctls, tilt_count, sensitivity);
#endif // RELAY_CHN_COUNT > 1
return esp_event_handler_register_with(relay_chn_event_loop,
RELAY_CHN_TILT_CMD_EVENT_BASE,
ESP_EVENT_ANY_ID,
relay_chn_tilt_event_handler, NULL);
}
void relay_chn_tilt_ctl_deinit(relay_chn_tilt_ctl_t *tilt_ctl)
@@ -736,8 +715,4 @@ void relay_chn_tilt_deinit()
#else
relay_chn_tilt_ctl_deinit(&tilt_ctl);
#endif // RELAY_CHN_COUNT > 1
esp_event_handler_unregister_with(relay_chn_event_loop,
RELAY_CHN_TILT_CMD_EVENT_BASE,
ESP_EVENT_ANY_ID,
relay_chn_tilt_event_handler);
}

View File

@@ -28,6 +28,7 @@ void tearDown()
reset_channels_to_idle_state();
}
#if CONFIG_RELAY_CHN_ENABLE_NVS == 1
static void test_nvs_flash_init(void)
{
esp_err_t ret;
@@ -52,9 +53,11 @@ static void test_nvs_flash_init(void)
}
}
#endif
TEST_ESP_OK(ret);
TEST_ESP_OK(ret);
}
#endif
#if CONFIG_RELAY_CHN_ENABLE_NVS == 1
static void test_nvs_flash_deinit(void)
{
esp_err_t ret;
@@ -65,11 +68,14 @@ static void test_nvs_flash_deinit(void)
#endif
TEST_ESP_OK(ret);
}
#endif
void app_main(void)
{
#if CONFIG_RELAY_CHN_ENABLE_NVS == 1
// Init NVS once for all tests
test_nvs_flash_init();
#endif
// Create relay_chn once for all tests
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
@@ -92,8 +98,10 @@ void app_main(void)
// Destroy relay_chn
relay_chn_destroy();
#if CONFIG_RELAY_CHN_ENABLE_NVS == 1
// Deinit NVS
test_nvs_flash_deinit();
#endif
ESP_LOGI(TEST_TAG, "All tests complete.");

View File

@@ -322,12 +322,10 @@ TEST_CASE("Flipping a running channel stops it and flips direction", "[relay_chn
// 1. Start channel running and verify state
relay_chn_run_forward(ch);
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
// 2. Flip the direction while running
relay_chn_flip_direction(ch);
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); // Give time for events to process
// 3. The channel should stop as part of the flip process
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(ch));
@@ -345,3 +343,99 @@ TEST_CASE("Direction flip handles invalid channel ID gracefully", "[relay_chn][c
relay_chn_flip_direction(invalid_ch); // Call with an invalid ID
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_DEFAULT, relay_chn_get_direction(invalid_ch));
}
#if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT == 1
#define TEST_RUN_LIMIT_SEC 5
#define TEST_SHORT_RUN_LIMIT_SEC 2
// ### Run Limit Tests
TEST_CASE("Test run limit initialization", "[relay_chn][run_limit]")
{
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
// Should initialize with default value
TEST_ASSERT_EQUAL(CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC, relay_chn_get_run_limit(i));
}
}
TEST_CASE("Test run limit setting boundaries", "[relay_chn][run_limit]")
{
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
// Test minimum boundary
relay_chn_set_run_limit(i, CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC - 1);
TEST_ASSERT_EQUAL(CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC, relay_chn_get_run_limit(i));
// Test maximum boundary
relay_chn_set_run_limit(i, CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC + 1);
TEST_ASSERT_EQUAL(CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC, relay_chn_get_run_limit(i));
// Test valid value
relay_chn_set_run_limit(i, TEST_RUN_LIMIT_SEC);
TEST_ASSERT_EQUAL(TEST_RUN_LIMIT_SEC, relay_chn_get_run_limit(i));
}
}
TEST_CASE("Test run limit stops channel after timeout", "[relay_chn][run_limit]")
{
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
// Set a short run limit for testing
relay_chn_set_run_limit(i, TEST_SHORT_RUN_LIMIT_SEC);
}
relay_chn_run_forward(RELAY_CHN_ID_ALL);
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
// Check running forward
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(i));
}
// Wait for run limit timeout
vTaskDelay(pdMS_TO_TICKS(TEST_SHORT_RUN_LIMIT_SEC * 1000 + test_delay_margin_ms));
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
}
}
TEST_CASE("Test run limit reset on direction change and time out finally", "[relay_chn][run_limit]")
{
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
// Set a short run limit
relay_chn_set_run_limit(i, TEST_SHORT_RUN_LIMIT_SEC);
// Start running forward
relay_chn_run_forward(i);
}
vTaskDelay(1000 / portTICK_PERIOD_MS); // Wait 1 second
// Change direction before timeout
relay_chn_run_reverse(RELAY_CHN_ID_ALL);
// Wait for the inertia period (after which the reverse command will be dispatched)
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(i));
}
// Timer should time out and stop the channel after the run limit time
vTaskDelay(pdMS_TO_TICKS(TEST_SHORT_RUN_LIMIT_SEC * 1000 + test_delay_margin_ms));
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
}
}
TEST_CASE("Test run limit persistence across stop/start", "[relay_chn][run_limit]")
{
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
// Set initial run limit
relay_chn_set_run_limit(i, TEST_RUN_LIMIT_SEC);
// Stop and start channel
relay_chn_stop(i);
relay_chn_run_forward(i);
// Run limit should persist
TEST_ASSERT_EQUAL(TEST_RUN_LIMIT_SEC, relay_chn_get_run_limit(i));
}
}
#endif // CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT == 1

View File

@@ -172,3 +172,79 @@ TEST_CASE("Flipping a running channel stops it and flips direction", "[relay_chn
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state());
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_FLIPPED, relay_chn_get_direction());
}
#if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT == 1
#define TEST_RUN_LIMIT_SEC 5
#define TEST_SHORT_RUN_LIMIT_SEC 2
// ### Run Limit Tests
TEST_CASE("Test run limit initialization", "[relay_chn][run_limit]")
{
// Should initialize with default value
TEST_ASSERT_EQUAL(CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC, relay_chn_get_run_limit());
}
TEST_CASE("Test run limit setting boundaries", "[relay_chn][run_limit]")
{
// Test minimum boundary
relay_chn_set_run_limit(CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC - 1);
TEST_ASSERT_EQUAL(CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC, relay_chn_get_run_limit());
// Test maximum boundary
relay_chn_set_run_limit(CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC + 1);
TEST_ASSERT_EQUAL(CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC, relay_chn_get_run_limit());
// Test valid value
relay_chn_set_run_limit(TEST_RUN_LIMIT_SEC);
TEST_ASSERT_EQUAL(TEST_RUN_LIMIT_SEC, relay_chn_get_run_limit());
}
TEST_CASE("Test run limit stops channel after timeout", "[relay_chn][run_limit]")
{
// Set a short run limit for testing
relay_chn_set_run_limit(TEST_SHORT_RUN_LIMIT_SEC);
// Start running forward
relay_chn_run_forward();
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state());
// Wait for run limit timeout
vTaskDelay(pdMS_TO_TICKS(TEST_SHORT_RUN_LIMIT_SEC * 1000 + test_delay_margin_ms));
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state());
}
TEST_CASE("Test run limit reset on direction change and time out finally", "[relay_chn][run_limit]")
{
// Set a short run limit
relay_chn_set_run_limit(TEST_SHORT_RUN_LIMIT_SEC);
// Start running forward
relay_chn_run_forward();
vTaskDelay(1000 / portTICK_PERIOD_MS); // Wait 1 second
// Change direction before timeout
relay_chn_run_reverse();
// Wait for the inertia period (after which the reverse command will be dispatched)
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state());
// Timer should time out and stop the channel after the run limit time
vTaskDelay(pdMS_TO_TICKS(TEST_SHORT_RUN_LIMIT_SEC * 1000 + test_delay_margin_ms));
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state());
}
TEST_CASE("Test run limit persistence across stop/start", "[relay_chn][run_limit]")
{
// Set initial run limit
relay_chn_set_run_limit(TEST_RUN_LIMIT_SEC);
// Stop and start channel
relay_chn_stop();
relay_chn_run_forward();
// Run limit should persist
TEST_ASSERT_EQUAL(TEST_RUN_LIMIT_SEC, relay_chn_get_run_limit());
}
#endif // CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT == 1

View File

@@ -84,6 +84,23 @@ TEST_CASE("Test relay_chn_nvs_erase_all", "[relay_chn][nvs]")
TEST_ESP_OK(relay_chn_nvs_deinit());
}
#ifdef CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT
TEST_CASE("Test run limit setting and getting", "[relay_chn][nvs][run_limit]")
{
TEST_ESP_OK(relay_chn_nvs_init());
const uint16_t run_limit_sec = 32;
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
TEST_ESP_OK(relay_chn_nvs_set_run_limit(i, run_limit_sec));
uint16_t run_limit_read;
TEST_ESP_OK(relay_chn_nvs_get_run_limit(i, &run_limit_read));
TEST_ASSERT_EQUAL(run_limit_sec, run_limit_read);
}
TEST_ESP_OK(relay_chn_nvs_deinit());
}
#endif
#ifdef RELAY_CHN_ENABLE_TILTING
TEST_CASE("Test sensitivity setting and getting", "[relay_chn][nvs][tilt]")
{

View File

@@ -77,6 +77,22 @@ TEST_CASE("Test relay_chn_nvs_erase_all", "[relay_chn][nvs]")
TEST_ESP_OK(relay_chn_nvs_deinit());
}
#ifdef CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT
TEST_CASE("Test run limit setting and getting", "[relay_chn][nvs][run_limit]")
{
TEST_ESP_OK(relay_chn_nvs_init());
const uint16_t run_limit_sec = 32;
TEST_ESP_OK(relay_chn_nvs_set_run_limit(0, run_limit_sec));
uint16_t run_limit_read;
TEST_ESP_OK(relay_chn_nvs_get_run_limit(0, &run_limit_read));
TEST_ASSERT_EQUAL(run_limit_sec, run_limit_read);
TEST_ESP_OK(relay_chn_nvs_deinit());
}
#endif
#ifdef RELAY_CHN_ENABLE_TILTING
TEST_CASE("Test sensitivity setting and getting", "[relay_chn][nvs][tilt]")
{

View File

@@ -395,13 +395,13 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
#
# Partition Table
#
CONFIG_PARTITION_TABLE_SINGLE_APP=y
# CONFIG_PARTITION_TABLE_SINGLE_APP is not set
# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set
# CONFIG_PARTITION_TABLE_TWO_OTA is not set
# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set
# CONFIG_PARTITION_TABLE_CUSTOM is not set
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv"
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions/part_nvs.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions/part_nvs.csv"
CONFIG_PARTITION_TABLE_OFFSET=0x8000
CONFIG_PARTITION_TABLE_MD5=y
# end of Partition Table
@@ -643,14 +643,6 @@ CONFIG_SPI_SLAVE_ISR_IN_IRAM=y
# CONFIG_UART_ISR_IN_IRAM is not set
# end of ESP-Driver:UART Configurations
#
# Event Loop Library
#
# CONFIG_ESP_EVENT_LOOP_PROFILING is not set
CONFIG_ESP_EVENT_POST_FROM_ISR=y
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
# end of Event Loop Library
#
# Hardware Settings
#
@@ -1268,7 +1260,8 @@ CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
# Relay Channel Driver Configuration
#
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_COUNT=2
CONFIG_RELAY_CHN_COUNT=8
CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT=y
CONFIG_RELAY_CHN_ENABLE_TILTING=y
CONFIG_RELAY_CHN_ENABLE_NVS=y
# end of Relay Channel Driver Configuration
@@ -1277,8 +1270,17 @@ CONFIG_RELAY_CHN_ENABLE_NVS=y
# Relay Channel NVS Storage Configuration
#
CONFIG_RELAY_CHN_NVS_NAMESPACE="relay_chn"
# CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION is not set
CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION=y
CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION_NAME="app_data"
# end of Relay Channel NVS Storage Configuration
#
# Relay Channel Run Limit Configuration
#
CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC=1
CONFIG_RELAY_CHN_RUN_LIMIT_MAX_SEC=600
CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC=60
# end of Relay Channel Run Limit Configuration
# end of Component config
# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set
@@ -1320,9 +1322,6 @@ CONFIG_STACK_CHECK_NONE=y
# CONFIG_WARN_WRITE_STRINGS is not set
CONFIG_ADC2_DISABLE_DAC=y
# CONFIG_MCPWM_ISR_IN_IRAM is not set
# CONFIG_EVENT_LOOP_PROFILING is not set
CONFIG_POST_EVENTS_FROM_ISR=y
CONFIG_POST_EVENTS_FROM_IRAM_ISR=y
# CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set
CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y
CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4

View File

@@ -4,6 +4,4 @@ CONFIG_ESP_TASK_WDT_INIT=n
# Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_COUNT=2
CONFIG_RELAY_CHN_ENABLE_TILTING=y
CONFIG_RELAY_CHN_ENABLE_NVS=y
CONFIG_RELAY_CHN_COUNT=8

View File

@@ -9,7 +9,6 @@ CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions/part_nvs.csv"
# Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_COUNT=2
CONFIG_RELAY_CHN_ENABLE_TILTING=y
CONFIG_RELAY_CHN_COUNT=8
CONFIG_RELAY_CHN_ENABLE_NVS=y
CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION=y

View File

@@ -0,0 +1,17 @@
# Disable task WDT for tests
CONFIG_ESP_TASK_WDT_INIT=n
# Partition configuration
CONFIG_PARTITION_TABLE_SINGLE_APP=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions/part_nvs.csv"
# Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_COUNT=8
CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT=y
CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC=1
CONFIG_RELAY_CHN_ENABLE_TILTING=y
CONFIG_RELAY_CHN_ENABLE_NVS=y
CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION=y

View File

@@ -0,0 +1,8 @@
# Disable task WDT for tests
CONFIG_ESP_TASK_WDT_INIT=n
# Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_COUNT=8
CONFIG_RELAY_CHN_ENABLE_NVS=y

View File

@@ -0,0 +1,9 @@
# Disable task WDT for tests
CONFIG_ESP_TASK_WDT_INIT=n
# Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_COUNT=8
CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT=y
CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC=1

View File

@@ -0,0 +1,8 @@
# Disable task WDT for tests
CONFIG_ESP_TASK_WDT_INIT=n
# Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_COUNT=8
CONFIG_RELAY_CHN_ENABLE_TILTING=y

View File

@@ -4,6 +4,3 @@ CONFIG_ESP_TASK_WDT_INIT=n
# Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_COUNT=1
CONFIG_RELAY_CHN_ENABLE_TILTING=y
CONFIG_RELAY_CHN_ENABLE_NVS=y

View File

@@ -9,7 +9,5 @@ CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions/part_nvs.csv"
# Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_COUNT=1
CONFIG_RELAY_CHN_ENABLE_TILTING=y
CONFIG_RELAY_CHN_ENABLE_NVS=y
CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION=y

View File

@@ -0,0 +1,16 @@
# Disable task WDT for tests
CONFIG_ESP_TASK_WDT_INIT=n
# Partition configuration
CONFIG_PARTITION_TABLE_SINGLE_APP=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions/part_nvs.csv"
# Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT=y
CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC=1
CONFIG_RELAY_CHN_ENABLE_TILTING=y
CONFIG_RELAY_CHN_ENABLE_NVS=y
CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION=y

View File

@@ -0,0 +1,7 @@
# Disable task WDT for tests
CONFIG_ESP_TASK_WDT_INIT=n
# Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_ENABLE_NVS=y

View File

@@ -0,0 +1,8 @@
# Disable task WDT for tests
CONFIG_ESP_TASK_WDT_INIT=n
# Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT=y
CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC=1

View File

@@ -0,0 +1,7 @@
# Disable task WDT for tests
CONFIG_ESP_TASK_WDT_INIT=n
# Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200
CONFIG_RELAY_CHN_ENABLE_TILTING=y