Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1776c81c8d | |||
|
2e81966afb
|
|||
| 5734f47cd3 | |||
|
d884f5f45c
|
|||
|
c7678d6084
|
|||
|
8527ebea83
|
|||
|
f31eae649f
|
|||
|
a143484748
|
|||
|
a9a8169710
|
|||
|
74f4341c1d
|
|||
|
a587036093
|
|||
|
82312ba7c3
|
|||
|
db62a7b5b2
|
|||
|
1ee70be715
|
|||
|
f1cb928341
|
|||
|
a90649a4d3
|
|||
|
b239b50abe
|
|||
|
9b2274ed7c
|
|||
| 5a38956146 | |||
| 82168f34eb | |||
| 2165e9d571 | |||
| 0ebe1c791e | |||
|
bacbe03e12
|
|||
|
be09cb883a
|
|||
|
925fd5de74
|
|||
|
2e3e92bb63
|
|||
|
c4482b8d49
|
|||
| 41c292cc89 | |||
|
ed5b86e863
|
|||
| c0c7fbf3df | |||
| a1c66d51c7 | |||
| 421dea7d69 | |||
| 99d753238b | |||
| 7afe6144bd | |||
| 4f39308f13 | |||
| 11786b7a06 | |||
| 7c18ddcc04 | |||
| e8303a9418 |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"relay_chn.h": "c"
|
"relay_chn.h": "c"
|
||||||
}
|
},
|
||||||
|
"idf.port": "/dev/ttyUSB0"
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ An ESP-IDF component for controlling relay channels, specifically designed for d
|
|||||||
|
|
||||||
## Description
|
## 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. It prevents short-circuits by automatically managing direction changes with configurable inertia timing.
|
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.
|
||||||
|
|
||||||
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.
|
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.
|
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.
|
||||||
@@ -31,13 +31,14 @@ Configure the component through menuconfig under "Relay Channel Driver Configura
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Copy the component to your project's components directory
|
Just add it as a custom dependency to your project's `idf_component.yml`:
|
||||||
2. Add dependency to your project's `idf_component.yml`:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
dependencies:
|
dependencies:
|
||||||
|
# Add as a custom component from git repository
|
||||||
relay_chn:
|
relay_chn:
|
||||||
version: "^0.1.0"
|
git: https://git.kozmotronik.com.tr/KozmotronikTech/relay_chn.git
|
||||||
|
version: '>=0.5.0'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
name: relay_chn
|
name: relay_chn
|
||||||
version: 0.1.0
|
version: "0.5.0"
|
||||||
description: Custom component for relay channel control
|
description: "Custom component for relay channel control"
|
||||||
dependencies:
|
license: "MIT"
|
||||||
idf:
|
url: "https://git.kozmotronik.com.tr/KozmotronikTech/relay_chn"
|
||||||
version: ">=4.0"
|
repository: "https://git.kozmotronik.com.tr/KozmotronikTech/relay_chn.git"
|
||||||
# TODO: Repo ve belgelendirme bağlantılarını ekle.
|
|
||||||
@@ -14,7 +14,8 @@
|
|||||||
* One relay channel consists of 2 output relays, hence 2 GPIO pins are required for each relay channel.
|
* One relay channel consists of 2 output relays, hence 2 GPIO pins are required for each relay channel.
|
||||||
* This module provides an API to control the relay channels, specifically to drive bipolar motors.
|
* This module provides an API to control the relay channels, specifically to drive bipolar motors.
|
||||||
* It also provides APIs to control the direction of the relay channel, bipolar motors in mind.
|
* It also provides APIs to control the direction of the relay channel, bipolar motors in mind.
|
||||||
* The module also automatically manages the direction change inertia to prevent short-circuiting the motor.
|
* 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 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
|
* The module internally uses a custom esp event loop to handle relay commands serially to ensure
|
||||||
@@ -48,6 +49,7 @@ typedef enum relay_chn_direction_enum relay_chn_direction_t;
|
|||||||
* @brief Enums that represent the state of a relay channel.
|
* @brief Enums that represent the state of a relay channel.
|
||||||
*/
|
*/
|
||||||
enum relay_chn_state_enum {
|
enum relay_chn_state_enum {
|
||||||
|
RELAY_CHN_STATE_UNDEFINED, ///< The relay channel state is undefined.
|
||||||
RELAY_CHN_STATE_FREE, ///< The relay channel is free to run or execute commands.
|
RELAY_CHN_STATE_FREE, ///< The relay channel is free to run or execute commands.
|
||||||
RELAY_CHN_STATE_STOPPED, ///< The relay channel is stopped and not running.
|
RELAY_CHN_STATE_STOPPED, ///< The relay channel is stopped and not running.
|
||||||
RELAY_CHN_STATE_FORWARD, ///< The relay channel is running in the forward direction.
|
RELAY_CHN_STATE_FORWARD, ///< The relay channel is running in the forward direction.
|
||||||
@@ -95,6 +97,13 @@ typedef void (*relay_chn_state_listener_t)(uint8_t chn_id, relay_chn_state_t old
|
|||||||
*/
|
*/
|
||||||
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 Destroy the relay channels and free resources.
|
||||||
|
*
|
||||||
|
* This function cleans up the relay channels and releases any resources allocated during their creation.
|
||||||
|
*/
|
||||||
|
void relay_chn_destroy(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Register a channel state change listener.
|
* @brief Register a channel state change listener.
|
||||||
*
|
*
|
||||||
|
|||||||
156
scripts/run_tests.sh
Executable file
156
scripts/run_tests.sh
Executable file
@@ -0,0 +1,156 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# ==== 1. Check ESP-IDF environment ====
|
||||||
|
if [[ -z "$IDF_PATH" ]]; then
|
||||||
|
echo "❌ ESP-IDF environment not found. Please source the export.sh file first:"
|
||||||
|
echo "'. $HOME/esp/esp-idf/export.sh' or wherever the ESP-IDF is installed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ==== 2. Valid Modes and Defaults ====
|
||||||
|
valid_test_tags=("core" "tilt" "listener" "all" "relay_chn")
|
||||||
|
arg_tag="all" # Default to 'all' if no tag specified
|
||||||
|
arg_clean=false
|
||||||
|
arg_log=false
|
||||||
|
arg_dry_run=false
|
||||||
|
arg_sdkconfig_file=""
|
||||||
|
flag_file=false
|
||||||
|
|
||||||
|
print_help() {
|
||||||
|
echo "Usage: $0 -t <tags> [OPTIONS]"
|
||||||
|
echo ""
|
||||||
|
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|all] Specify which test tag to run."
|
||||||
|
echo ""
|
||||||
|
echo " If no tag is specified, it defaults to 'all'."
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -f, --file <path> Specify a custom sdkconfig file to use for the build."
|
||||||
|
echo " Defaults to 'sdkconfig.defaults' if not provided."
|
||||||
|
echo " -c, --clean Perform a 'fullclean' before building the tests."
|
||||||
|
echo " -l, --log Log the test output to a timestamped file."
|
||||||
|
echo " -n, --dry-run Build the project without running qemu."
|
||||||
|
echo " -h, --help Show this help message and exit."
|
||||||
|
}
|
||||||
|
|
||||||
|
help() {
|
||||||
|
print_help
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
print_help
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==== 3. Argument Parsing ====
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
--tag|-t)
|
||||||
|
arg_tag="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--file|-f)
|
||||||
|
arg_sdkconfig_file="$2"
|
||||||
|
flag_file=true
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--clean|-c)
|
||||||
|
arg_clean=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--log|-l)
|
||||||
|
arg_log=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--dry-run|-n)
|
||||||
|
arg_dry_run=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--help|-h)
|
||||||
|
help
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ==== 4. Validity Check ====
|
||||||
|
if [[ ! " ${valid_test_tags[*]} " =~ " $arg_tag " ]]; then
|
||||||
|
echo "❌ Invalid mode: '$arg_tag'"
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ==== 5. Resolve Paths and Switch to Working Directory ====
|
||||||
|
script_dir=$(dirname "$(readlink -f "$0")")
|
||||||
|
project_root=$(dirname "$script_dir")
|
||||||
|
|
||||||
|
echo "🔍 Searching for 'test_apps' directory in '$project_root'..."
|
||||||
|
test_apps_dir=$(find "$project_root" -type d -name "test_apps" | head -n 1)
|
||||||
|
|
||||||
|
if [[ -z "$test_apps_dir" || ! -d "$test_apps_dir" ]]; then
|
||||||
|
echo "❌ 'test_apps' directory not found within the project root: '$project_root'"
|
||||||
|
echo " Please ensure the script is in a 'scripts' directory and 'test_apps' is a sibling."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✅ Found 'test_apps' at: $test_apps_dir"
|
||||||
|
|
||||||
|
if $flag_file; then
|
||||||
|
if [[ -z "$arg_sdkconfig_file" || ! -f "$arg_sdkconfig_file" ]]; then
|
||||||
|
echo "❌ Invalid or missing file: '$arg_sdkconfig_file'"
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
# Resolve to an absolute path to work correctly after changing directory
|
||||||
|
arg_sdkconfig_file=$(readlink -f "$arg_sdkconfig_file")
|
||||||
|
else
|
||||||
|
echo "⚠️ No SDK configuration file provided. Using default sdkconfig."
|
||||||
|
arg_sdkconfig_file="$test_apps_dir/sdkconfig.defaults"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "🧪 Test mode: $arg_tag"
|
||||||
|
echo "🧹 Clean: $arg_clean | 📄 Log: $arg_log"
|
||||||
|
|
||||||
|
echo "📂 Changing to working directory: $test_apps_dir"
|
||||||
|
cd "$test_apps_dir" || exit 1
|
||||||
|
|
||||||
|
# ==== 6. Clean if requested ====
|
||||||
|
if $arg_clean; then
|
||||||
|
echo "🧹 Doing Fullclean..."
|
||||||
|
idf.py fullclean
|
||||||
|
rm sdkconfig
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ==== 7. Building and Running Tests ====
|
||||||
|
# In some locales, we can get errors like: "Error: unknown opcode or format name 'wsr.IBREAKA1'"
|
||||||
|
# The 'LC_ALL=C' env variable is set to ensure consistent locale settings.
|
||||||
|
LC_ALL=C \
|
||||||
|
SDKCONFIG_DEFAULTS="$arg_sdkconfig_file" \
|
||||||
|
RELAY_CHN_UNITY_TEST_GROUP_TAG="$arg_tag" \
|
||||||
|
idf.py reconfigure build
|
||||||
|
|
||||||
|
echo "🚀 Running test with QEMU..."
|
||||||
|
|
||||||
|
if $arg_log; then
|
||||||
|
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||||
|
LOGFILE="test_log_${arg_tag}_$TIMESTAMP.txt"
|
||||||
|
if $arg_dry_run; then
|
||||||
|
echo "🔍 Dry run mode: Logging to $LOGFILE but not executing." | tee "$LOGFILE"
|
||||||
|
echo "Command: idf.py qemu" | tee "$LOGFILE"
|
||||||
|
else
|
||||||
|
echo "📜 Logging test output to: $LOGFILE"
|
||||||
|
idf.py qemu --qemu-extra-args "-no-reboot" | tee "$LOGFILE"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if $arg_dry_run; then
|
||||||
|
echo "🔍 Dry run mode: Not executing idf.py qemu."
|
||||||
|
echo "Command: idf.py qemu"
|
||||||
|
else
|
||||||
|
echo "🚀 Running idf.py qemu..."
|
||||||
|
idf.py qemu --qemu-extra-args "-no-reboot"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
247
src/relay_chn.c
247
src/relay_chn.c
@@ -23,6 +23,7 @@
|
|||||||
#include "esp_event_base.h"
|
#include "esp_event_base.h"
|
||||||
#include "esp_event.h"
|
#include "esp_event.h"
|
||||||
#include "relay_chn.h"
|
#include "relay_chn.h"
|
||||||
|
#include "freertos/idf_additions.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -174,18 +175,25 @@ typedef struct relay_chn_type {
|
|||||||
static esp_err_t relay_chn_init_tilt_control(relay_chn_t *relay_chn);
|
static esp_err_t relay_chn_init_tilt_control(relay_chn_t *relay_chn);
|
||||||
static esp_err_t relay_chn_tilt_init(void);
|
static esp_err_t relay_chn_tilt_init(void);
|
||||||
static void relay_chn_tilt_count_reset(relay_chn_t *relay_chn);
|
static void relay_chn_tilt_count_reset(relay_chn_t *relay_chn);
|
||||||
|
static esp_err_t relay_chn_dispatch_tilt_cmd(relay_chn_t *relay_chn, relay_chn_tilt_cmd_t cmd);
|
||||||
|
|
||||||
#endif // RELAY_CHN_ENABLE_TILTING
|
#endif // RELAY_CHN_ENABLE_TILTING
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Structure to manage the state change listeners.
|
* @brief Structure to hold a listener entry in the linked list.
|
||||||
*/
|
*/
|
||||||
struct relay_chn_state_listener_manager_type {
|
typedef struct relay_chn_listener_entry_type {
|
||||||
uint8_t listener_count; ///< The number of registered listeners.
|
relay_chn_state_listener_t listener; ///< The listener function pointer.
|
||||||
relay_chn_state_listener_t *listeners; ///< The list that holds references to the registered listeners.
|
ListItem_t list_item; ///< FreeRTOS list item.
|
||||||
} relay_chn_state_listener_manager;
|
} relay_chn_listener_entry_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The list that holds references to the registered listeners.
|
||||||
|
*
|
||||||
|
* Uses a FreeRTOS list for safe and dynamic management of listeners.
|
||||||
|
*/
|
||||||
|
static List_t relay_chn_listener_list;
|
||||||
|
|
||||||
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;
|
||||||
@@ -280,7 +288,8 @@ static esp_err_t relay_chn_create_event_loop()
|
|||||||
.task_core_id = tskNO_AFFINITY
|
.task_core_id = tskNO_AFFINITY
|
||||||
};
|
};
|
||||||
esp_err_t ret = esp_event_loop_create(&loop_args, &relay_chn_event_loop);
|
esp_err_t ret = esp_event_loop_create(&loop_args, &relay_chn_event_loop);
|
||||||
ret |= esp_event_handler_register_with(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,
|
RELAY_CHN_CMD_EVENT,
|
||||||
ESP_EVENT_ANY_ID,
|
ESP_EVENT_ANY_ID,
|
||||||
relay_chn_event_handler, NULL);
|
relay_chn_event_handler, NULL);
|
||||||
@@ -289,6 +298,8 @@ static esp_err_t relay_chn_create_event_loop()
|
|||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(gpio_map, ESP_ERR_INVALID_ARG, TAG, "gpio_map cannot be NULL");
|
||||||
|
|
||||||
// Check if the device's GPIOs are enough for the number of channels
|
// Check if the device's GPIOs are enough for the number of channels
|
||||||
if (RELAY_CHN_COUNT > (GPIO_PIN_COUNT / 2)) {
|
if (RELAY_CHN_COUNT > (GPIO_PIN_COUNT / 2)) {
|
||||||
ESP_LOGE(TAG, "Not enough GPIOs for the number of channels!");
|
ESP_LOGE(TAG, "Not enough GPIOs for the number of channels!");
|
||||||
@@ -358,84 +369,117 @@ esp_err_t relay_chn_create(const gpio_num_t* gpio_map, uint8_t gpio_count)
|
|||||||
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to initialize tilt feature");
|
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to initialize tilt feature");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Init the state listener manager
|
// Init the state listener list
|
||||||
relay_chn_state_listener_manager.listeners = malloc(sizeof(relay_chn_state_listener_t*));
|
vListInitialise(&relay_chn_listener_list);
|
||||||
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)
|
void relay_chn_destroy(void)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < relay_chn_state_listener_manager.listener_count; i++) {
|
// Destroy the event loop
|
||||||
if (relay_chn_state_listener_manager.listeners[i] == listener) {
|
esp_event_loop_delete(relay_chn_event_loop);
|
||||||
// This is the listener to unregister. Check if it is in the middle
|
relay_chn_event_loop = NULL;
|
||||||
ESP_LOGD(TAG, "relay_chn_listener_index: Listener %p; found at index %d.", listener, i);
|
|
||||||
return i;
|
// Free the listeners
|
||||||
|
while (listCURRENT_LIST_LENGTH(&relay_chn_listener_list) > 0) {
|
||||||
|
ListItem_t *pxItem = listGET_HEAD_ENTRY(&relay_chn_listener_list);
|
||||||
|
relay_chn_listener_entry_t *entry = listGET_LIST_ITEM_OWNER(pxItem);
|
||||||
|
uxListRemove(pxItem);
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy the timers and reset GPIOs
|
||||||
|
for (int i = 0; i < RELAY_CHN_COUNT; i++) {
|
||||||
|
relay_chn_t* relay_chn = &relay_channels[i];
|
||||||
|
if (relay_chn->inertia_timer != NULL) {
|
||||||
|
esp_timer_delete(relay_chn->inertia_timer);
|
||||||
|
relay_chn->inertia_timer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if RELAY_CHN_ENABLE_TILTING == 1
|
||||||
|
if (relay_chn->tilt_control.tilt_timer != NULL) {
|
||||||
|
esp_timer_delete(relay_chn->tilt_control.tilt_timer);
|
||||||
|
relay_chn->tilt_control.tilt_timer = NULL;
|
||||||
|
}
|
||||||
|
#endif // RELAY_CHN_ENABLE_TILTING
|
||||||
|
|
||||||
|
gpio_reset_pin(relay_chn->output.forward_pin);
|
||||||
|
gpio_reset_pin(relay_chn->output.reverse_pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find a listener entry in the list by its function pointer.
|
||||||
|
*
|
||||||
|
* This function replaces the old index-based search and is used to check
|
||||||
|
* for the existence of a listener before registration or for finding it
|
||||||
|
* during unregistration.
|
||||||
|
*
|
||||||
|
* @param listener The listener function pointer to find.
|
||||||
|
* @return Pointer to the listener entry if found, otherwise NULL.
|
||||||
|
*/
|
||||||
|
static relay_chn_listener_entry_t* find_listener_entry(relay_chn_state_listener_t listener)
|
||||||
|
{
|
||||||
|
// Iterate through the linked list of listeners
|
||||||
|
for (ListItem_t *pxListItem = listGET_HEAD_ENTRY(&relay_chn_listener_list);
|
||||||
|
pxListItem != listGET_END_MARKER(&relay_chn_listener_list);
|
||||||
|
pxListItem = listGET_NEXT(pxListItem)) {
|
||||||
|
|
||||||
|
relay_chn_listener_entry_t *entry = (relay_chn_listener_entry_t *) listGET_LIST_ITEM_OWNER(pxListItem);
|
||||||
|
if (entry->listener == listener) {
|
||||||
|
// Found the listener, return the entry
|
||||||
|
return entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
|
||||||
|
// Listener was not found in the list
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t relay_chn_register_listener(relay_chn_state_listener_t listener)
|
esp_err_t relay_chn_register_listener(relay_chn_state_listener_t listener)
|
||||||
{
|
{
|
||||||
if (listener == NULL) {
|
ESP_RETURN_ON_FALSE(listener, ESP_ERR_INVALID_ARG, TAG, "Listener cannot be NULL");
|
||||||
ESP_LOGE(TAG, "relay_chn_register_listener: A NULL listener given.");
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (relay_chn_listener_index(listener) > -1) {
|
// Check for duplicates
|
||||||
ESP_LOGD(TAG, "relay_chn_register_listener: The listener %p is already registered.", listener);
|
if (find_listener_entry(listener) != NULL) {
|
||||||
|
ESP_LOGD(TAG, "Listener %p already registered", listener);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGD(TAG, "relay_chn_register_listener: Register listener: %p", listener);
|
// Allocate memory for the new listener entry
|
||||||
relay_chn_state_listener_manager.listeners[relay_chn_state_listener_manager.listener_count] = listener;
|
relay_chn_listener_entry_t *entry = malloc(sizeof(relay_chn_listener_entry_t));
|
||||||
// Update listener count
|
ESP_RETURN_ON_FALSE(entry, ESP_ERR_NO_MEM, TAG, "Failed to allocate memory for listener");
|
||||||
relay_chn_state_listener_manager.listener_count++;
|
|
||||||
|
|
||||||
|
// Initialize and insert the new listener
|
||||||
|
entry->listener = listener;
|
||||||
|
vListInitialiseItem(&(entry->list_item));
|
||||||
|
listSET_LIST_ITEM_OWNER(&(entry->list_item), (void *)entry);
|
||||||
|
vListInsertEnd(&relay_chn_listener_list, &(entry->list_item));
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Registered listener %p", listener);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void relay_chn_unregister_listener(relay_chn_state_listener_t listener)
|
void relay_chn_unregister_listener(relay_chn_state_listener_t listener)
|
||||||
{
|
{
|
||||||
if (listener == NULL) {
|
if (listener == NULL)
|
||||||
ESP_LOGD(TAG, "relay_chn_unregister_listener: A NULL listener given, nothing to do.");
|
{
|
||||||
return;
|
ESP_LOGD(TAG, "Cannot unregister a NULL listener.");
|
||||||
}
|
|
||||||
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t max_index = relay_chn_state_listener_manager.listener_count - 1;
|
// Find the listener entry in the list
|
||||||
// Check whether the listener's index is in the middle
|
relay_chn_listener_entry_t *entry = find_listener_entry(listener);
|
||||||
if (i == max_index) {
|
|
||||||
// free(&relay_chn_state_listener_manager.listeners[i]);
|
if (entry != NULL) {
|
||||||
relay_chn_state_listener_manager.listeners[i] = NULL;
|
// Remove the item from the list and free the allocated memory
|
||||||
|
uxListRemove(&(entry->list_item));
|
||||||
|
free(entry);
|
||||||
|
ESP_LOGD(TAG, "Unregistered listener %p", listener);
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "Listener %p not found for unregistration.", listener);
|
||||||
}
|
}
|
||||||
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--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -490,17 +534,24 @@ static esp_err_t relay_chn_start_esp_timer_once(esp_timer_handle_t esp_timer, ui
|
|||||||
|
|
||||||
static void relay_chn_update_state(relay_chn_t *relay_chn, relay_chn_state_t new_state)
|
static void relay_chn_update_state(relay_chn_t *relay_chn, relay_chn_state_t new_state)
|
||||||
{
|
{
|
||||||
relay_chn_state_t old = relay_chn->state;
|
relay_chn_state_t old_state = relay_chn->state;
|
||||||
|
|
||||||
|
// Only update and notify if the state has actually changed.
|
||||||
|
if (old_state == new_state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
relay_chn->state = new_state;
|
relay_chn->state = new_state;
|
||||||
|
|
||||||
for (uint8_t i = 0; i < relay_chn_state_listener_manager.listener_count; i++) {
|
// Iterate through the linked list of listeners and notify them.
|
||||||
relay_chn_state_listener_t listener = relay_chn_state_listener_manager.listeners[i];
|
for (ListItem_t *pxListItem = listGET_HEAD_ENTRY(&relay_chn_listener_list);
|
||||||
if (listener == NULL) {
|
pxListItem != listGET_END_MARKER(&relay_chn_listener_list);
|
||||||
relay_chn_state_listener_manager.listener_count -= 1;
|
pxListItem = listGET_NEXT(pxListItem)) {
|
||||||
ESP_LOGD(TAG, "relay_chn_update_state: A listener is NULL at index: %u", i);
|
relay_chn_listener_entry_t *entry = (relay_chn_listener_entry_t *) listGET_LIST_ITEM_OWNER(pxListItem);
|
||||||
|
if (entry && entry->listener) {
|
||||||
|
// Emit the state change to the listeners
|
||||||
|
entry->listener(relay_chn->id, old_state, new_state);
|
||||||
}
|
}
|
||||||
// Emit the state change to the listeners
|
|
||||||
listener(relay_chn->id, old, new_state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,6 +659,37 @@ static void relay_chn_issue_cmd(relay_chn_t* relay_chn, relay_chn_cmd_t cmd)
|
|||||||
relay_chn_start_esp_timer_once(relay_chn->inertia_timer, RELAY_CHN_OPPOSITE_INERTIA_MS);
|
relay_chn_start_esp_timer_once(relay_chn->inertia_timer, RELAY_CHN_OPPOSITE_INERTIA_MS);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#if RELAY_CHN_ENABLE_TILTING == 1
|
||||||
|
case RELAY_CHN_STATE_TILT_FORWARD:
|
||||||
|
// Terminate tilting first
|
||||||
|
relay_chn_dispatch_tilt_cmd(relay_chn, RELAY_CHN_TILT_CMD_STOP);
|
||||||
|
if (cmd == RELAY_CHN_CMD_FORWARD) {
|
||||||
|
// Schedule for running forward
|
||||||
|
relay_chn->pending_cmd = cmd;
|
||||||
|
relay_chn_update_state(relay_chn, RELAY_CHN_STATE_FORWARD_PENDING);
|
||||||
|
relay_chn_start_esp_timer_once(relay_chn->inertia_timer, RELAY_CHN_OPPOSITE_INERTIA_MS);
|
||||||
|
} else if (cmd == RELAY_CHN_CMD_REVERSE) {
|
||||||
|
// Run directly since it is the same direction
|
||||||
|
relay_chn_dispatch_cmd(relay_chn, cmd);
|
||||||
|
relay_chn_update_state(relay_chn, RELAY_CHN_STATE_REVERSE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RELAY_CHN_STATE_TILT_REVERSE:
|
||||||
|
// Terminate tilting first
|
||||||
|
relay_chn_dispatch_tilt_cmd(relay_chn, RELAY_CHN_TILT_CMD_STOP);
|
||||||
|
if (cmd == RELAY_CHN_CMD_FORWARD) {
|
||||||
|
// Run directly since it is the same direction
|
||||||
|
relay_chn_dispatch_cmd(relay_chn, cmd);
|
||||||
|
relay_chn_update_state(relay_chn, RELAY_CHN_STATE_FORWARD);
|
||||||
|
} else if (cmd == RELAY_CHN_CMD_REVERSE) {
|
||||||
|
// Schedule for running reverse
|
||||||
|
relay_chn->pending_cmd = cmd;
|
||||||
|
relay_chn_update_state(relay_chn, RELAY_CHN_STATE_REVERSE_PENDING);
|
||||||
|
relay_chn_start_esp_timer_once(relay_chn->inertia_timer, RELAY_CHN_OPPOSITE_INERTIA_MS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
default: ESP_LOGD(TAG, "relay_chn_evaluate: Unknown relay channel state!");
|
default: ESP_LOGD(TAG, "relay_chn_evaluate: Unknown relay channel state!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -615,16 +697,16 @@ static void relay_chn_issue_cmd(relay_chn_t* relay_chn, relay_chn_cmd_t cmd)
|
|||||||
/* relay_chn APIs */
|
/* relay_chn APIs */
|
||||||
relay_chn_state_t relay_chn_get_state(uint8_t chn_id)
|
relay_chn_state_t relay_chn_get_state(uint8_t chn_id)
|
||||||
{
|
{
|
||||||
if (!relay_chn_is_channel_id_valid(chn_id)) {
|
if (!relay_chn_is_channel_id_valid(chn_id) || chn_id == RELAY_CHN_ID_ALL) {
|
||||||
return RELAY_CHN_STATE_STOPPED;
|
return RELAY_CHN_STATE_UNDEFINED;
|
||||||
}
|
}
|
||||||
return relay_channels[chn_id].state;
|
return relay_channels[chn_id].state;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *relay_chn_get_state_str(uint8_t chn_id)
|
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) || chn_id == RELAY_CHN_ID_ALL) {
|
||||||
return "INVALID";
|
return relay_chn_state_str(RELAY_CHN_STATE_UNDEFINED);
|
||||||
}
|
}
|
||||||
return relay_chn_state_str(relay_channels[chn_id].state);
|
return relay_chn_state_str(relay_channels[chn_id].state);
|
||||||
}
|
}
|
||||||
@@ -701,7 +783,8 @@ static esp_err_t relay_chn_output_stop(relay_chn_t *relay_chn)
|
|||||||
{
|
{
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
ret = gpio_set_level(relay_chn->output.forward_pin, 0);
|
ret = gpio_set_level(relay_chn->output.forward_pin, 0);
|
||||||
ret |= gpio_set_level(relay_chn->output.reverse_pin, 0);
|
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to set forward pin to LOW for relay channel #%d", relay_chn->id);
|
||||||
|
ret = gpio_set_level(relay_chn->output.reverse_pin, 0);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,7 +792,8 @@ static esp_err_t relay_chn_output_forward(relay_chn_t *relay_chn)
|
|||||||
{
|
{
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
ret = gpio_set_level(relay_chn->output.forward_pin, 1);
|
ret = gpio_set_level(relay_chn->output.forward_pin, 1);
|
||||||
ret |= gpio_set_level(relay_chn->output.reverse_pin, 0);
|
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to set forward pin to HIGH for relay channel #%d", relay_chn->id);
|
||||||
|
ret = gpio_set_level(relay_chn->output.reverse_pin, 0);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -717,7 +801,8 @@ static esp_err_t relay_chn_output_reverse(relay_chn_t *relay_chn)
|
|||||||
{
|
{
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
ret = gpio_set_level(relay_chn->output.forward_pin, 0);
|
ret = gpio_set_level(relay_chn->output.forward_pin, 0);
|
||||||
ret |= gpio_set_level(relay_chn->output.reverse_pin, 1);
|
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to set forward pin to LOW for relay channel #%d", relay_chn->id);
|
||||||
|
ret = gpio_set_level(relay_chn->output.reverse_pin, 1);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1183,8 +1268,11 @@ static uint32_t relay_chn_tilt_count_update(relay_chn_t *relay_chn)
|
|||||||
return ++relay_chn->tilt_control.tilt_counter.tilt_forward_count;
|
return ++relay_chn->tilt_control.tilt_counter.tilt_forward_count;
|
||||||
}
|
}
|
||||||
else if (relay_chn->tilt_control.cmd == RELAY_CHN_TILT_CMD_REVERSE) {
|
else if (relay_chn->tilt_control.cmd == RELAY_CHN_TILT_CMD_REVERSE) {
|
||||||
if (relay_chn->tilt_control.tilt_counter.tilt_forward_count > 0)
|
if (relay_chn->tilt_control.tilt_counter.tilt_forward_count > 0) {
|
||||||
return --relay_chn->tilt_control.tilt_counter.tilt_forward_count;
|
--relay_chn->tilt_control.tilt_counter.tilt_forward_count;
|
||||||
|
// Still should do one more move, return non-zero value
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1198,8 +1286,11 @@ static uint32_t relay_chn_tilt_count_update(relay_chn_t *relay_chn)
|
|||||||
return ++relay_chn->tilt_control.tilt_counter.tilt_reverse_count;
|
return ++relay_chn->tilt_control.tilt_counter.tilt_reverse_count;
|
||||||
}
|
}
|
||||||
else if (relay_chn->tilt_control.cmd == RELAY_CHN_TILT_CMD_FORWARD) {
|
else if (relay_chn->tilt_control.cmd == RELAY_CHN_TILT_CMD_FORWARD) {
|
||||||
if (relay_chn->tilt_control.tilt_counter.tilt_reverse_count > 0)
|
if (relay_chn->tilt_control.tilt_counter.tilt_reverse_count > 0) {
|
||||||
return --relay_chn->tilt_control.tilt_counter.tilt_reverse_count;
|
--relay_chn->tilt_control.tilt_counter.tilt_reverse_count;
|
||||||
|
// Still should do one more move, return non-zero value
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
|
||||||
# in this exact order for cmake to work correctly
|
|
||||||
cmake_minimum_required(VERSION 3.5)
|
|
||||||
|
|
||||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components"
|
|
||||||
"../../relay_chn")
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
|
||||||
project(relay_chn_test)
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
idf_component_register(SRCS_DIRS "."
|
|
||||||
PRIV_INCLUDE_DIRS "."
|
|
||||||
PRIV_REQUIRES unity test_utils relay_chn)
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
#include "driver/gpio.h"
|
|
||||||
#include "unity.h"
|
|
||||||
#include "unity_test_utils.h"
|
|
||||||
#include "relay_chn.h"
|
|
||||||
|
|
||||||
|
|
||||||
const gpio_num_t gpio_map[] = {GPIO_NUM_4, GPIO_NUM_5, GPIO_NUM_18, GPIO_NUM_19};
|
|
||||||
const uint8_t gpio_count = sizeof(gpio_map) / sizeof(gpio_map[0]);
|
|
||||||
const uint8_t relay_chn_count = gpio_count / 2;
|
|
||||||
|
|
||||||
TEST_CASE("relay chn inits correctly", "[relay_chn]")
|
|
||||||
{
|
|
||||||
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Relay channels run forward and update state", "[relay_chn][forward]")
|
|
||||||
{
|
|
||||||
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
|
||||||
// Test forward run on all channels
|
|
||||||
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
|
|
||||||
relay_chn_run_forward(i); // Run the channel forward
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(i));
|
|
||||||
relay_chn_stop(i); // Stop the channel
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
|
|
||||||
|
|
||||||
relay_chn_flip_direction(i); // Flip the direction
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_FLIPPED, relay_chn_get_direction(i));
|
|
||||||
relay_chn_run_forward(i); // Run the channel forward
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(i));
|
|
||||||
relay_chn_stop(i); // Stop the channel
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Relay channels run reverse and update state", "[relay_chn][reverse]")
|
|
||||||
{
|
|
||||||
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
|
||||||
// Test reverse run on all channels
|
|
||||||
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
|
|
||||||
relay_chn_run_reverse(i); // Run the channel reverse
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(i));
|
|
||||||
relay_chn_stop(i); // Stop the channel
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
|
|
||||||
|
|
||||||
relay_chn_flip_direction(i); // Flip the direction
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_FLIPPED, relay_chn_get_direction(i));
|
|
||||||
relay_chn_run_reverse(i); // Run the channel forward
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(i));
|
|
||||||
relay_chn_stop(i); // Stop the channel
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void check_channels_state_unchanged(void)
|
|
||||||
{
|
|
||||||
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_DEFAULT, relay_chn_get_direction(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Relay channels do not change state for invalid channel", "[relay_chn][invalid]")
|
|
||||||
{
|
|
||||||
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
|
||||||
// Test invalid channel run
|
|
||||||
relay_chn_run_forward(relay_chn_count + 1); // Run the channel forward
|
|
||||||
check_channels_state_unchanged();
|
|
||||||
relay_chn_run_reverse(relay_chn_count + 1); // Run the channel reverse
|
|
||||||
check_channels_state_unchanged();
|
|
||||||
relay_chn_stop(relay_chn_count + 1); // Stop the channel
|
|
||||||
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(relay_chn_count + 1));
|
|
||||||
check_channels_state_unchanged();
|
|
||||||
relay_chn_flip_direction(relay_chn_count + 1); // Flip the direction
|
|
||||||
check_channels_state_unchanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void setUp(void)
|
|
||||||
{
|
|
||||||
// Run before each test
|
|
||||||
}
|
|
||||||
|
|
||||||
void tearDown(void)
|
|
||||||
{
|
|
||||||
// Run after each test
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test app entry point
|
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
// Run the Unity tests menu
|
|
||||||
unity_run_menu();
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# For IDF 5.0
|
|
||||||
CONFIG_ESP_TASK_WDT_EN=n
|
|
||||||
|
|
||||||
# For IDF4.4
|
|
||||||
CONFIG_ESP_TASK_WDT=n
|
|
||||||
20
test_apps/CMakeLists.txt
Normal file
20
test_apps/CMakeLists.txt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# This is the project CMakeLists.txt file for the test subproject
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
# Define component search paths
|
||||||
|
# IMPORTANT: We should tell to the ESP-IDF
|
||||||
|
# where it can find relay_chn component.
|
||||||
|
# We add the 'relay_chn' root directory to the EXTRA_COMPONENT_DIRS by specifying: "../"
|
||||||
|
set(EXTRA_COMPONENT_DIRS "../")
|
||||||
|
|
||||||
|
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||||
|
set(COMPONENTS main)
|
||||||
|
|
||||||
|
# Include ESP-IDF project build system
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
|
||||||
|
# Inject the test tag into the build
|
||||||
|
add_compile_definitions(RELAY_CHN_UNITY_TEST_GROUP_TAG=\"$ENV{RELAY_CHN_UNITY_TEST_GROUP_TAG}\")
|
||||||
|
|
||||||
|
# Define the name of this project
|
||||||
|
project(relay_chn_test)
|
||||||
20
test_apps/main/CMakeLists.txt
Normal file
20
test_apps/main/CMakeLists.txt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# === These files must be included in any case ===
|
||||||
|
set(srcs "test_common.c"
|
||||||
|
"test_app_main.c"
|
||||||
|
"test_relay_chn_core.c"
|
||||||
|
"test_relay_chn_listener.c")
|
||||||
|
|
||||||
|
if(CONFIG_RELAY_CHN_ENABLE_TILTING)
|
||||||
|
list(APPEND srcs "test_relay_chn_tilt.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "srcs=${srcs}")
|
||||||
|
|
||||||
|
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||||
|
# the component can be registered as WHOLE_ARCHIVE
|
||||||
|
idf_component_register(
|
||||||
|
SRCS ${srcs}
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
REQUIRES unity relay_chn
|
||||||
|
WHOLE_ARCHIVE
|
||||||
|
)
|
||||||
49
test_apps/main/test_app_main.c
Normal file
49
test_apps/main/test_app_main.c
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "test_common.h"
|
||||||
|
#include "unity.h"
|
||||||
|
#include "unity_internals.h"
|
||||||
|
#include "unity_test_runner.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef RELAY_CHN_UNITY_TEST_GROUP_TAG
|
||||||
|
#warning "RELAY_CHN_UNITY_TEST_GROUP_TAG is not defined, using default 'relay_chn'"
|
||||||
|
#define RELAY_CHN_UNITY_TEST_GROUP_TAG "relay_chn"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void setUp()
|
||||||
|
{
|
||||||
|
g_is_component_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown()
|
||||||
|
{
|
||||||
|
// Clean up after each test
|
||||||
|
if (g_is_component_initialized) {
|
||||||
|
relay_chn_destroy();
|
||||||
|
g_is_component_initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
UNITY_BEGIN();
|
||||||
|
|
||||||
|
// Log general test information
|
||||||
|
ESP_LOGI(TEST_TAG, "Available test count: %d", unity_get_test_count());
|
||||||
|
ESP_LOGI(TEST_TAG, "Running tests for tag: %s", RELAY_CHN_UNITY_TEST_GROUP_TAG);
|
||||||
|
|
||||||
|
if (strncmp(RELAY_CHN_UNITY_TEST_GROUP_TAG, "all", strlen("all")) == 0) {
|
||||||
|
unity_run_all_tests();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unity_run_tests_by_tag(RELAY_CHN_UNITY_TEST_GROUP_TAG, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
UNITY_END();
|
||||||
|
ESP_LOGI(TEST_TAG, "All tests complete.");
|
||||||
|
|
||||||
|
esp_restart(); // Restart to invoke qemu exit
|
||||||
|
}
|
||||||
17
test_apps/main/test_common.c
Normal file
17
test_apps/main/test_common.c
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include "test_common.h"
|
||||||
|
|
||||||
|
const char *TEST_TAG = "RELAY_CHN_TEST";
|
||||||
|
|
||||||
|
// GPIO eşlemesi (örn: GPIO_NUM_4 vs GPIO_NUM_5)
|
||||||
|
const gpio_num_t gpio_map[] = {
|
||||||
|
GPIO_NUM_4, GPIO_NUM_5, GPIO_NUM_18, GPIO_NUM_19
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t gpio_count = sizeof(gpio_map) / sizeof(gpio_map[0]);
|
||||||
|
const uint8_t relay_chn_count = gpio_count / 2;
|
||||||
|
|
||||||
|
// Konfigürasyon tabanlı inertia süresi
|
||||||
|
const uint32_t opposite_inertia_ms = CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS;
|
||||||
|
const uint32_t test_delay_margin_ms = 50; // ms toleransı
|
||||||
|
|
||||||
|
bool g_is_component_initialized = false;
|
||||||
25
test_apps/main/test_common.h
Normal file
25
test_apps/main/test_common.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string.h> // For memset
|
||||||
|
#include "unity.h"
|
||||||
|
#include "relay_chn.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
// Test log tag
|
||||||
|
extern const char *TEST_TAG;
|
||||||
|
|
||||||
|
// GPIO konfigürasyonları
|
||||||
|
extern const gpio_num_t gpio_map[];
|
||||||
|
extern const uint8_t gpio_count;
|
||||||
|
extern const uint8_t relay_chn_count;
|
||||||
|
|
||||||
|
// Config parametreleri
|
||||||
|
extern const uint32_t opposite_inertia_ms;
|
||||||
|
extern const uint32_t test_delay_margin_ms;
|
||||||
|
|
||||||
|
// Init durumu
|
||||||
|
extern bool g_is_component_initialized;
|
||||||
403
test_apps/main/test_relay_chn_core.c
Normal file
403
test_apps/main/test_relay_chn_core.c
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
#include "test_common.h"
|
||||||
|
|
||||||
|
|
||||||
|
// --- Initialization Tests ---
|
||||||
|
|
||||||
|
TEST_CASE("relay_chn_create handles invalid arguments", "[relay_chn][core]")
|
||||||
|
{
|
||||||
|
// 1. Test with NULL gpio_map
|
||||||
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(NULL, gpio_count));
|
||||||
|
|
||||||
|
// 2. Test with incorrect gpio_count (must be RELAY_CHN_COUNT * 2)
|
||||||
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(gpio_map, gpio_count - 1));
|
||||||
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(gpio_map, 1));
|
||||||
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(gpio_map, 0));
|
||||||
|
|
||||||
|
// 3. Test with invalid GPIO numbers (GPIO_NUM_MAX is an invalid GPIO for output)
|
||||||
|
gpio_num_t invalid_gpio_map[] = {GPIO_NUM_4, GPIO_NUM_MAX, GPIO_NUM_18, GPIO_NUM_19};
|
||||||
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(invalid_gpio_map, gpio_count));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Basic Functionality Tests ---
|
||||||
|
|
||||||
|
// TEST_CASE: Test that relay channels initialize correctly to RELAY_CHN_STATE_FREE
|
||||||
|
TEST_CASE("Relay channels initialize correctly to FREE state", "[relay_chn][core]") {
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test that relays do nothing when an invlid channel id given
|
||||||
|
TEST_CASE("Run forward does nothing if channel id is invalid", "[relay_chn][core]") {
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
int invalid_id = relay_chn_count * 2 + i;
|
||||||
|
relay_chn_run_forward(invalid_id); // relay_chn_run_forward returns void
|
||||||
|
// Short delay for state to update
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test that relays run in the forward direction and update their state
|
||||||
|
TEST_CASE("Relay channels run forward and update state", "[relay_chn][core]") {
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
relay_chn_run_forward(i); // relay_chn_run_forward returns void
|
||||||
|
// Short delay for state to update
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test that relays do nothing when an invlid channel id given
|
||||||
|
TEST_CASE("Run reverse does nothing if channel id is invalid", "[relay_chn][core]") {
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// Verify that no valid channels were affected
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
int invalid_id = relay_chn_count * 2 + i;
|
||||||
|
// Call run_reverse with an invalid ID
|
||||||
|
relay_chn_run_reverse(invalid_id);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test that relays run in the reverse direction and update their state
|
||||||
|
TEST_CASE("Relay channels run reverse and update state", "[relay_chn][core]") {
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
relay_chn_run_reverse(i); // relay_chn_run_reverse returns void
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ### Broadcast Command (RELAY_CHN_ID_ALL) Tests
|
||||||
|
|
||||||
|
TEST_CASE("run_forward with ID_ALL sets all channels to FORWARD", "[relay_chn][core][id_all]")
|
||||||
|
{
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
relay_chn_run_forward(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("run_reverse with ID_ALL sets all channels to REVERSE", "[relay_chn][core][id_all]")
|
||||||
|
{
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
relay_chn_run_reverse(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("stop with ID_ALL stops all running channels", "[relay_chn][core][id_all]")
|
||||||
|
{
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// 1. Start all channels forward to ensure they are in a known running state
|
||||||
|
relay_chn_run_forward(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
|
||||||
|
// 2. Stop all channels using the broadcast command
|
||||||
|
relay_chn_stop(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
|
||||||
|
// 3. Verify all channels have transitioned to the FREE state
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TEST_CASE: Test that relays stop and transition to RELAY_CHN_STATE_FREE
|
||||||
|
// This test also verifies the transition to FREE state after a STOP command.
|
||||||
|
TEST_CASE("Relay channels stop and update to FREE state", "[relay_chn][core]") {
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
// First, run forward to test stopping and transitioning to FREE state
|
||||||
|
relay_chn_run_forward(i); // relay_chn_run_forward returns void
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(i));
|
||||||
|
|
||||||
|
// Now, issue the stop command
|
||||||
|
relay_chn_stop(i); // relay_chn_stop returns void
|
||||||
|
// Immediately after stop, state should be STOPPED
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
|
||||||
|
|
||||||
|
// Then, wait for the inertia period for it to transition to RELAY_CHN_STATE_FREE
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Get state should return UNDEFINED when id is not valid
|
||||||
|
TEST_CASE("Get state returns UNDEFINED when id is invalid", "[relay_chn][core]") {
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
int invalid_id = relay_chn_count * 2 + i;
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_UNDEFINED, relay_chn_get_state(invalid_id));
|
||||||
|
}
|
||||||
|
// Test for running states also
|
||||||
|
relay_chn_run_forward(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
int invalid_id = relay_chn_count * 2 + i;
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_UNDEFINED, relay_chn_get_state(invalid_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Get state string should return "UNKNOWN" when id is not valid
|
||||||
|
TEST_CASE("Get state string returns UNKNOWN when id is invalid", "[relay_chn][core]") {
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
int invalid_id = relay_chn_count * 2 + i;
|
||||||
|
TEST_ASSERT_EQUAL_STRING("UNKNOWN", relay_chn_get_state_str(invalid_id));
|
||||||
|
}
|
||||||
|
// Test for running states also
|
||||||
|
relay_chn_run_forward(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
int invalid_id = relay_chn_count * 2 + i;
|
||||||
|
TEST_ASSERT_EQUAL_STRING("UNKNOWN", relay_chn_get_state_str(invalid_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test independent operation of multiple relay channels
|
||||||
|
TEST_CASE("Multiple channels can operate independently", "[relay_chn][core]") {
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
if (relay_chn_count >= 2) {
|
||||||
|
// Start Channel 0 in forward direction
|
||||||
|
relay_chn_run_forward(0); // relay_chn_run_forward returns void
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(0));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(1)); // Other channel should not be affected
|
||||||
|
|
||||||
|
// Start Channel 1 in reverse direction
|
||||||
|
relay_chn_run_reverse(1); // relay_chn_run_reverse returns void
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(0));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(1));
|
||||||
|
|
||||||
|
// Stop Channel 0 and wait for it to become FREE
|
||||||
|
relay_chn_stop(0); // relay_chn_stop returns void
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(0));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(1)); // Other channel should continue running
|
||||||
|
|
||||||
|
// Stop Channel 1 and wait for it to become FREE
|
||||||
|
relay_chn_stop(1); // relay_chn_stop returns void
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(0));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(1));
|
||||||
|
} else {
|
||||||
|
ESP_LOGW("TEST", "Skipping 'Multiple channels can operate independently' test: Not enough channels available.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ### Inertia and State Transition Tests
|
||||||
|
|
||||||
|
// This section specifically targets the inertia periods and complex state transitions as per the component's logic.
|
||||||
|
|
||||||
|
// TEST_CASE: Test transition from forward to reverse with inertia and state checks
|
||||||
|
// Scenario: RELAY_CHN_STATE_FORWARD -> (relay_chn_run_reverse) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_REVERSE
|
||||||
|
TEST_CASE("Forward to Reverse transition with opposite inertia", "[relay_chn][core][inertia]") {
|
||||||
|
uint8_t ch = 0; // Channel to test
|
||||||
|
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// 1. Start in forward direction
|
||||||
|
relay_chn_run_forward(ch); // relay_chn_run_forward returns void
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); // Short delay for state stabilization
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// 2. Issue reverse command
|
||||||
|
relay_chn_run_reverse(ch); // relay_chn_run_reverse returns void
|
||||||
|
// Immediately after the command, the motor should be stopped
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE_PENDING, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// 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(ch)); // Should now be in reverse state
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test transition from reverse to forward with inertia and state checks
|
||||||
|
// Scenario: RELAY_CHN_STATE_REVERSE -> (relay_chn_run_forward) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_FORWARD
|
||||||
|
TEST_CASE("Reverse to Forward transition with opposite inertia", "[relay_chn][core][inertia]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// 1. Start in reverse direction
|
||||||
|
relay_chn_run_reverse(ch); // relay_chn_run_reverse returns void
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// 2. Issue forward command
|
||||||
|
relay_chn_run_forward(ch); // relay_chn_run_forward returns void
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD_PENDING, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// Wait for inertia
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test issuing the same run command while already running (no inertia expected)
|
||||||
|
// Scenario: RELAY_CHN_STATE_FORWARD -> (relay_chn_run_forward) -> RELAY_CHN_STATE_FORWARD
|
||||||
|
TEST_CASE("Running in same direction does not incur inertia", "[relay_chn][core][inertia]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// 1. Start in forward direction
|
||||||
|
relay_chn_run_forward(ch); // relay_chn_run_forward returns void
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// 2. Issue the same forward command again
|
||||||
|
relay_chn_run_forward(ch); // relay_chn_run_forward returns void
|
||||||
|
// As per the code, is_direction_opposite_to_current_motion should return false, so no inertia.
|
||||||
|
// Just a short delay to check state remains the same.
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test transition from FREE state to running (no inertia expected)
|
||||||
|
// Scenario: RELAY_CHN_STATE_FREE -> (relay_chn_run_forward) -> RELAY_CHN_STATE_FORWARD
|
||||||
|
TEST_CASE("FREE to Running transition without inertia", "[relay_chn][core][inertia]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// setUp() should have already brought the channel to FREE state
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// Start in forward direction
|
||||||
|
relay_chn_run_forward(ch); // relay_chn_run_forward returns void
|
||||||
|
// No inertia is expected when starting from FREE state.
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### Direction Flipping Tests
|
||||||
|
|
||||||
|
TEST_CASE("Single channel direction can be flipped", "[relay_chn][core][direction]")
|
||||||
|
{
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
const uint8_t ch = 0;
|
||||||
|
|
||||||
|
// 1. Initial direction should be default
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_DEFAULT, relay_chn_get_direction(ch));
|
||||||
|
|
||||||
|
// 2. Flip the direction
|
||||||
|
relay_chn_flip_direction(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms)); // Wait for flip inertia
|
||||||
|
|
||||||
|
// 3. Verify direction is flipped
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_FLIPPED, relay_chn_get_direction(ch));
|
||||||
|
|
||||||
|
// 4. Flip back
|
||||||
|
relay_chn_flip_direction(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms)); // Wait for flip inertia
|
||||||
|
|
||||||
|
// 5. Verify direction is back to default
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_DEFAULT, relay_chn_get_direction(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("All channels direction can be flipped simultaneously", "[relay_chn][core][direction][id_all]")
|
||||||
|
{
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// 1. Flip all channels
|
||||||
|
relay_chn_flip_direction(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
|
||||||
|
// 2. Verify all channels are flipped
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_FLIPPED, relay_chn_get_direction(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Flip all back
|
||||||
|
relay_chn_flip_direction(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
|
||||||
|
// 4. Verify all channels are back to default
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_DEFAULT, relay_chn_get_direction(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Flipping a running channel stops it and flips direction", "[relay_chn][core][direction]")
|
||||||
|
{
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
const uint8_t ch = 0;
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
// 4. Wait for the flip inertia to pass, after which it should be FREE and FLIPPED
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(ch));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_FLIPPED, relay_chn_get_direction(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Direction flip handles invalid channel ID gracefully", "[relay_chn][core][direction]")
|
||||||
|
{
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
const uint8_t invalid_ch = relay_chn_count + 5;
|
||||||
|
|
||||||
|
relay_chn_flip_direction(invalid_ch); // Call with an invalid ID
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_DEFAULT, relay_chn_get_direction(invalid_ch));
|
||||||
|
}
|
||||||
135
test_apps/main/test_relay_chn_listener.c
Normal file
135
test_apps/main/test_relay_chn_listener.c
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#include "test_common.h"
|
||||||
|
|
||||||
|
|
||||||
|
// --- Listener Test Globals ---
|
||||||
|
typedef struct {
|
||||||
|
uint8_t chn_id;
|
||||||
|
relay_chn_state_t old_state;
|
||||||
|
relay_chn_state_t new_state;
|
||||||
|
int call_count;
|
||||||
|
} listener_callback_info_t;
|
||||||
|
|
||||||
|
static listener_callback_info_t listener1_info;
|
||||||
|
static listener_callback_info_t listener2_info;
|
||||||
|
|
||||||
|
// --- Listener Test Helper Functions ---
|
||||||
|
|
||||||
|
// Clear the memory from possible garbage values
|
||||||
|
static void reset_listener_info(listener_callback_info_t* info) {
|
||||||
|
memset(info, 0, sizeof(listener_callback_info_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_listener_1(uint8_t chn_id, relay_chn_state_t old_state, relay_chn_state_t new_state) {
|
||||||
|
listener1_info.chn_id = chn_id;
|
||||||
|
listener1_info.old_state = old_state;
|
||||||
|
listener1_info.new_state = new_state;
|
||||||
|
listener1_info.call_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_listener_2(uint8_t chn_id, relay_chn_state_t old_state, relay_chn_state_t new_state) {
|
||||||
|
listener2_info.chn_id = chn_id;
|
||||||
|
listener2_info.old_state = old_state;
|
||||||
|
listener2_info.new_state = new_state;
|
||||||
|
listener2_info.call_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### Listener Functionality Tests
|
||||||
|
|
||||||
|
TEST_CASE("Listener is called on state change", "[relay_chn][listener]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
reset_listener_info(&listener1_info);
|
||||||
|
|
||||||
|
// 1. Register the listener
|
||||||
|
TEST_ESP_OK(relay_chn_register_listener(test_listener_1));
|
||||||
|
|
||||||
|
// 2. Trigger a state change
|
||||||
|
relay_chn_run_forward(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); // Allow event to be processed
|
||||||
|
|
||||||
|
// 3. Verify the listener was called with correct parameters
|
||||||
|
TEST_ASSERT_EQUAL(1, listener1_info.call_count);
|
||||||
|
TEST_ASSERT_EQUAL(ch, listener1_info.chn_id);
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, listener1_info.old_state);
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, listener1_info.new_state);
|
||||||
|
|
||||||
|
// 4. Unregister to clean up
|
||||||
|
relay_chn_unregister_listener(test_listener_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Unregistered listener is not called", "[relay_chn][listener]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
reset_listener_info(&listener1_info);
|
||||||
|
|
||||||
|
// 1. Register and then immediately unregister the listener
|
||||||
|
TEST_ESP_OK(relay_chn_register_listener(test_listener_1));
|
||||||
|
relay_chn_unregister_listener(test_listener_1);
|
||||||
|
|
||||||
|
// 2. Trigger a state change
|
||||||
|
relay_chn_run_forward(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
|
||||||
|
// 3. Verify the listener was NOT called
|
||||||
|
TEST_ASSERT_EQUAL(0, listener1_info.call_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Multiple listeners are called on state change", "[relay_chn][listener]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
reset_listener_info(&listener1_info);
|
||||||
|
reset_listener_info(&listener2_info);
|
||||||
|
|
||||||
|
// 1. Register two different listeners
|
||||||
|
TEST_ESP_OK(relay_chn_register_listener(test_listener_1));
|
||||||
|
TEST_ESP_OK(relay_chn_register_listener(test_listener_2));
|
||||||
|
|
||||||
|
// 2. Trigger a state change
|
||||||
|
relay_chn_run_forward(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
|
||||||
|
// 3. Verify listener 1 was called correctly
|
||||||
|
TEST_ASSERT_EQUAL(1, listener1_info.call_count);
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, listener1_info.old_state);
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, listener1_info.new_state);
|
||||||
|
|
||||||
|
// 4. Verify listener 2 was also called correctly
|
||||||
|
TEST_ASSERT_EQUAL(1, listener2_info.call_count);
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, listener2_info.old_state);
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, listener2_info.new_state);
|
||||||
|
|
||||||
|
// 5. Clean up
|
||||||
|
relay_chn_unregister_listener(test_listener_1);
|
||||||
|
relay_chn_unregister_listener(test_listener_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Listener registration handles invalid arguments and duplicates", "[relay_chn][listener]") {
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
reset_listener_info(&listener1_info);
|
||||||
|
|
||||||
|
// 1. Registering a NULL listener should fail
|
||||||
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_register_listener(NULL));
|
||||||
|
|
||||||
|
// 2. Unregistering a NULL listener should not crash
|
||||||
|
relay_chn_unregister_listener(NULL);
|
||||||
|
|
||||||
|
// 3. Registering the same listener twice should be handled gracefully
|
||||||
|
TEST_ESP_OK(relay_chn_register_listener(test_listener_1));
|
||||||
|
TEST_ESP_OK(relay_chn_register_listener(test_listener_1)); // Second call should be a no-op
|
||||||
|
|
||||||
|
// 4. Trigger a state change and verify the listener is only called ONCE
|
||||||
|
relay_chn_run_forward(0);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(1, listener1_info.call_count);
|
||||||
|
|
||||||
|
// 5. Clean up
|
||||||
|
relay_chn_unregister_listener(test_listener_1);
|
||||||
|
}
|
||||||
389
test_apps/main/test_relay_chn_tilt.c
Normal file
389
test_apps/main/test_relay_chn_tilt.c
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
#include "test_common.h"
|
||||||
|
|
||||||
|
|
||||||
|
// ### Tilt Functionality Tests (Conditional)
|
||||||
|
|
||||||
|
// This section will only be compiled if **`CONFIG_RELAY_CHN_ENABLE_TILTING`** is defined as **`1`** in `sdkconfig`.
|
||||||
|
|
||||||
|
#ifndef CONFIG_RELAY_CHN_ENABLE_TILTING
|
||||||
|
#error "This test requires CONFIG_RELAY_CHN_ENABLE_TILTING"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RELAY_CHN_CMD_FORWARD 1
|
||||||
|
#define RELAY_CHN_CMD_REVERSE 2
|
||||||
|
|
||||||
|
// Helper function to prepare channel for tilt tests
|
||||||
|
void prepare_channel_for_tilt(uint8_t chn_id, int initial_cmd) {
|
||||||
|
// Ensure the channel has had a 'last_run_cmd'
|
||||||
|
if (initial_cmd == RELAY_CHN_CMD_FORWARD) {
|
||||||
|
relay_chn_run_forward(chn_id);
|
||||||
|
} else { // Assuming initial_cmd is RELAY_CHN_CMD_REVERSE
|
||||||
|
relay_chn_run_reverse(chn_id);
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); // Allow command to process
|
||||||
|
relay_chn_stop(chn_id); // Stop it to set last_run_cmd but return to FREE for next test
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(chn_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test transition from running forward to tilt forward
|
||||||
|
// Scenario: RELAY_CHN_STATE_FORWARD -> (relay_chn_tilt_forward) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_TILT_FORWARD
|
||||||
|
TEST_CASE("Run Forward to Tilt Forward transition with inertia", "[relay_chn][tilt][inertia]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// Prepare channel by running forward first to set last_run_cmd
|
||||||
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
||||||
|
|
||||||
|
// 1. Start in forward direction
|
||||||
|
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. Issue tilt forward command
|
||||||
|
relay_chn_tilt_forward(ch);
|
||||||
|
// After tilt command, it should immediately stop and then trigger inertia.
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// Wait for the inertia period (after which the tilt command will be dispatched)
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test transition from running reverse to tilt reverse
|
||||||
|
// Scenario: RELAY_CHN_STATE_REVERSE -> (relay_chn_tilt_reverse) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_TILT_REVERSE
|
||||||
|
TEST_CASE("Run Reverse to Tilt Reverse transition with inertia", "[relay_chn][tilt][inertia]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// Prepare channel by running reverse first to set last_run_cmd
|
||||||
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_REVERSE);
|
||||||
|
|
||||||
|
// 1. Start in reverse direction
|
||||||
|
relay_chn_run_reverse(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// 2. Issue tilt reverse command
|
||||||
|
relay_chn_tilt_reverse(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test transition from FREE state to tilt forward (now with preparation)
|
||||||
|
// Scenario: RELAY_CHN_STATE_FREE -> (prepare) -> RELAY_CHN_STATE_FREE -> (relay_chn_tilt_forward) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_TILT_FORWARD
|
||||||
|
TEST_CASE("FREE to Tilt Forward transition with inertia (prepared)", "[relay_chn][tilt][inertia]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// Prepare channel by running forward first to set last_run_cmd
|
||||||
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(ch)); // Ensure we are back to FREE
|
||||||
|
|
||||||
|
// Issue tilt forward command
|
||||||
|
relay_chn_tilt_forward(ch);
|
||||||
|
// From FREE state, tilt command should still incur the inertia due to the internal timer logic
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test transition from FREE state to tilt reverse (now with preparation)
|
||||||
|
// Scenario: RELAY_CHN_STATE_FREE -> (prepare) -> RELAY_CHN_STATE_FREE -> (relay_chn_tilt_reverse) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_TILT_REVERSE
|
||||||
|
TEST_CASE("FREE to Tilt Reverse transition with inertia (prepared)", "[relay_chn][tilt][inertia]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// Prepare channel by running reverse first to set last_run_cmd
|
||||||
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_REVERSE);
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(ch)); // Ensure we are back to FREE
|
||||||
|
|
||||||
|
// Issue tilt reverse command
|
||||||
|
relay_chn_tilt_reverse(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test transition from tilt forward to run forward (inertia expected for run)
|
||||||
|
// Scenario: RELAY_CHN_STATE_TILT_FORWARD -> (relay_chn_run_forward) -> RELAY_CHN_STATE_FORWARD
|
||||||
|
TEST_CASE("Tilt Forward to Run Forward transition with inertia", "[relay_chn][tilt][inertia]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// Prepare channel by running forward first to set last_run_cmd, then tilt
|
||||||
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
||||||
|
relay_chn_tilt_forward(ch); // Go to tilt state
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// 2. Issue run forward command
|
||||||
|
relay_chn_run_forward(ch);
|
||||||
|
// From Tilt to Run in the same logical name but in the opposite direction, inertia is expected.
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD_PENDING, relay_chn_get_state(ch));
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test transition from tilt reverse to run reverse (no inertia expected for run)
|
||||||
|
// Scenario: RELAY_CHN_STATE_TILT_REVERSE -> (relay_chn_run_reverse) -> RELAY_CHN_STATE_REVERSE
|
||||||
|
TEST_CASE("Tilt Reverse to Run Reverse transition with inertia", "[relay_chn][tilt][inertia]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// Prepare channel by running reverse first to set last_run_cmd, then tilt
|
||||||
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_REVERSE);
|
||||||
|
relay_chn_tilt_reverse(ch); // Go to tilt state
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// 2. Issue run reverse command
|
||||||
|
relay_chn_run_reverse(ch);
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE_PENDING, relay_chn_get_state(ch));
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test transition from tilt forward to run reverse (without inertia)
|
||||||
|
// Scenario: RELAY_CHN_STATE_TILT_FORWARD -> (relay_chn_run_reverse) -> RELAY_CHN_STATE_REVERSE
|
||||||
|
TEST_CASE("Tilt Forward to Run Reverse transition without inertia", "[relay_chn][tilt][inertia]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// Prepare channel by running forward first to set last_run_cmd, then tilt
|
||||||
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
||||||
|
relay_chn_tilt_forward(ch); // Go to tilt state
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// 2. Issue run reverse command (opposite direction)
|
||||||
|
relay_chn_run_reverse(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST_CASE: Test stopping from a tilt state (no inertia for stop command itself)
|
||||||
|
// Scenario: RELAY_CHN_STATE_TILT_FORWARD -> (relay_chn_stop) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_FREE
|
||||||
|
TEST_CASE("Tilt to Stop transition without immediate inertia for stop", "[relay_chn][tilt][inertia]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// Prepare channel by running forward first to set last_run_cmd, then tilt
|
||||||
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
||||||
|
relay_chn_tilt_forward(ch); // Go to tilt state
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// 2. Issue stop command
|
||||||
|
relay_chn_stop(ch);
|
||||||
|
// Stop command should apply immediately, setting state to FREE since last state was tilt.
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### Tilt Broadcast Command (RELAY_CHN_ID_ALL) Tests
|
||||||
|
|
||||||
|
TEST_CASE("tilt_forward with ID_ALL sets all channels to TILT_FORWARD", "[relay_chn][tilt][id_all]")
|
||||||
|
{
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// 1. Prepare all channels.
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
prepare_channel_for_tilt(i, RELAY_CHN_CMD_FORWARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Issue tilt forward to all channels
|
||||||
|
relay_chn_tilt_forward(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); // Tilt from FREE doesn't have stop-inertia
|
||||||
|
|
||||||
|
// 3. Verify all channels are tilting forward
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("tilt_reverse with ID_ALL sets all channels to TILT_REVERSE", "[relay_chn][tilt][id_all]")
|
||||||
|
{
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// 1. Prepare all channels.
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
prepare_channel_for_tilt(i, RELAY_CHN_CMD_REVERSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Issue tilt reverse to all channels
|
||||||
|
relay_chn_tilt_reverse(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
|
||||||
|
// 3. Verify all channels are tilting reverse
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("tilt_stop with ID_ALL stops all tilting channels", "[relay_chn][tilt][id_all]")
|
||||||
|
{
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// 1. Prepare and start all channels tilting forward
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
prepare_channel_for_tilt(i, RELAY_CHN_CMD_REVERSE);
|
||||||
|
}
|
||||||
|
relay_chn_tilt_forward(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
|
||||||
|
// 2. Stop tilting on all channels
|
||||||
|
relay_chn_tilt_stop(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
|
||||||
|
// 3. Verify all channels are free
|
||||||
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("tilt_auto with ID_ALL tilts channels based on last run direction", "[relay_chn][tilt][id_all]")
|
||||||
|
{
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// This test requires at least 2 channels to demonstrate different behaviors
|
||||||
|
TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(2, relay_chn_count, "Test requires at least 2 channels");
|
||||||
|
|
||||||
|
// 1. Prepare channel 0 with last run FORWARD and channel 1 with last run REVERSE
|
||||||
|
prepare_channel_for_tilt(0, RELAY_CHN_CMD_FORWARD);
|
||||||
|
prepare_channel_for_tilt(1, RELAY_CHN_CMD_REVERSE);
|
||||||
|
|
||||||
|
// 2. Issue auto tilt command to all channels
|
||||||
|
relay_chn_tilt_auto(RELAY_CHN_ID_ALL);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); // Tilt from FREE state is dispatched immediately
|
||||||
|
|
||||||
|
// 3. Verify channel 0 tilts forward (last run was forward) and channel 1 tilts reverse (last run was reverse)
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(0));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test relay_chn_tilt_auto() chooses correct tilt direction
|
||||||
|
TEST_CASE("relay_chn_tilt_auto chooses correct direction", "[relay_chn][tilt][auto]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
// Prepare FORWARD
|
||||||
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
||||||
|
relay_chn_tilt_auto(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
||||||
|
relay_chn_tilt_stop(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
|
||||||
|
// Prepare REVERSE
|
||||||
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_REVERSE);
|
||||||
|
relay_chn_tilt_auto(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test sensitivity set/get
|
||||||
|
TEST_CASE("relay_chn_tilt_sensitivity_set and get", "[relay_chn][tilt][sensitivity]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
uint8_t val = 0;
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
relay_chn_tilt_sensitivity_set(ch, 0);
|
||||||
|
TEST_ESP_OK(relay_chn_tilt_sensitivity_get(ch, &val, 1));
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, val);
|
||||||
|
|
||||||
|
relay_chn_tilt_sensitivity_set(ch, 50);
|
||||||
|
TEST_ESP_OK(relay_chn_tilt_sensitivity_get(ch, &val, 1));
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(50, val);
|
||||||
|
|
||||||
|
relay_chn_tilt_sensitivity_set(ch, 100);
|
||||||
|
TEST_ESP_OK(relay_chn_tilt_sensitivity_get(ch, &val, 1));
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(100, val);
|
||||||
|
|
||||||
|
// Set all channels
|
||||||
|
relay_chn_tilt_sensitivity_set(RELAY_CHN_ID_ALL, 42);
|
||||||
|
uint8_t vals[CONFIG_RELAY_CHN_COUNT] = {0};
|
||||||
|
TEST_ESP_OK(relay_chn_tilt_sensitivity_get(RELAY_CHN_ID_ALL, vals, relay_chn_count));
|
||||||
|
for (int i = 0; i < relay_chn_count; ++i) {
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(42, vals[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test tilt counter logic: forward x3, reverse x3, extra reverse fails
|
||||||
|
TEST_CASE("tilt counter logic: forward and reverse consumption", "[relay_chn][tilt][counter]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
||||||
|
|
||||||
|
// Tilt forward 3 times
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
relay_chn_tilt_forward(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
||||||
|
relay_chn_tilt_stop(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now tilt reverse 3 times (should succeed)
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
relay_chn_tilt_reverse(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
if (i < 3) {
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state(ch));
|
||||||
|
relay_chn_tilt_stop(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra reverse tilt should fail (counter exhausted)
|
||||||
|
relay_chn_tilt_reverse(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
// Should not enter TILT_REVERSE, should remain FREE or STOPPED
|
||||||
|
relay_chn_state_t state = relay_chn_get_state(ch);
|
||||||
|
TEST_ASSERT(state != RELAY_CHN_STATE_TILT_REVERSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test run command during TILT state
|
||||||
|
TEST_CASE("run command during TILT state transitions correctly", "[relay_chn][tilt][run-during-tilt]") {
|
||||||
|
uint8_t ch = 0;
|
||||||
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
||||||
|
g_is_component_initialized = true;
|
||||||
|
|
||||||
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
||||||
|
relay_chn_tilt_forward(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
||||||
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
||||||
|
|
||||||
|
// Issue run reverse while in TILT_FORWARD
|
||||||
|
relay_chn_run_reverse(ch);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
||||||
|
// Should transition to REVERSE or REVERSE_PENDING depending on inertia logic
|
||||||
|
relay_chn_state_t state = relay_chn_get_state(ch);
|
||||||
|
TEST_ASSERT(state == RELAY_CHN_STATE_REVERSE || state == RELAY_CHN_STATE_REVERSE_PENDING);
|
||||||
|
}
|
||||||
1400
test_apps/sdkconfig
Normal file
1400
test_apps/sdkconfig
Normal file
File diff suppressed because it is too large
Load Diff
8
test_apps/sdkconfig.defaults
Normal file
8
test_apps/sdkconfig.defaults
Normal 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=2
|
||||||
|
CONFIG_RELAY_CHN_ENABLE_TILTING=y
|
||||||
1400
test_apps/sdkconfig.old
Normal file
1400
test_apps/sdkconfig.old
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user