22 Commits

Author SHA1 Message Date
5e92e0477b Merge pull request 'Merge main with the latest changes and its tag into dev' (!40) from main into dev
Reviewed-on: #40
2025-09-13 12:04:40 +03:00
fafc23591a Merge pull request 'release-1.0.0' (!39) from release-1.0.0 into main
Reviewed-on: #39
2025-09-13 11:55:48 +03:00
497f45e336 Fix and optimize tilt test case issues
Tilt tests started to fail after the latest changes. Had to make
some fixes and optimizations so that the test code resets the
tilt controls correctly and aligns with relay_chn's timing requirements.

Fixes #1115
2025-09-13 09:54:12 +03:00
95ca976bc6 Allow changing log level dynamically 2025-09-13 09:46:10 +03:00
300f9a1317 Fix unused variable warning
TAG constant is only used when run limit is enabled. As a result,
the compiler generates an "unused variable" warning for other cases.
Fixed this warning by adding the unused attribute.
2025-09-13 09:45:13 +03:00
5440440c4d Untrack autogenerated sdkconfig.old file 2025-09-13 09:39:24 +03:00
8416187d86 Fix test_apps directory issue
Fixed test_apps directory locating issue by fixing the path to
"project_root/test_apps" and removed find command since it
searches recursively and founds the similar directories in
managed_components directory. Also added current time to the output.
2025-09-12 15:10:37 +03:00
9f45a2310d Update and enrich the manifest file 2025-09-12 10:27:46 +03:00
a1ff54b6e9 Bump versions to 1.0.0 2025-09-12 09:39:16 +03:00
c718a1380f Merge pull request 'feat/1104-add-examples' (!38) from feat/1104-add-examples into dev
Reviewed-on: #38
2025-09-11 16:44:19 +03:00
6caa4f1bd5 Ignore session specific settings files 2025-09-11 16:03:29 +03:00
4edebf206e Add a multi channel example
Added a multi channel example with run limit, tilting and channel selection
features. Refs #1104 and closes #1111.
2025-09-11 15:50:34 +03:00
e30b445b91 Fix channel validity check
Fixed mistaken channel validity check in `tilt_stop` funciton
in multi mode. Refs #1111 and fixes #1114.
2025-09-11 14:21:24 +03:00
9ee974e677 Add a single channel example
Added a single channel example with run limit and tilting
features. Refs #1104 and closes #1105.
2025-09-09 18:15:32 +03:00
31e351a129 Fix tilting opposite direction when running
Fixed TILT_STOP command issuing chain that was causing a running channel
to be stopped when an opposite direction tilting requested. For exeample:
RUN_FORWARD > TILT_REVERSE. Refs #1105 and fixes #1110.
2025-09-09 17:50:02 +03:00
3ce079c2e8 Fix unwanted reverse tilts
Added a helper function to determine if the channel may perform
the requested tilt command. Refs #1105 and fixes #1109.
2025-09-09 17:09:08 +03:00
a5b320c152 Add state to string API function
Added a state to string public API function. There was already
a private function (`relay_chn_state_str`) that provides
this functionality. So this function has been renamed to
`relay_chn_state_to_str` and made publicly available.

Refs #1104, #1105 and closes #1108.
2025-09-09 10:53:56 +03:00
087deb338e Fix STOP command issuing when idle
Fixed almost unconditional STOP command issuing when the
channel is idle. Refs #1104, #1105 and closes #1107.
2025-09-09 09:26:24 +03:00
fbf8b5dfc8 Fix mispelled config parameter
Fixed a mispelled configuration parameter. Refs #1105
2025-09-09 09:14:28 +03:00
a3f83eaaee Merge pull request 'opt/1085-optimization-and-cleanup' (!37) from opt/1085-optimization-and-cleanup into dev
Reviewed-on: #37
2025-09-05 11:05:41 +03:00
61ca2197e1 Fix conditional compilation issues.
Fixed include directory settings and conditional includes in CMakeLists and test_common.c. Refs #1085.
2025-09-04 18:23:05 +03:00
86cc29a33b Fix static variable names
Fixed static variable names according to the ESP-IDF C code formatting guide.

Refs #1085 and fixes #1103
2025-09-04 18:20:51 +03:00
46 changed files with 1681 additions and 1695 deletions

4
.gitignore vendored
View File

@@ -59,6 +59,7 @@ tools/test_apps/**/sdkconfig.old
# autogenerated config files # autogenerated config files
sdkconfig sdkconfig
test_apps/sdkconfig test_apps/sdkconfig
test_apps/sdkconfig.old
TEST_LOGS/ TEST_LOGS/
build_summary_*.xml build_summary_*.xml
@@ -73,6 +74,7 @@ test_multi_heap_host
# VS Code Settings # VS Code Settings
# .vscode/ # .vscode/
settings.json
# VIM files # VIM files
*.swp *.swp
@@ -112,4 +114,4 @@ XUNIT_RESULT*.xml
.clangd .clangd
# Vale # Vale
.vale/styles/* .vale/styles/*

View File

@@ -1,3 +0,0 @@
{
"C_Cpp.intelliSenseEngine": "default"
}

View File

@@ -16,7 +16,7 @@ else()
list(APPEND srcs "src/relay_chn_ctl_single.c") list(APPEND srcs "src/relay_chn_ctl_single.c")
endif() endif()
if(CONFIG_RELAY_CHN_NVS) if(CONFIG_RELAY_CHN_ENABLE_NVS)
list(APPEND srcs "src/relay_chn_nvs.c") list(APPEND srcs "src/relay_chn_nvs.c")
endif() endif()

View File

@@ -130,7 +130,7 @@ dependencies:
# Add as a custom component from git repository # Add as a custom component from git repository
relay_chn: relay_chn:
git: https://git.kozmotronik.com.tr/KozmotronikTech/relay_chn.git git: https://git.kozmotronik.com.tr/KozmotronikTech/relay_chn.git
version: '>=0.5.0' version: '>=1.0.0'
``` ```
## Usage ## Usage

6
examples/relay_chn_multi/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
build/
sdkconfig
sdkconfig.old
# Exclude auto-populated settings file
settings.json

View File

@@ -0,0 +1,23 @@
{
"configurations": [
{
"name": "ESP-IDF",
"compilerPath": "${config:idf.toolsPath}/tools/riscv32-esp-elf/esp-14.2.0_20241119/riscv32-esp-elf/bin/riscv32-esp-elf-gcc",
"compileCommands": "/disk/Projeler/ESP-Components/relay_chn/examples/relay_chn_single/build/compile_commands.json",
"includePath": [
"${config:idf.espIdfPath}/components/**",
"${config:idf.espIdfPathWin}/components/**",
"${workspaceFolder}/**"
],
"browse": {
"path": [
"${config:idf.espIdfPath}/components",
"${config:idf.espIdfPathWin}/components",
"${workspaceFolder}"
],
"limitSymbolsToIncludedHeaders": true
}
}
],
"version": 4
}

View File

@@ -0,0 +1,15 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "gdbtarget",
"request": "attach",
"name": "Eclipse CDT GDB Adapter"
},
{
"type": "espidf",
"name": "Launch",
"request": "launch"
}
]
}

View File

@@ -0,0 +1,10 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five 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(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(relay_chn_single)

View File

@@ -0,0 +1,176 @@
# Relay Channel Multi Example
## Introduction
This example demonstrates how to use the relay channel component to control a 3-channel setup with button inputs and LED status indication. It showcases:
- Basic relay channel operations (forward/reverse running, stopping)
- Secondary operations (tilting, direction flipping)
- State change event handling with a listener
- Relay channel run limit
- Button event handling using esp-iot-solution's button component
- Visual feedback using esp-iot-solution's LED indicator component
## How to Use Example
This example has been tested on an `ESP32-C3-DevKitM-1U` board. However, it can be adapted to any ESP32-based board with at least six available GPIO pins by adjusting the configuration options.
### Hardware Required
* An ESP32-based development board
* 2 relays connected to GPIO pins (default: GPIO4, GPIO5)
* 3 buttons connected to GPIO pins:
- UP button (default: GPIO0)
- DOWN button (default: GPIO1)
- STOP button (default: GPIO2)
- SELECT button (default: GPIO3)
* LED indicators for status indication of each channel (default: GPIO4, GPIO10 and GPIO9 respectively)
#### Hardware Schematic
Relay blocks are ommitted for simplicity. You can refer to schematic of the [Single-channel example](/examples/relay_chn_single/README.md) for a fully implemented relay block.
> [!NOTE]
> A single relay channel consists of two relay block and two GPIO pins.
![Hardware Schematic](example_schematic.png)
### Configuration
The example can be configured through `menuconfig` under "Relay Channel Multi Example Configuration":
1. Button active level (`EXAMPLE_RLCHN_BTN_ACTIVE_LEVEL`)
- Select between active LOW or HIGH logic level for buttons
2. GPIO assignments:
- UP button pin (`EXAMPLE_RLCHN_BTN_UP_IO_NUM`, default: 0)
- DOWN button pin (`EXAMPLE_RLCHN_BTN_DOWN_IO_NUM`, default: 1)
- STOP button pin (`EXAMPLE_RLCHN_BTN_STOP_IO_NUM`, default: 2)
- SELECT button pin (`EXAMPLE_RLCHN_BTN_SELECT_IO_NUM`, default: 3)
- LED indicator pin (`EXAMPLE_RLCHN_LED_INDICATOR1_IO_NUM`, default: 4)
- LED indicator pin (`EXAMPLE_RLCHN_LED_INDICATOR2_IO_NUM`, default: 10)
- LED indicator pin (`EXAMPLE_RLCHN_LED_INDICATOR3_IO_NUM`, default: 9)
3. Long press timing:
- `EXAMPLE_RLCHN_BTN_LONG_PRESS_TIME_MS`: Duration for long press actions (1500-3000ms, default: 2000ms)
### Button Operations
The example uses esp-iot-solution's `button` component to handle the following operations:
- **UP button**:
* Short press: Start forward movement
* Long press: Start forward tilt (stops on release)
- **DOWN button**:
* Short press: Start reverse movement
* Long press: Start reverse tilt (stops on release)
- **STOP button**:
* Short press: Stop movement
* Long press: Flip movement direction
- **SELECT button**:
* Short press: Selects a channel to operate
* Long press: Selects all channels to operate in batch
### LED Indicator States
The example uses esp-iot-solution's `led_indicator` component to show different states:
- **Running**: LED blinks at 300ms on, 100ms off
- **Tilting**: Fast blink at 100ms on, 50ms off
- **Operation Success**: Two quick blinks
- **Operation Fail**: One long blink
### Dependencies
This example requires:
- ESP-IDF v4.1 or later
- esp-iot-solution components:
* button v4.1.1 or later
* led_indicator v1.1.1 or later
- relay_chn component
## Example Output
When the application boots, it will wait for a button event. Then ta state listener will print state changes.
```log
I (269) main_task: Calling app_main()
I (269) RELAY_CHN_MULTI_EXAMPLE: Initializing relay channel
I (279) gpio: GPIO[18]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (279) gpio: GPIO[19]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (289) gpio: GPIO[5]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (299) gpio: GPIO[6]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (309) gpio: GPIO[7]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (319) gpio: GPIO[8]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (329) RELAY_CHN_MULTI_EXAMPLE: Initializing buttons
I (329) RELAY_CHN_MULTI_EXAMPLE: Initializing buttons with active level: 0
I (339) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (339) button: IoT Button Version: 4.1.3
I (349) gpio: GPIO[1]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (359) gpio: GPIO[2]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (359) gpio: GPIO[3]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (369) RELAY_CHN_MULTI_EXAMPLE: Setting up button callbacks. Configured long press time: 2000 ms
I (379) RELAY_CHN_MULTI_EXAMPLE: Initializing LED indicator
I (389) gpio: GPIO[4]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (389) led_indicator: LED Indicator Version: 1.1.1
I (399) gpio: GPIO[4]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (409) led_indicator: Indicator create successfully. type:GPIO mode, hardware_data:0x3fc96498, blink_lists:custom
I (419) gpio: GPIO[10]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (429) led_indicator: LED Indicator Version: 1.1.1
I (429) gpio: GPIO[10]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (439) led_indicator: Indicator create successfully. type:GPIO mode, hardware_data:0x3fc965a4, blink_lists:custom
I (449) gpio: GPIO[9]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (459) led_indicator: LED Indicator Version: 1.1.1
I (459) gpio: GPIO[9]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (469) led_indicator: Indicator create successfully. type:GPIO mode, hardware_data:0x3fc966b0, blink_lists:custom
I (479) RELAY_CHN_MULTI_EXAMPLE: Relay Channel Multi Example is ready to operate
I (489) main_task: Returned from app_main()
I (3759) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from IDLE to FORWARD
I (6639) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from FORWARD to STOPPED
I (7439) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from STOPPED to IDLE
I (7439) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from IDLE to TILT_FORWARD
I (8119) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from TILT_FORWARD to IDLE
I (13579) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from IDLE to TILT_REVERSE
I (14419) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from TILT_REVERSE to IDLE
I (20319) RELAY_CHN_MULTI_EXAMPLE: Selected channel: 1
I (21479) RELAY_CHN_MULTI_EXAMPLE: Selected channel: 2
I (22229) RELAY_CHN_MULTI_EXAMPLE: Selected channel: 0
I (23169) RELAY_CHN_MULTI_EXAMPLE: Selected channel: 1
I (29039) RELAY_CHN_MULTI_EXAMPLE: Selected all channels
I (35419) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from IDLE to FORWARD
I (35419) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #1, from IDLE to FORWARD
I (35419) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #2, from IDLE to FORWARD
I (39349) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from FORWARD to STOPPED
I (39349) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #1, from FORWARD to STOPPED
I (39359) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #2, from FORWARD to STOPPED
I (40149) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from STOPPED to IDLE
I (40149) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from IDLE to TILT_FORWARD
I (40159) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #1, from STOPPED to IDLE
I (40169) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #1, from IDLE to TILT_FORWARD
I (40179) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #2, from STOPPED to IDLE
I (40189) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #2, from IDLE to TILT_FORWARD
I (41699) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from TILT_FORWARD to IDLE
I (41699) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #1, from TILT_FORWARD to IDLE
I (41709) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #2, from TILT_FORWARD to IDLE
I (64129) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from IDLE to TILT_REVERSE
I (64129) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #1, from IDLE to TILT_REVERSE
I (64139) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #2, from IDLE to TILT_REVERSE
I (68929) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from TILT_REVERSE to IDLE
I (68929) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #1, from TILT_REVERSE to IDLE
I (68939) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #2, from TILT_REVERSE to IDLE
I (280109) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from IDLE to FORWARD
I (280109) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #1, from IDLE to FORWARD
I (280109) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #2, from IDLE to FORWARD
I (283219) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from FORWARD to STOPPED
I (283219) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from STOPPED to REVERSE_PENDING
I (283229) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #1, from FORWARD to STOPPED
I (283239) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #1, from STOPPED to REVERSE_PENDING
I (283249) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #2, from FORWARD to STOPPED
I (283259) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #2, from STOPPED to REVERSE_PENDING
I (284019) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #0, from REVERSE_PENDING to REVERSE
I (284019) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #1, from REVERSE_PENDING to REVERSE
I (284029) RELAY_CHN_MULTI_EXAMPLE: example_event_listener: State change for #2, from REVERSE_PENDING to REVERSE
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "relay_chn_multi_main.c"
PRIV_REQUIRES button led_indicator relay_chn)

View File

@@ -0,0 +1,52 @@
menu "Relay Channel Multi Example Configuration"
choice EXAMPLE_RLCHN_BTN_ACTIVE_LEVEL
prompt "Choose an active level for buttons"
default EXAMPLE_RLCHN_BTN_ACTIVE_LEVEL_LOW
help
Specify the active level for buttons.
config EXAMPLE_RLCHN_BTN_ACTIVE_LEVEL_LOW
bool "Active level LOW"
config EXAMPLE_RLCHN_BTN_ACTIVE_LEVEL_HIGH
bool "Active level HIGH"
endchoice
config EXAMPLE_RLCHN_BTN_UP_IO_NUM
int "GPIO number for UP button"
default 0
config EXAMPLE_RLCHN_BTN_DOWN_IO_NUM
int "GPIO number for DOWN button"
default 1
config EXAMPLE_RLCHN_BTN_STOP_IO_NUM
int "GPIO number for STOP button"
default 2
config EXAMPLE_RLCHN_BTN_SELECT_IO_NUM
int "GPIO number for STOP button"
default 3
config EXAMPLE_RLCHN_LED_INDICATOR1_IO_NUM
int "GPIO number for LED indicator output for channel 1"
default 4
config EXAMPLE_RLCHN_LED_INDICATOR2_IO_NUM
int "GPIO number for LED indicator output for channel 2"
default 10
config EXAMPLE_RLCHN_LED_INDICATOR3_IO_NUM
int "GPIO number for LED indicator output for channel 3"
default 9
config EXAMPLE_RLCHN_BTN_LONG_PRESS_TIME_MS
int "Long press time in ms to start secondary actions"
range 1500 3000
default 2000
help
Long press time in milliseconds is required to start secondary actions
like tilting and flipping.
endmenu

View File

@@ -0,0 +1,8 @@
dependencies:
idf:
version: '>=4.1.0'
espressif/button: ^4.1.1
espressif/led_indicator: ^1.1.1
relay_chn:
version: '*'
override_path: ../../../

View File

@@ -0,0 +1,427 @@
/*
* SPDX-FileCopyrightText: 2025 Kozmotronik Tech
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "driver/gpio.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_timer.h"
#include "button_gpio.h"
#include "iot_button.h"
#include "led_indicator.h"
#include "relay_chn.h"
static const char *TAG = "RELAY_CHN_MULTI_EXAMPLE";
#define EXAMPLE_CHN_INDICATOR_ON_TIME_MS 3000
#define EXAMPLE_ALL_CHANNELS CONFIG_RELAY_CHN_COUNT
/**
* @brief LED indicator modes for different states.
*/
typedef enum {
INDICATOR_MODE_SELECT, /*!< Channel select indication */
INDICATOR_MODE_OK, /*!< OK/Success indication */
INDICATOR_MODE_FAIL, /*!< Fail/Error indication */
INDICATOR_MODE_TILTING, /*!< Tilting operation in progress */
INDICATOR_MODE_RUNNING, /*!< Full run operation in progress */
INDICATOR_MODE_MAX /*!< Maximum number of indicator modes */
} indicator_mode_t;
/** @brief Blink pattern for channel select indication. */
static const blink_step_t indc_mode_select[] = {
{LED_BLINK_HOLD, LED_STATE_ON, EXAMPLE_CHN_INDICATOR_ON_TIME_MS}, // step1: turn on LED 3000 ms
{LED_BLINK_HOLD, LED_STATE_OFF, 10}, // step2: turn off LED 500 ms
{LED_BLINK_STOP, 0, 0}, // step4: stop blink (off)
};
/** @brief Blink pattern for OK/Success indication. */
static const blink_step_t indc_mode_ok[] = {
{LED_BLINK_HOLD, LED_STATE_ON, 100}, // step1: turn on LED 100 ms
{LED_BLINK_HOLD, LED_STATE_OFF, 50}, // step2: turn off LED 50 ms
{LED_BLINK_HOLD, LED_STATE_ON, 100}, // step3: turn on LED 100 ms
{LED_BLINK_HOLD, LED_STATE_OFF, 50}, // step4: turn off LED 50 ms
{LED_BLINK_STOP, 0, 0}, // step5: stop blink (off)
};
/** @brief Blink pattern for Fail/Error indication. */
static const blink_step_t indc_mode_fail[] = {
{LED_BLINK_HOLD, LED_STATE_ON, 1000}, // step1: turn on LED 1000 ms
{LED_BLINK_HOLD, LED_STATE_OFF, 500}, // step2: turn off LED 500 ms
{LED_BLINK_STOP, 0, 0}, // step4: stop blink (off)
};
/** @brief Blink pattern for full run operation. */
static const blink_step_t indc_mode_running[] = {
{LED_BLINK_HOLD, LED_STATE_ON, 300}, // step1: turn on LED 300 ms
{LED_BLINK_HOLD, LED_STATE_OFF, 100}, // step2: turn off LED 100 ms
{LED_BLINK_LOOP, 0, 0}, // step3: loop from step1
};
/** @brief Blink pattern for tilting operation. */
static const blink_step_t indc_mode_tilting[] = {
{LED_BLINK_HOLD, LED_STATE_ON, 100}, // step1: turn on LED 100 ms
{LED_BLINK_HOLD, LED_STATE_OFF, 50}, // step2: turn off LED 50 ms
{LED_BLINK_LOOP, 0, 0}, // step3: loop from step1
};
/** @brief Array of LED indicator blink patterns. */
blink_step_t const *led_indicator_modes[] = {
[INDICATOR_MODE_SELECT] = indc_mode_select,
[INDICATOR_MODE_OK] = indc_mode_ok,
[INDICATOR_MODE_FAIL] = indc_mode_fail,
[INDICATOR_MODE_RUNNING] = indc_mode_running,
[INDICATOR_MODE_TILTING] = indc_mode_tilting,
[INDICATOR_MODE_MAX] = NULL,
};
/** @brief Handle for the LED indicator. */
static led_indicator_handle_t indicators[CONFIG_RELAY_CHN_COUNT];
static uint8_t selected_channel = 0; /*!< Currently selected channel */
static uint8_t selected_channel_backup = 0; /*!< Backup for last selected channel */
static int64_t last_indc_update_us = 0; /*!< Timestamp of the last channel selection */
/**
* @brief Initializes the buttons for user interaction.
*
* This function configures and creates GPIO buttons for UP, DOWN, and STOP
* operations. It also registers callbacks for single-click and long-press
* events to control the relay channel.
*
* @return esp_err_t
* - ESP_OK: Success
* - Others: Fail
*/
static esp_err_t init_buttons(void);
/**
* @brief Initializes the LED indicator.
*
* This function configures and creates the LED indicator used to provide
* visual feedback on the relay channel's status.
*
* @return esp_err_t
* - ESP_OK: Success
* - ESP_FAIL: Fail
*/
static esp_err_t init_led_indicators(void);
/**
* @brief Starts all indicators with the specified mode.
*
* @param mode One of the indicator modes defined with indicator_mode_t.
*/
static void start_all_indicators_for_mode(indicator_mode_t mode);
/**
* @brief Stops all indicators with the specified mode.
*
* @param mode One of the indicator modes defined with indicator_mode_t.
*/
static void stop_all_indicators_for_mode(indicator_mode_t mode);
/**
* @brief Event listener for relay channel state changes to log the state transition.
*/
static void example_event_listener(uint8_t ch, relay_chn_state_t old_state, relay_chn_state_t new_state);
void app_main(void)
{
const uint8_t gpio_map[] = { 18, 19, 5, 6, 7, 8 };
const uint8_t gpio_count = sizeof(gpio_map) / sizeof(gpio_map[0]);
ESP_LOGI(TAG, "Initializing relay channel");
ESP_ERROR_CHECK(relay_chn_create(gpio_map, gpio_count));
ESP_ERROR_CHECK(relay_chn_register_listener(example_event_listener));
ESP_LOGI(TAG, "Initializing buttons");
ESP_ERROR_CHECK(init_buttons());
ESP_LOGI(TAG, "Initializing LED indicator");
ESP_ERROR_CHECK(init_led_indicators());
ESP_LOGI(TAG, "Relay Channel Multi Example is ready to operate");
// Indicate init was successful
start_all_indicators_for_mode(INDICATOR_MODE_OK);
}
static void on_click_up(void *arg, void *data)
{
if (selected_channel == EXAMPLE_ALL_CHANNELS) {
relay_chn_run_forward_all();
} else {
relay_chn_run_forward(selected_channel);
}
}
static void on_click_down(void *arg, void *data)
{
if (selected_channel == EXAMPLE_ALL_CHANNELS) {
relay_chn_run_reverse_all();
} else {
relay_chn_run_reverse(selected_channel);
}
}
static void on_click_stop(void *arg, void *data)
{
if (selected_channel == EXAMPLE_ALL_CHANNELS) {
relay_chn_stop_all();
} else {
relay_chn_stop(selected_channel);
}
}
static void on_click_flip(void *arg, void *data)
{
if (selected_channel == EXAMPLE_ALL_CHANNELS) {
relay_chn_flip_direction_all();
start_all_indicators_for_mode(INDICATOR_MODE_OK);
} else {
relay_chn_flip_direction(selected_channel);
led_indicator_start(indicators[selected_channel], INDICATOR_MODE_OK);
}
}
static void on_click_tilt_up(void *arg, void *data)
{
if (selected_channel == EXAMPLE_ALL_CHANNELS) {
relay_chn_tilt_forward_all();
} else {
relay_chn_tilt_forward(selected_channel);
}
}
static void on_click_tilt_down(void *arg, void *data)
{
if (selected_channel == EXAMPLE_ALL_CHANNELS) {
relay_chn_tilt_reverse_all();
} else {
relay_chn_tilt_reverse(selected_channel);
}
}
static void on_release_tilt(void *arg, void *data)
{
if (selected_channel == EXAMPLE_ALL_CHANNELS) {
relay_chn_tilt_stop_all();
} else {
relay_chn_tilt_stop(selected_channel);
}
}
static void on_click_select(void *arg, void *data)
{
int64_t now_us = esp_timer_get_time();
uint32_t delta_ms = (now_us - last_indc_update_us) / 1000;
if (delta_ms >= EXAMPLE_CHN_INDICATOR_ON_TIME_MS) {
// Channel indicator was off, turn it on first
if (selected_channel == EXAMPLE_ALL_CHANNELS) {
start_all_indicators_for_mode(INDICATOR_MODE_SELECT);
} else {
led_indicator_start(indicators[selected_channel], INDICATOR_MODE_SELECT);
}
last_indc_update_us = esp_timer_get_time(); // Save last selection time
return;
}
if (selected_channel == EXAMPLE_ALL_CHANNELS) {
// All channels selected previously, restore the selected channel
selected_channel = selected_channel_backup;
ESP_LOGI(TAG, "Restored selected channel: %d", selected_channel);
stop_all_indicators_for_mode(INDICATOR_MODE_SELECT);
led_indicator_start(indicators[selected_channel], INDICATOR_MODE_SELECT);
return;
}
// Channel indicator is turned on, select the next channel
selected_channel = ((selected_channel + 1) % CONFIG_RELAY_CHN_COUNT);
ESP_LOGI(TAG, "Selected channel: %d", selected_channel);
// Update the indicator
stop_all_indicators_for_mode(INDICATOR_MODE_SELECT);
led_indicator_start(indicators[selected_channel], INDICATOR_MODE_SELECT);
last_indc_update_us = esp_timer_get_time(); // Save last selection time
}
static void on_click_select_all(void *arg, void *data)
{
if (selected_channel == EXAMPLE_ALL_CHANNELS) {
ESP_LOGI(TAG, "All channels are selected already");
int64_t now_us = esp_timer_get_time();
uint32_t delta_ms = (now_us - last_indc_update_us) / 1000;
// If the indicators in sleep, wake up
if (delta_ms >= EXAMPLE_CHN_INDICATOR_ON_TIME_MS) {
start_all_indicators_for_mode(INDICATOR_MODE_SELECT);
}
return;
}
selected_channel_backup = selected_channel;
selected_channel = EXAMPLE_ALL_CHANNELS;
ESP_LOGI(TAG, "Selected all channels");
start_all_indicators_for_mode(INDICATOR_MODE_SELECT);
last_indc_update_us = esp_timer_get_time(); // Save last selection time
}
static void start_all_indicators_for_mode(indicator_mode_t mode)
{
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
led_indicator_start(indicators[i], mode);
}
}
static void stop_all_indicators_for_mode(indicator_mode_t mode)
{
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
led_indicator_stop(indicators[i], mode);
led_indicator_set_on_off(indicators[i], false);
}
}
static void example_event_listener(uint8_t ch, relay_chn_state_t old_state, relay_chn_state_t new_state)
{
ESP_LOGI(TAG, "example_event_listener: State change for #%d, from %s to %s",
ch, relay_chn_state_to_str(old_state), relay_chn_state_to_str(new_state));
switch (new_state) {
case RELAY_CHN_STATE_FORWARD:
case RELAY_CHN_STATE_REVERSE:
led_indicator_start(indicators[ch], INDICATOR_MODE_RUNNING);
break;
case RELAY_CHN_STATE_STOPPED:
case RELAY_CHN_STATE_IDLE:
if (old_state == RELAY_CHN_STATE_FORWARD || old_state == RELAY_CHN_STATE_REVERSE) {
led_indicator_stop(indicators[ch], INDICATOR_MODE_RUNNING);
// Make sure the indicator turned off
led_indicator_set_on_off(indicators[ch], false);
}
else if (old_state == RELAY_CHN_STATE_TILT_FORWARD || old_state == RELAY_CHN_STATE_TILT_REVERSE) {
led_indicator_stop(indicators[ch], INDICATOR_MODE_TILTING);
// Make sure the indicator turned off
led_indicator_set_on_off(indicators[ch], false);
}
break;
case RELAY_CHN_STATE_TILT_FORWARD:
case RELAY_CHN_STATE_TILT_REVERSE:
led_indicator_start(indicators[ch], INDICATOR_MODE_TILTING);
break;
default: // No-op
}
}
static esp_err_t init_buttons()
{
esp_err_t ret;
button_config_t btn_cfg = {0};
uint8_t active_level = CONFIG_EXAMPLE_RLCHN_BTN_ACTIVE_LEVEL_LOW ? 0 : 1;
button_gpio_config_t btn_gpio_ccfg = {
.gpio_num = (gpio_num_t) CONFIG_EXAMPLE_RLCHN_BTN_UP_IO_NUM,
.active_level = active_level
};
ESP_LOGI(TAG, "Initializing buttons with active level: %u", active_level);
button_handle_t btn_up = NULL, btn_down = NULL, btn_stop = NULL, btn_select = NULL;
// --- Create buttons ---
ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_ccfg, &btn_up);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to create UP button");
ESP_RETURN_ON_FALSE(btn_up != NULL, ret, TAG, "Failed to create UP button");
btn_gpio_ccfg.gpio_num = (gpio_num_t) CONFIG_EXAMPLE_RLCHN_BTN_DOWN_IO_NUM;
ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_ccfg, &btn_down);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to create DOWN button");
ESP_RETURN_ON_FALSE(btn_down != NULL, ret, TAG, "Failed to create DOWN button");
btn_gpio_ccfg.gpio_num = (gpio_num_t) CONFIG_EXAMPLE_RLCHN_BTN_STOP_IO_NUM;
ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_ccfg, &btn_stop);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to create STOP button");
ESP_RETURN_ON_FALSE(btn_stop != NULL, ret, TAG, "Failed to create STOP button");
btn_gpio_ccfg.gpio_num = (gpio_num_t) CONFIG_EXAMPLE_RLCHN_BTN_SELECT_IO_NUM;
ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_ccfg, &btn_select);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to create SELECT button");
ESP_RETURN_ON_FALSE(btn_select != NULL, ret, TAG, "Failed to create SELECT button");
// --- Create buttons ---
// --- Register button callbacks ---
ESP_LOGI(TAG, "Setting up button callbacks. Configured long press time: %d ms", CONFIG_EXAMPLE_RLCHN_BTN_LONG_PRESS_TIME_MS);
button_event_args_t btn_event_args = {
.long_press.press_time = CONFIG_EXAMPLE_RLCHN_BTN_LONG_PRESS_TIME_MS
};
// --- Register UP and TILT_UP operations on UP button ---
ret = iot_button_register_cb(btn_up, BUTTON_SINGLE_CLICK, NULL, on_click_up, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register UP button click callback");
ret = iot_button_register_cb(btn_up, BUTTON_LONG_PRESS_START, &btn_event_args, on_click_tilt_up, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register TILT_UP button press callback");
ret = iot_button_register_cb(btn_up, BUTTON_LONG_PRESS_UP, NULL, on_release_tilt, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register TILT_UP button release callback");
// --- Register DOWN and TILT_DOWN operations on DOWN button ---
ret = iot_button_register_cb(btn_down, BUTTON_SINGLE_CLICK, NULL, on_click_down, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register DOWN button click callback");
ret = iot_button_register_cb(btn_down, BUTTON_LONG_PRESS_START, &btn_event_args, on_click_tilt_down, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register TILT_DOWN button press callback");
ret = iot_button_register_cb(btn_down, BUTTON_LONG_PRESS_UP, NULL, on_release_tilt, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register TILT_DOWN button release callback");
// --- Register STOP and FLIP operations on STOP ---
ret = iot_button_register_cb(btn_stop, BUTTON_SINGLE_CLICK, NULL, on_click_stop, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register STOP button click callback");
ret = iot_button_register_cb(btn_stop, BUTTON_LONG_PRESS_START, &btn_event_args, on_click_flip, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register FLIP button press callback");
// --- Register channel SELECT and SELECT_ALL ---
ret = iot_button_register_cb(btn_select, BUTTON_SINGLE_CLICK, NULL, on_click_select, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register SELECT button click callback");
ret = iot_button_register_cb(btn_select, BUTTON_LONG_PRESS_START, &btn_event_args, on_click_select_all, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register SELECT_ALL press callback");
return ESP_OK;
}
static esp_err_t init_led_indicators()
{
const int indicator_io_nums[CONFIG_RELAY_CHN_COUNT] = {
CONFIG_EXAMPLE_RLCHN_LED_INDICATOR1_IO_NUM,
CONFIG_EXAMPLE_RLCHN_LED_INDICATOR2_IO_NUM,
CONFIG_EXAMPLE_RLCHN_LED_INDICATOR3_IO_NUM
};
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
gpio_num_t indicator_io_num = (gpio_num_t) indicator_io_nums[i];
gpio_reset_pin(indicator_io_num); // Clear the output buffers
led_indicator_gpio_config_t led_indicator_gpio_cfg = {
.gpio_num = indicator_io_num,
.is_active_level_high = true
};
led_indicator_config_t led_indicator_cfg = {
.mode = LED_GPIO_MODE,
.led_indicator_gpio_config = &led_indicator_gpio_cfg,
.blink_lists = led_indicator_modes,
.blink_list_num = INDICATOR_MODE_MAX
};
indicators[i] = led_indicator_create(&led_indicator_cfg);
if (!indicators[i]) {
ESP_LOGE(TAG, "Failed to create LED indicator");
return ESP_FAIL;
}
}
return ESP_OK;
}

View File

@@ -0,0 +1,10 @@
# Halt on panic
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
# Relay Channel Configs
CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT=y
CONFIG_RELAY_CHN_COUNT=3
# Keep this as short as possible for example purposes
CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC=5
CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC=20
CONFIG_RELAY_CHN_ENABLE_TILTING=y

6
examples/relay_chn_single/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
build/
sdkconfig
sdkconfig.old
# Exclude auto-populated settings file
settings.json

View File

@@ -0,0 +1,23 @@
{
"configurations": [
{
"name": "ESP-IDF",
"compilerPath": "${config:idf.toolsPath}/tools/riscv32-esp-elf/esp-14.2.0_20241119/riscv32-esp-elf/bin/riscv32-esp-elf-gcc",
"compileCommands": "/disk/Projeler/ESP-Components/relay_chn/examples/relay_chn_single/build/compile_commands.json",
"includePath": [
"${config:idf.espIdfPath}/components/**",
"${config:idf.espIdfPathWin}/components/**",
"${workspaceFolder}/**"
],
"browse": {
"path": [
"${config:idf.espIdfPath}/components",
"${config:idf.espIdfPathWin}/components",
"${workspaceFolder}"
],
"limitSymbolsToIncludedHeaders": true
}
}
],
"version": 4
}

View File

@@ -0,0 +1,15 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "gdbtarget",
"request": "attach",
"name": "Eclipse CDT GDB Adapter"
},
{
"type": "espidf",
"name": "Launch",
"request": "launch"
}
]
}

View File

@@ -0,0 +1,10 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five 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(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(relay_chn_single)

View File

@@ -0,0 +1,126 @@
# Relay Channel Single Example
## Introduction
This example demonstrates how to use the relay channel component to control a 2-relay setup with button inputs and LED status indication. It showcases:
- Basic relay channel operations (forward/reverse running, stopping)
- Secondary operations (tilting, direction flipping)
- State change event handling with multiple listeners
- Relay channel run limit
- Button event handling using esp-iot-solution's button component
- Visual feedback using esp-iot-solution's LED indicator component
## How to Use Example
This example has been tested on an `ESP32-C3-DevKitM-1U` board. However, it can be adapted to any ESP32-based board with at least six available GPIO pins by adjusting the configuration options.
### Hardware Required
* An ESP32-based development board
* 2 relays connected to GPIO pins (default: GPIO4, GPIO5)
* 3 buttons connected to GPIO pins:
- UP button (default: GPIO0)
- DOWN button (default: GPIO1)
- STOP button (default: GPIO2)
* 1 LED for status indication (default: GPIO3)
#### Hardware Schematic
![Hardware Schematic](example_schematic.png)
### Configuration
The example can be configured through `menuconfig` under "Relay Channel Single Example Configuration":
1. Button active level (`EXAMPLE_RLCHN_BTN_ACTIVE_LEVEL`)
- Select between active LOW or HIGH logic level for buttons
2. GPIO assignments:
- UP button pin (`EXAMPLE_RLCHN_BTN_UP_IO_NUM`, default: 0)
- DOWN button pin (`EXAMPLE_RLCHN_BTN_DOWN_IO_NUM`, default: 1)
- STOP button pin (`EXAMPLE_RLCHN_BTN_STOP_IO_NUM`, default: 2)
- LED indicator pin (`EXAMPLE_RLCHN_LED_INDICATOR_IO_NUM`, default: 3)
3. Long press timing:
- `EXAMPLE_RLCHN_BTN_LONG_PRESS_TIME_MS`: Duration for long press actions (1500-3000ms, default: 2000ms)
### Button Operations
The example uses esp-iot-solution's `button` component to handle the following operations:
- **UP button**:
* Short press: Start forward movement
* Long press: Start forward tilt (stops on release)
- **DOWN button**:
* Short press: Start reverse movement
* Long press: Start reverse tilt (stops on release)
- **STOP button**:
* Short press: Stop movement
* Long press: Flip movement direction
### LED Indicator States
The example uses esp-iot-solution's `led_indicator` component to show different states:
- **Running**: LED blinks at 300ms on, 100ms off
- **Tilting**: Fast blink at 100ms on, 50ms off
- **Operation Success**: Two quick blinks
- **Operation Fail**: One long blink
### Dependencies
This example requires:
- ESP-IDF v4.1 or later
- esp-iot-solution components:
* button v4.1.1 or later
* led_indicator v1.1.1 or later
- relay_chn component
## Example Output
When the application boots, it will wait for a button event. Then the two state listeners will print state changes.
```log
I (273) main_task: Calling app_main()
I (273) RELAY_CHN_SINGLE_EXAMPLE: Initializing default NVS storage
I (283) RELAY_CHN_SINGLE_EXAMPLE: nvs_flash_init: NVS flash init return: ESP_OK
I (283) RELAY_CHN_SINGLE_EXAMPLE: Initializing relay channel
I (293) gpio: GPIO[4]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (303) gpio: GPIO[5]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (313) RELAY_CHN_SINGLE_EXAMPLE: Initializing buttons
I (313) RELAY_CHN_SINGLE_EXAMPLE: Initializing buttons with active level: 0
I (323) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (323) button: IoT Button Version: 4.1.3
I (333) gpio: GPIO[1]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (343) gpio: GPIO[2]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (343) RELAY_CHN_SINGLE_EXAMPLE: Setting up button callbacks. Configured long press time: 2000 ms
I (353) RELAY_CHN_SINGLE_EXAMPLE: Initializing LED indicator
I (363) gpio: GPIO[3]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (373) led_indicator: LED Indicator Version: 1.1.1
I (373) gpio: GPIO[3]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (383) led_indicator: Indicator create successfully. type:GPIO mode, hardware_data:0x3fc97be4, blink_lists:custom
I (393) RELAY_CHN_SINGLE_EXAMPLE: Relay Channel Single Example is ready to operate
I (403) main_task: Returned from app_main()
I (3683) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_1: Defining new indicator mode for #0 and state change from 1 to 3
I (3683) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_2: State change for #0, from IDLE to FORWARD
I (9513) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_1: Defining new indicator mode for #0 and state change from 3 to 2
I (9513) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_2: State change for #0, from FORWARD to STOPPED
I (9523) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_1: Defining new indicator mode for #0 and state change from 2 to 6
I (9533) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_2: State change for #0, from STOPPED to REVERSE_PENDING
I (10313) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_1: Defining new indicator mode for #0 and state change from 6 to 4
I (10313) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_2: State change for #0, from REVERSE_PENDING to REVERSE
I (32173) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_1: Defining new indicator mode for #0 and state change from 4 to 2
I (32173) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_2: State change for #0, from REVERSE to STOPPED
I (32973) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_1: Defining new indicator mode for #0 and state change from 2 to 1
I (32973) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_2: State change for #0, from STOPPED to IDLE
I (36423) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_1: Defining new indicator mode for #0 and state change from 1 to 8
I (36423) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_2: State change for #0, from IDLE to TILT_REVERSE
I (41153) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_1: Defining new indicator mode for #0 and state change from 8 to 1
I (41153) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_2: State change for #0, from TILT_REVERSE to IDLE
I (47113) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_1: Defining new indicator mode for #0 and state change from 1 to 7
I (47113) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_2: State change for #0, from IDLE to TILT_FORWARD
I (51913) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_1: Defining new indicator mode for #0 and state change from 7 to 1
I (51913) RELAY_CHN_SINGLE_EXAMPLE: example_event_listener_2: State change for #0, from TILT_FORWARD to IDLE
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "relay_chn_single_main.c"
PRIV_REQUIRES button led_indicator relay_chn)

View File

@@ -0,0 +1,40 @@
menu "Relay Channel Single Example Configuration"
choice EXAMPLE_RLCHN_BTN_ACTIVE_LEVEL
prompt "Choose an active level for buttons"
default EXAMPLE_RLCHN_BTN_ACTIVE_LEVEL_LOW
help
Specify the active level for buttons.
config EXAMPLE_RLCHN_BTN_ACTIVE_LEVEL_LOW
bool "Active level LOW"
config EXAMPLE_RLCHN_BTN_ACTIVE_LEVEL_HIGH
bool "Active level HIGH"
endchoice
config EXAMPLE_RLCHN_BTN_UP_IO_NUM
int "GPIO number for UP button"
default 0
config EXAMPLE_RLCHN_BTN_DOWN_IO_NUM
int "GPIO number for DOWN button"
default 1
config EXAMPLE_RLCHN_BTN_STOP_IO_NUM
int "GPIO number for STOP button"
default 2
config EXAMPLE_RLCHN_LED_INDICATOR_IO_NUM
int "GPIO number for LED indicator output"
default 3
config EXAMPLE_RLCHN_BTN_LONG_PRESS_TIME_MS
int "Long press time in ms to start secondary actions"
range 1500 3000
default 2000
help
Long press time in milliseconds is required to start secondary actions
like tilting and flipping.
endmenu

View File

@@ -0,0 +1,8 @@
dependencies:
idf:
version: '>=4.1.0'
espressif/button: ^4.1.1
espressif/led_indicator: ^1.1.1
relay_chn:
version: '*'
override_path: ../../../

View File

@@ -0,0 +1,290 @@
/*
* SPDX-FileCopyrightText: 2025 Kozmotronik Tech
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "driver/gpio.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "button_gpio.h"
#include "iot_button.h"
#include "led_indicator.h"
#include "relay_chn.h"
static const char *TAG = "RELAY_CHN_SINGLE_EXAMPLE";
/**
* @brief LED indicator modes for different states.
*/
typedef enum {
INDICATOR_MODE_OK, /*!< OK/Success indication */
INDICATOR_MODE_FAIL, /*!< Fail/Error indication */
INDICATOR_MODE_TILTING, /*!< Tilting operation in progress */
INDICATOR_MODE_RUNNING, /*!< Full run operation in progress */
INDICATOR_MODE_MAX /*!< Maximum number of indicator modes */
} indicator_mode_t;
/** @brief Blink pattern for OK/Success indication. */
static const blink_step_t indc_mode_ok[] = {
{LED_BLINK_HOLD, LED_STATE_ON, 100}, // step1: turn on LED 100 ms
{LED_BLINK_HOLD, LED_STATE_OFF, 50}, // step2: turn off LED 50 ms
{LED_BLINK_HOLD, LED_STATE_ON, 100}, // step3: turn on LED 100 ms
{LED_BLINK_HOLD, LED_STATE_OFF, 50}, // step4: turn off LED 50 ms
{LED_BLINK_STOP, 0, 0}, // step5: stop blink (off)
};
/** @brief Blink pattern for Fail/Error indication. */
static const blink_step_t indc_mode_fail[] = {
{LED_BLINK_HOLD, LED_STATE_ON, 1000}, // step1: turn on LED 1000 ms
{LED_BLINK_HOLD, LED_STATE_OFF, 500}, // step2: turn off LED 500 ms
{LED_BLINK_STOP, 0, 0}, // step4: stop blink (off)
};
/** @brief Blink pattern for full run operation. */
static const blink_step_t indc_mode_running[] = {
{LED_BLINK_HOLD, LED_STATE_ON, 300}, // step1: turn on LED 300 ms
{LED_BLINK_HOLD, LED_STATE_OFF, 100}, // step2: turn off LED 100 ms
{LED_BLINK_LOOP, 0, 0}, // step3: loop from step1
};
/** @brief Blink pattern for tilting operation. */
static const blink_step_t indc_mode_tilting[] = {
{LED_BLINK_HOLD, LED_STATE_ON, 100}, // step1: turn on LED 100 ms
{LED_BLINK_HOLD, LED_STATE_OFF, 50}, // step2: turn off LED 50 ms
{LED_BLINK_LOOP, 0, 0}, // step3: loop from step1
};
/** @brief Array of LED indicator blink patterns. */
blink_step_t const *led_indicator_modes[] = {
[INDICATOR_MODE_OK] = indc_mode_ok,
[INDICATOR_MODE_FAIL] = indc_mode_fail,
[INDICATOR_MODE_RUNNING] = indc_mode_running,
[INDICATOR_MODE_TILTING] = indc_mode_tilting,
[INDICATOR_MODE_MAX] = NULL,
};
/** @brief Handle for the LED indicator. */
static led_indicator_handle_t indicator = NULL;
/**
* @brief Initializes the buttons for user interaction.
*
* This function configures and creates GPIO buttons for UP, DOWN, and STOP
* operations. It also registers callbacks for single-click and long-press
* events to control the relay channel.
*
* @return esp_err_t
* - ESP_OK: Success
* - Others: Fail
*/
static esp_err_t init_buttons(void);
/**
* @brief Initializes the LED indicator.
*
* This function configures and creates the LED indicator used to provide
* visual feedback on the relay channel's status.
*
* @return esp_err_t
* - ESP_OK: Success
* - ESP_FAIL: Fail
*/
static esp_err_t init_led_indicator(void);
/**
* @brief Event listener for relay channel state changes to control the LED indicator.
*/
static void example_event_listener_1(uint8_t ch, relay_chn_state_t old_state, relay_chn_state_t new_state);
/**
* @brief Event listener for relay channel state changes to log the state transition.
*/
static void example_event_listener_2(uint8_t ch, relay_chn_state_t old_state, relay_chn_state_t new_state);
void app_main(void)
{
const uint8_t gpio_map[] = { 4, 5 };
const uint8_t gpio_count = sizeof(gpio_map) / sizeof(gpio_map[0]);
ESP_LOGI(TAG, "Initializing relay channel");
ESP_ERROR_CHECK(relay_chn_create(gpio_map, gpio_count));
ESP_ERROR_CHECK(relay_chn_register_listener(example_event_listener_1));
ESP_ERROR_CHECK(relay_chn_register_listener(example_event_listener_2));
ESP_LOGI(TAG, "Initializing buttons");
ESP_ERROR_CHECK(init_buttons());
ESP_LOGI(TAG, "Initializing LED indicator");
ESP_ERROR_CHECK(init_led_indicator());
ESP_LOGI(TAG, "Relay Channel Single Example is ready to operate");
// Indicate init was successful
led_indicator_start(indicator, INDICATOR_MODE_OK);
}
static void on_click_up(void *arg, void *data)
{
relay_chn_run_forward();
}
static void on_click_down(void *arg, void *data)
{
relay_chn_run_reverse();
}
static void on_click_stop(void *arg, void *data)
{
relay_chn_stop();
}
static void on_click_flip(void *arg, void *data)
{
relay_chn_flip_direction();
led_indicator_start(indicator, INDICATOR_MODE_OK);
}
static void on_click_tilt_up(void *arg, void *data)
{
relay_chn_tilt_forward();
}
static void on_click_tilt_down(void *arg, void *data)
{
relay_chn_tilt_reverse();
}
static void on_release_tilt(void *arg, void *data)
{
relay_chn_tilt_stop();
}
static void example_event_listener_1(uint8_t ch, relay_chn_state_t old_state, relay_chn_state_t new_state)
{
ESP_LOGI(TAG, "example_event_listener_1: Defining new indicator mode for #%d and state change from %d to %d", ch, old_state, new_state);
switch (new_state) {
case RELAY_CHN_STATE_FORWARD:
case RELAY_CHN_STATE_REVERSE:
led_indicator_start(indicator, INDICATOR_MODE_RUNNING);
break;
case RELAY_CHN_STATE_STOPPED:
case RELAY_CHN_STATE_IDLE:
if (old_state == RELAY_CHN_STATE_FORWARD || old_state == RELAY_CHN_STATE_REVERSE) {
led_indicator_stop(indicator, INDICATOR_MODE_RUNNING);
// Make sure the indicator turned off
led_indicator_set_on_off(indicator, false);
}
else if (old_state == RELAY_CHN_STATE_TILT_FORWARD || old_state == RELAY_CHN_STATE_TILT_REVERSE) {
led_indicator_stop(indicator, INDICATOR_MODE_TILTING);
// Make sure the indicator turned off
led_indicator_set_on_off(indicator, false);
}
break;
case RELAY_CHN_STATE_TILT_FORWARD:
case RELAY_CHN_STATE_TILT_REVERSE:
led_indicator_start(indicator, INDICATOR_MODE_TILTING);
break;
default: // No-op
}
}
static void example_event_listener_2(uint8_t ch, relay_chn_state_t old_state, relay_chn_state_t new_state)
{
ESP_LOGI(TAG, "example_event_listener_2: State change for #%d, from %s to %s",
ch, relay_chn_state_to_str(old_state), relay_chn_state_to_str(new_state));
}
static esp_err_t init_buttons()
{
esp_err_t ret;
button_config_t btn_cfg = {0};
uint8_t active_level = CONFIG_EXAMPLE_RLCHN_BTN_ACTIVE_LEVEL_LOW ? 0 : 1;
button_gpio_config_t btn_gpio_ccfg = {
.gpio_num = (gpio_num_t) CONFIG_EXAMPLE_RLCHN_BTN_UP_IO_NUM,
.active_level = active_level
};
ESP_LOGI(TAG, "Initializing buttons with active level: %u", active_level);
button_handle_t btn_up = NULL, btn_down = NULL, btn_stop = NULL;
// --- Create buttons ---
ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_ccfg, &btn_up);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to create UP button");
ESP_RETURN_ON_FALSE(btn_up != NULL, ret, TAG, "Failed to create UP button");
btn_gpio_ccfg.gpio_num = (gpio_num_t) CONFIG_EXAMPLE_RLCHN_BTN_DOWN_IO_NUM;
ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_ccfg, &btn_down);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to create DOWN button");
ESP_RETURN_ON_FALSE(btn_down != NULL, ret, TAG, "Failed to create DOWN button");
btn_gpio_ccfg.gpio_num = (gpio_num_t) CONFIG_EXAMPLE_RLCHN_BTN_STOP_IO_NUM;
ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_ccfg, &btn_stop);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to create STOP button");
ESP_RETURN_ON_FALSE(btn_stop != NULL, ret, TAG, "Failed to create STOP button");
// --- Create buttons ---
// --- Register button callbacks ---
ESP_LOGI(TAG, "Setting up button callbacks. Configured long press time: %d ms", CONFIG_EXAMPLE_RLCHN_BTN_LONG_PRESS_TIME_MS);
button_event_args_t btn_event_args = {
.long_press.press_time = CONFIG_EXAMPLE_RLCHN_BTN_LONG_PRESS_TIME_MS
};
// --- Register UP and TILT_UP operations on UP button ---
ret = iot_button_register_cb(btn_up, BUTTON_SINGLE_CLICK, NULL, on_click_up, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register UP button click callback");
ret = iot_button_register_cb(btn_up, BUTTON_LONG_PRESS_START, &btn_event_args, on_click_tilt_up, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register TILT_UP button press callback");
ret = iot_button_register_cb(btn_up, BUTTON_LONG_PRESS_UP, NULL, on_release_tilt, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register TILT_UP button release callback");
// --- Register DOWN and TILT_DOWN operations on DOWN button ---
ret = iot_button_register_cb(btn_down, BUTTON_SINGLE_CLICK, NULL, on_click_down, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register DOWN button click callback");
ret = iot_button_register_cb(btn_down, BUTTON_LONG_PRESS_START, &btn_event_args, on_click_tilt_down, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register TILT_DOWN button press callback");
ret = iot_button_register_cb(btn_down, BUTTON_LONG_PRESS_UP, NULL, on_release_tilt, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register TILT_DOWN button release callback");
// --- Register STOP and FLIP operations on STOP ---
ret = iot_button_register_cb(btn_stop, BUTTON_SINGLE_CLICK, NULL, on_click_stop, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register STOP button click callback");
ret = iot_button_register_cb(btn_stop, BUTTON_LONG_PRESS_START, &btn_event_args, on_click_flip, NULL);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to register FLIP button press callback");
return ESP_OK;
}
static esp_err_t init_led_indicator()
{
const gpio_num_t indicator_io_num = (gpio_num_t) CONFIG_EXAMPLE_RLCHN_LED_INDICATOR_IO_NUM;
gpio_reset_pin(indicator_io_num); // Clear the output buffers
led_indicator_gpio_config_t led_indicator_gpio_cfg = {
.gpio_num = indicator_io_num,
.is_active_level_high = true
};
led_indicator_config_t led_indicator_cfg = {
.mode = LED_GPIO_MODE,
.led_indicator_gpio_config = &led_indicator_gpio_cfg,
.blink_lists = led_indicator_modes,
.blink_list_num = INDICATOR_MODE_MAX
};
indicator = led_indicator_create(&led_indicator_cfg);
if (!indicator) {
ESP_LOGE(TAG, "Failed to create LED indicator");
return ESP_FAIL;
}
return ESP_OK;
}

View File

@@ -0,0 +1,9 @@
# Halt on panic
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
# Relay Channel Configs
CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT=y
# Keep this as short as possible for example purposes
CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC=5
CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC=20
CONFIG_RELAY_CHN_ENABLE_TILTING=y

View File

@@ -1,6 +1,12 @@
name: relay_chn version: "1.0.0"
version: "0.5.0" description: "Relay channel driver for bipolar motors."
description: "Custom component for relay channel control"
license: "MIT" license: "MIT"
url: "https://git.kozmotronik.com.tr/KozmotronikTech/relay_chn" url: "https://git.kozmotronik.com.tr/KozmotronikTech/relay_chn"
repository: "https://git.kozmotronik.com.tr/KozmotronikTech/relay_chn.git" repository: "https://git.kozmotronik.com.tr/KozmotronikTech/relay_chn.git"
dependencies:
idf: ">=5.0"
examples:
- path: examples/relay_chn_single
- path: examples/relay_chn_multi
files:
use_gitignore: true

View File

@@ -63,6 +63,14 @@ esp_err_t relay_chn_register_listener(relay_chn_state_listener_t listener);
*/ */
void relay_chn_unregister_listener(relay_chn_state_listener_t listener); void relay_chn_unregister_listener(relay_chn_state_listener_t listener);
/**
* @brief Return the text presentation of an state.
*
* @param state A state with type of relay_chn_state_t.
* @return char* The text presentation of the state. "UNKNOWN" if the state is not known.
*/
char *relay_chn_state_to_str(relay_chn_state_t state);
#if CONFIG_RELAY_CHN_ENABLE_TILTING #if CONFIG_RELAY_CHN_ENABLE_TILTING
/** /**
* @brief Get the default tilting sensitivity for the relay channel. * @brief Get the default tilting sensitivity for the relay channel.

View File

@@ -93,14 +93,6 @@ esp_err_t relay_chn_start_esp_timer_once(esp_timer_handle_t esp_timer, uint32_t
*/ */
void relay_chn_update_state(relay_chn_ctl_t *chn_ctl, relay_chn_state_t new_state); void relay_chn_update_state(relay_chn_ctl_t *chn_ctl, relay_chn_state_t new_state);
/**
* @brief Return the text presentation of an state.
*
* @param state A state with type of relay_chn_state_t.
* @return char* The text presentation of the state. "UNKNOWN" if the state is not known.
*/
char *relay_chn_state_str(relay_chn_state_t state);
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
/** /**
* @brief Check if the provided channel ID is valid. * @brief Check if the provided channel ID is valid.

View File

@@ -98,8 +98,7 @@ fi
script_dir=$(dirname "$(readlink -f "$0")") script_dir=$(dirname "$(readlink -f "$0")")
project_root=$(dirname "$script_dir") project_root=$(dirname "$script_dir")
echo "🔍 Searching for 'test_apps' directory in '$project_root'..." test_apps_dir="${project_root}/test_apps"
test_apps_dir=$(find "$project_root" -type d -name "test_apps" | head -n 1)
if [[ -z "$test_apps_dir" || ! -d "$test_apps_dir" ]]; then if [[ -z "$test_apps_dir" || ! -d "$test_apps_dir" ]]; then
echo "❌ 'test_apps' directory not found within the project root: '$project_root'" echo "❌ 'test_apps' directory not found within the project root: '$project_root'"
@@ -107,7 +106,7 @@ if [[ -z "$test_apps_dir" || ! -d "$test_apps_dir" ]]; then
exit 1 exit 1
fi fi
echo "✅ Found 'test_apps' at: $test_apps_dir" echo "⏳ Current time is: $(date +"%Y-%m-%d %H:%M:%S")"
echo "🧪 Test mode: $arg_tag | Profile: $arg_profile" echo "🧪 Test mode: $arg_tag | Profile: $arg_profile"
echo "🧹 Clean: $arg_clean | 📄 Log: $arg_log" echo "🧹 Clean: $arg_clean | 📄 Log: $arg_log"

View File

@@ -14,10 +14,15 @@ project_root=$(dirname "$script_dir")
echo "Script dir: ${script_dir}" echo "Script dir: ${script_dir}"
echo "Project root: ${project_root}" echo "Project root: ${project_root}"
echo "🔍 Searching for 'test_apps' directory in '$project_root'..." test_apps_dir="${project_root}/test_apps"
test_apps_dir=$(find "$project_root" -type d -name "test_apps" | head -n 1)
echo "test_apps dir: ${test_apps_dir}" echo "test_apps dir: ${test_apps_dir}"
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
# Execute tests for all profiles # Execute tests for all profiles
mapfile -t profiles < <(find "${test_apps_dir}/profiles" -maxdepth 1 -type f) mapfile -t profiles < <(find "${test_apps_dir}/profiles" -maxdepth 1 -type f)

View File

@@ -216,8 +216,8 @@ void relay_chn_issue_cmd(relay_chn_ctl_t* chn_ctl, relay_chn_cmd_t cmd)
} }
if (cmd == RELAY_CHN_CMD_STOP) { if (cmd == RELAY_CHN_CMD_STOP) {
if (chn_ctl->state == RELAY_CHN_STATE_STOPPED) { if (chn_ctl->state == RELAY_CHN_STATE_STOPPED || chn_ctl->state == RELAY_CHN_STATE_IDLE) {
return; // Do nothing if already stopped return; // Do nothing if already stopped or idle
} }
// If the command is STOP, issue it immediately // If the command is STOP, issue it immediately
relay_chn_dispatch_cmd(chn_ctl, cmd); relay_chn_dispatch_cmd(chn_ctl, cmd);
@@ -471,7 +471,7 @@ char *relay_chn_cmd_str(relay_chn_cmd_t cmd)
} }
} }
char *relay_chn_state_str(relay_chn_state_t state) char *relay_chn_state_to_str(relay_chn_state_t state)
{ {
switch (state) { switch (state) {
case RELAY_CHN_STATE_IDLE: case RELAY_CHN_STATE_IDLE:

View File

@@ -5,6 +5,7 @@
*/ */
#include "esp_check.h" #include "esp_check.h"
#include "relay_chn.h"
#include "relay_chn_priv_types.h" #include "relay_chn_priv_types.h"
#include "relay_chn_core.h" #include "relay_chn_core.h"
#include "relay_chn_ctl.h" #include "relay_chn_ctl.h"
@@ -16,7 +17,7 @@
static const char *TAG = "RELAY_CHN_CTL"; static const char *TAG = "RELAY_CHN_CTL";
static relay_chn_ctl_t chn_ctls[CONFIG_RELAY_CHN_COUNT]; static relay_chn_ctl_t s_chn_ctls[CONFIG_RELAY_CHN_COUNT];
esp_err_t relay_chn_ctl_init(relay_chn_output_t *outputs, relay_chn_run_info_t *run_infos) esp_err_t relay_chn_ctl_init(relay_chn_output_t *outputs, relay_chn_run_info_t *run_infos)
@@ -24,7 +25,7 @@ esp_err_t relay_chn_ctl_init(relay_chn_output_t *outputs, relay_chn_run_info_t *
// Initialize all relay channels // Initialize all relay channels
esp_err_t ret; esp_err_t ret;
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
relay_chn_ctl_t* chn_ctl = &chn_ctls[i]; relay_chn_ctl_t* chn_ctl = &s_chn_ctls[i];
relay_chn_output_t* output = &outputs[i]; relay_chn_output_t* output = &outputs[i];
relay_chn_run_info_t* run_info = &run_infos[i]; relay_chn_run_info_t* run_info = &run_infos[i];
@@ -54,7 +55,7 @@ esp_err_t relay_chn_ctl_init(relay_chn_output_t *outputs, relay_chn_run_info_t *
void relay_chn_ctl_deinit() void relay_chn_ctl_deinit()
{ {
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
relay_chn_ctl_t* chn_ctl = &chn_ctls[i]; relay_chn_ctl_t* chn_ctl = &s_chn_ctls[i];
if (chn_ctl->inertia_timer != NULL) { if (chn_ctl->inertia_timer != NULL) {
esp_timer_delete(chn_ctl->inertia_timer); esp_timer_delete(chn_ctl->inertia_timer);
chn_ctl->inertia_timer = NULL; chn_ctl->inertia_timer = NULL;
@@ -71,7 +72,7 @@ void relay_chn_ctl_deinit()
relay_chn_state_t relay_chn_ctl_get_state(uint8_t chn_id) relay_chn_state_t relay_chn_ctl_get_state(uint8_t chn_id)
{ {
return relay_chn_is_channel_id_valid(chn_id) ? return relay_chn_is_channel_id_valid(chn_id) ?
chn_ctls[chn_id].state : RELAY_CHN_STATE_UNDEFINED; s_chn_ctls[chn_id].state : RELAY_CHN_STATE_UNDEFINED;
} }
esp_err_t relay_chn_ctl_get_state_all(relay_chn_state_t *states) esp_err_t relay_chn_ctl_get_state_all(relay_chn_state_t *states)
@@ -84,7 +85,7 @@ esp_err_t relay_chn_ctl_get_state_all(relay_chn_state_t *states)
ESP_LOGW(TAG, "get_state_all: States have been copied until channel %d since states[%d] is NULL", i, i); ESP_LOGW(TAG, "get_state_all: States have been copied until channel %d since states[%d] is NULL", i, i);
break; break;
} }
*dest_state = chn_ctls[i].state; *dest_state = s_chn_ctls[i].state;
} }
return ESP_OK; return ESP_OK;
} }
@@ -92,22 +93,22 @@ esp_err_t relay_chn_ctl_get_state_all(relay_chn_state_t *states)
char *relay_chn_ctl_get_state_str(uint8_t chn_id) char *relay_chn_ctl_get_state_str(uint8_t chn_id)
{ {
return relay_chn_is_channel_id_valid(chn_id) return relay_chn_is_channel_id_valid(chn_id)
? relay_chn_state_str(chn_ctls[chn_id].state) ? relay_chn_state_to_str(s_chn_ctls[chn_id].state)
: relay_chn_state_str(RELAY_CHN_STATE_UNDEFINED); : relay_chn_state_to_str(RELAY_CHN_STATE_UNDEFINED);
} }
static void relay_chn_ctl_issue_cmd_on_all_channels(relay_chn_cmd_t cmd) static void relay_chn_ctl_issue_cmd_on_all_channels(relay_chn_cmd_t cmd)
{ {
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
relay_chn_issue_cmd(&chn_ctls[i], cmd); relay_chn_issue_cmd(&s_chn_ctls[i], cmd);
} }
} }
void relay_chn_ctl_run_forward(uint8_t chn_id) void relay_chn_ctl_run_forward(uint8_t chn_id)
{ {
if (relay_chn_is_channel_id_valid(chn_id)) if (relay_chn_is_channel_id_valid(chn_id))
relay_chn_issue_cmd(&chn_ctls[chn_id], RELAY_CHN_CMD_FORWARD); relay_chn_issue_cmd(&s_chn_ctls[chn_id], RELAY_CHN_CMD_FORWARD);
} }
void relay_chn_ctl_run_forward_all() void relay_chn_ctl_run_forward_all()
@@ -118,7 +119,7 @@ void relay_chn_ctl_run_forward_all()
void relay_chn_ctl_run_reverse(uint8_t chn_id) void relay_chn_ctl_run_reverse(uint8_t chn_id)
{ {
if (relay_chn_is_channel_id_valid(chn_id)) if (relay_chn_is_channel_id_valid(chn_id))
relay_chn_issue_cmd(&chn_ctls[chn_id], RELAY_CHN_CMD_REVERSE); relay_chn_issue_cmd(&s_chn_ctls[chn_id], RELAY_CHN_CMD_REVERSE);
} }
void relay_chn_ctl_run_reverse_all() void relay_chn_ctl_run_reverse_all()
@@ -129,7 +130,7 @@ void relay_chn_ctl_run_reverse_all()
void relay_chn_ctl_stop(uint8_t chn_id) void relay_chn_ctl_stop(uint8_t chn_id)
{ {
if (relay_chn_is_channel_id_valid(chn_id)) if (relay_chn_is_channel_id_valid(chn_id))
relay_chn_issue_cmd(&chn_ctls[chn_id], RELAY_CHN_CMD_STOP); relay_chn_issue_cmd(&s_chn_ctls[chn_id], RELAY_CHN_CMD_STOP);
} }
void relay_chn_ctl_stop_all() void relay_chn_ctl_stop_all()
@@ -140,7 +141,7 @@ void relay_chn_ctl_stop_all()
void relay_chn_ctl_flip_direction(uint8_t chn_id) void relay_chn_ctl_flip_direction(uint8_t chn_id)
{ {
if (relay_chn_is_channel_id_valid(chn_id)) if (relay_chn_is_channel_id_valid(chn_id))
relay_chn_issue_cmd(&chn_ctls[chn_id], RELAY_CHN_CMD_FLIP); relay_chn_issue_cmd(&s_chn_ctls[chn_id], RELAY_CHN_CMD_FLIP);
} }
void relay_chn_ctl_flip_direction_all() void relay_chn_ctl_flip_direction_all()
@@ -151,7 +152,7 @@ void relay_chn_ctl_flip_direction_all()
relay_chn_direction_t relay_chn_ctl_get_direction(uint8_t chn_id) relay_chn_direction_t relay_chn_ctl_get_direction(uint8_t chn_id)
{ {
return relay_chn_is_channel_id_valid(chn_id) return relay_chn_is_channel_id_valid(chn_id)
? relay_chn_output_get_direction(chn_ctls[chn_id].output) ? relay_chn_output_get_direction(s_chn_ctls[chn_id].output)
: RELAY_CHN_DIRECTION_DEFAULT; : RELAY_CHN_DIRECTION_DEFAULT;
} }
@@ -165,7 +166,7 @@ esp_err_t relay_chn_ctl_get_direction_all(relay_chn_direction_t *directions)
ESP_LOGW(TAG, "get_direction_all: Directions have been copied until channel %d since directions[%d] is NULL", i, i); ESP_LOGW(TAG, "get_direction_all: Directions have been copied until channel %d since directions[%d] is NULL", i, i);
break; break;
} }
*dest_direction = relay_chn_output_get_direction(chn_ctls[i].output); *dest_direction = relay_chn_output_get_direction(s_chn_ctls[i].output);
} }
return ESP_OK; return ESP_OK;
} }
@@ -173,7 +174,7 @@ esp_err_t relay_chn_ctl_get_direction_all(relay_chn_direction_t *directions)
#if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT #if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT
uint16_t relay_chn_ctl_get_run_limit(uint8_t chn_id) uint16_t relay_chn_ctl_get_run_limit(uint8_t chn_id)
{ {
return relay_chn_is_channel_id_valid(chn_id) ? chn_ctls[chn_id].run_limit_sec : 0; return relay_chn_is_channel_id_valid(chn_id) ? s_chn_ctls[chn_id].run_limit_sec : 0;
} }
esp_err_t relay_chn_ctl_get_run_limit_all(uint16_t *limits_sec) esp_err_t relay_chn_ctl_get_run_limit_all(uint16_t *limits_sec)
@@ -186,7 +187,7 @@ esp_err_t relay_chn_ctl_get_run_limit_all(uint16_t *limits_sec)
ESP_LOGW(TAG, "get_run_limit_all: Run limits have been copied until channel %d since limits_sec[%d] is NULL", i, i); ESP_LOGW(TAG, "get_run_limit_all: Run limits have been copied until channel %d since limits_sec[%d] is NULL", i, i);
break; break;
} }
*dest_limit_sec = chn_ctls[i].run_limit_sec; *dest_limit_sec = s_chn_ctls[i].run_limit_sec;
} }
return ESP_OK; return ESP_OK;
} }
@@ -199,7 +200,7 @@ static void relay_chn_ctl_set_run_limit_common(uint8_t chn_id, uint16_t limit_se
else if (limit_sec < CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC) else if (limit_sec < CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC)
limit_sec = CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC; limit_sec = CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC;
chn_ctls[chn_id].run_limit_sec = limit_sec; s_chn_ctls[chn_id].run_limit_sec = limit_sec;
#if CONFIG_RELAY_CHN_ENABLE_NVS #if CONFIG_RELAY_CHN_ENABLE_NVS
relay_chn_nvs_set_run_limit(chn_id, limit_sec); relay_chn_nvs_set_run_limit(chn_id, limit_sec);
@@ -241,10 +242,10 @@ esp_err_t relay_chn_ctl_set_run_limit_all_with(uint16_t limit_sec)
relay_chn_ctl_t *relay_chn_ctl_get(uint8_t chn_id) relay_chn_ctl_t *relay_chn_ctl_get(uint8_t chn_id)
{ {
return relay_chn_is_channel_id_valid(chn_id) ? &chn_ctls[chn_id] : NULL; return relay_chn_is_channel_id_valid(chn_id) ? &s_chn_ctls[chn_id] : NULL;
} }
relay_chn_ctl_t *relay_chn_ctl_get_all(void) relay_chn_ctl_t *relay_chn_ctl_get_all(void)
{ {
return chn_ctls; return s_chn_ctls;
} }

View File

@@ -5,6 +5,7 @@
*/ */
#include "esp_check.h" #include "esp_check.h"
#include "relay_chn.h"
#include "relay_chn_priv_types.h" #include "relay_chn_priv_types.h"
#include "relay_chn_core.h" #include "relay_chn_core.h"
#include "relay_chn_ctl.h" #include "relay_chn_ctl.h"
@@ -14,45 +15,45 @@
#include "relay_chn_nvs.h" #include "relay_chn_nvs.h"
#endif #endif
static const char *TAG = "RELAY_CHN_CTL"; static const char *TAG __attribute__((unused)) = "RELAY_CHN_CTL";
static relay_chn_ctl_t chn_ctl; static relay_chn_ctl_t s_chn_ctl;
esp_err_t relay_chn_ctl_init(relay_chn_output_t *output, relay_chn_run_info_t *run_info) esp_err_t relay_chn_ctl_init(relay_chn_output_t *output, relay_chn_run_info_t *run_info)
{ {
// Initialize the relay channel // Initialize the relay channel
chn_ctl.id = 0; // Single channel, so ID is 0 s_chn_ctl.id = 0; // Single channel, so ID is 0
chn_ctl.state = RELAY_CHN_STATE_IDLE; s_chn_ctl.state = RELAY_CHN_STATE_IDLE;
chn_ctl.pending_cmd = RELAY_CHN_CMD_NONE; s_chn_ctl.pending_cmd = RELAY_CHN_CMD_NONE;
chn_ctl.output = output; s_chn_ctl.output = output;
chn_ctl.run_info = run_info; s_chn_ctl.run_info = run_info;
#if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT #if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT
uint16_t run_limit_sec = CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC; uint16_t run_limit_sec = CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC;
esp_err_t ret; esp_err_t ret;
#if CONFIG_RELAY_CHN_ENABLE_NVS #if CONFIG_RELAY_CHN_ENABLE_NVS
// Load run limit value from NVS // Load run limit value from NVS
ret = relay_chn_nvs_get_run_limit(chn_ctl.id, &run_limit_sec, CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC); ret = relay_chn_nvs_get_run_limit(s_chn_ctl.id, &run_limit_sec, CONFIG_RELAY_CHN_RUN_LIMIT_DEFAULT_SEC);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to load run limit from NVS with error: %s", esp_err_to_name(ret)); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to load run limit from NVS with error: %s", esp_err_to_name(ret));
#endif #endif
chn_ctl.run_limit_sec = run_limit_sec; s_chn_ctl.run_limit_sec = run_limit_sec;
ret = relay_chn_init_run_limit_timer(&chn_ctl); ret = relay_chn_init_run_limit_timer(&s_chn_ctl);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to initialize run limit timer"); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to initialize run limit timer");
#endif #endif
return relay_chn_init_timer(&chn_ctl); // Create direction change inertia timer return relay_chn_init_timer(&s_chn_ctl); // Create direction change inertia timer
} }
void relay_chn_ctl_deinit() void relay_chn_ctl_deinit()
{ {
if (chn_ctl.inertia_timer != NULL) { if (s_chn_ctl.inertia_timer != NULL) {
esp_timer_delete(chn_ctl.inertia_timer); esp_timer_delete(s_chn_ctl.inertia_timer);
chn_ctl.inertia_timer = NULL; s_chn_ctl.inertia_timer = NULL;
} }
#if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT #if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT
if (chn_ctl.run_limit_timer != NULL) { if (s_chn_ctl.run_limit_timer != NULL) {
esp_timer_delete(chn_ctl.run_limit_timer); esp_timer_delete(s_chn_ctl.run_limit_timer);
chn_ctl.run_limit_timer = NULL; s_chn_ctl.run_limit_timer = NULL;
} }
#endif #endif
} }
@@ -60,43 +61,43 @@ void relay_chn_ctl_deinit()
/* relay_chn APIs */ /* relay_chn APIs */
relay_chn_state_t relay_chn_ctl_get_state() relay_chn_state_t relay_chn_ctl_get_state()
{ {
return chn_ctl.state; return s_chn_ctl.state;
} }
char *relay_chn_ctl_get_state_str() char *relay_chn_ctl_get_state_str()
{ {
return relay_chn_state_str(chn_ctl.state); return relay_chn_state_to_str(s_chn_ctl.state);
} }
void relay_chn_ctl_run_forward() void relay_chn_ctl_run_forward()
{ {
relay_chn_issue_cmd(&chn_ctl, RELAY_CHN_CMD_FORWARD); relay_chn_issue_cmd(&s_chn_ctl, RELAY_CHN_CMD_FORWARD);
} }
void relay_chn_ctl_run_reverse() void relay_chn_ctl_run_reverse()
{ {
relay_chn_issue_cmd(&chn_ctl, RELAY_CHN_CMD_REVERSE); relay_chn_issue_cmd(&s_chn_ctl, RELAY_CHN_CMD_REVERSE);
} }
void relay_chn_ctl_stop() void relay_chn_ctl_stop()
{ {
relay_chn_issue_cmd(&chn_ctl, RELAY_CHN_CMD_STOP); relay_chn_issue_cmd(&s_chn_ctl, RELAY_CHN_CMD_STOP);
} }
void relay_chn_ctl_flip_direction() void relay_chn_ctl_flip_direction()
{ {
relay_chn_issue_cmd(&chn_ctl, RELAY_CHN_CMD_FLIP); relay_chn_issue_cmd(&s_chn_ctl, RELAY_CHN_CMD_FLIP);
} }
relay_chn_direction_t relay_chn_ctl_get_direction() relay_chn_direction_t relay_chn_ctl_get_direction()
{ {
return relay_chn_output_get_direction(chn_ctl.output); return relay_chn_output_get_direction(s_chn_ctl.output);
} }
#if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT #if CONFIG_RELAY_CHN_ENABLE_RUN_LIMIT
uint16_t relay_chn_ctl_get_run_limit() uint16_t relay_chn_ctl_get_run_limit()
{ {
return chn_ctl.run_limit_sec; return s_chn_ctl.run_limit_sec;
} }
void relay_chn_ctl_set_run_limit(uint16_t limit_sec) void relay_chn_ctl_set_run_limit(uint16_t limit_sec)
@@ -107,10 +108,10 @@ void relay_chn_ctl_set_run_limit(uint16_t limit_sec)
else if (limit_sec < CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC) else if (limit_sec < CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC)
limit_sec = CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC; limit_sec = CONFIG_RELAY_CHN_RUN_LIMIT_MIN_SEC;
chn_ctl.run_limit_sec = limit_sec; s_chn_ctl.run_limit_sec = limit_sec;
#if CONFIG_RELAY_CHN_ENABLE_NVS #if CONFIG_RELAY_CHN_ENABLE_NVS
relay_chn_nvs_set_run_limit(chn_ctl.id, limit_sec); relay_chn_nvs_set_run_limit(s_chn_ctl.id, limit_sec);
#endif #endif
} }
#endif #endif
@@ -118,5 +119,5 @@ void relay_chn_ctl_set_run_limit(uint16_t limit_sec)
relay_chn_ctl_t *relay_chn_ctl_get() relay_chn_ctl_t *relay_chn_ctl_get()
{ {
return &chn_ctl; return &s_chn_ctl;
} }

View File

@@ -48,22 +48,22 @@ typedef struct {
} relay_chn_notify_msg_t; } relay_chn_notify_msg_t;
// The list that holds references to the registered listeners. // The list that holds references to the registered listeners.
static List_t listeners; static List_t s_listeners;
static QueueHandle_t notify_msg_queue = NULL; static QueueHandle_t s_notify_queue = NULL;
static TaskHandle_t notify_task_handle = NULL; static TaskHandle_t s_notify_task = NULL;
static void relay_chn_notify_task(void *arg); static void relay_chn_notify_task(void *arg);
esp_err_t relay_chn_notify_init(void) esp_err_t relay_chn_notify_init(void)
{ {
if (notify_msg_queue != NULL) { if (s_notify_queue != NULL) {
return ESP_OK; return ESP_OK;
} }
notify_msg_queue = xQueueCreate(RELAY_CHN_NOTIFY_QUEUE_LEN, sizeof(relay_chn_notify_msg_t)); s_notify_queue = xQueueCreate(RELAY_CHN_NOTIFY_QUEUE_LEN, sizeof(relay_chn_notify_msg_t));
if (!notify_msg_queue) { if (!s_notify_queue) {
ESP_LOGE(TAG, "Failed to create notify queue"); ESP_LOGE(TAG, "Failed to create notify queue");
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
@@ -71,34 +71,34 @@ esp_err_t relay_chn_notify_init(void)
// Create the notify dispatcher task // Create the notify dispatcher task
BaseType_t ret = xTaskCreate(relay_chn_notify_task, "task_rlch_ntfy", BaseType_t ret = xTaskCreate(relay_chn_notify_task, "task_rlch_ntfy",
RELAY_CHN_NOTIFY_TASK_STACK, NULL, RELAY_CHN_NOTIFY_TASK_STACK, NULL,
RELAY_CHN_NOTIFY_TASK_PRIO, &notify_task_handle); RELAY_CHN_NOTIFY_TASK_PRIO, &s_notify_task);
if (ret != pdPASS) { if (ret != pdPASS) {
ESP_LOGE(TAG, "Failed to create notify task"); ESP_LOGE(TAG, "Failed to create notify task");
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
// Init the state listener list // Init the state listener list
vListInitialise(&listeners); vListInitialise(&s_listeners);
return ESP_OK; return ESP_OK;
} }
void relay_chn_notify_deinit(void) void relay_chn_notify_deinit(void)
{ {
if (notify_task_handle != NULL) { if (s_notify_task != NULL) {
vTaskDelete(notify_task_handle); vTaskDelete(s_notify_task);
notify_task_handle = NULL; s_notify_task = NULL;
} }
if (notify_msg_queue != NULL) { if (s_notify_queue != NULL) {
vQueueDelete(notify_msg_queue); vQueueDelete(s_notify_queue);
notify_msg_queue = NULL; s_notify_queue = NULL;
} }
if (!listLIST_IS_EMPTY(&listeners)) { if (!listLIST_IS_EMPTY(&s_listeners)) {
// Free the listeners // Free the listeners
while (listCURRENT_LIST_LENGTH(&listeners) > 0) { while (listCURRENT_LIST_LENGTH(&s_listeners) > 0) {
ListItem_t *pxItem = listGET_HEAD_ENTRY(&listeners); ListItem_t *pxItem = listGET_HEAD_ENTRY(&s_listeners);
relay_chn_listener_entry_t *entry = listGET_LIST_ITEM_OWNER(pxItem); relay_chn_listener_entry_t *entry = listGET_LIST_ITEM_OWNER(pxItem);
uxListRemove(pxItem); uxListRemove(pxItem);
free(entry); free(entry);
@@ -118,14 +118,14 @@ void relay_chn_notify_deinit(void)
*/ */
static relay_chn_listener_entry_t* find_listener_entry(relay_chn_state_listener_t listener) static relay_chn_listener_entry_t* find_listener_entry(relay_chn_state_listener_t listener)
{ {
if (listLIST_IS_EMPTY(&listeners)) { if (listLIST_IS_EMPTY(&s_listeners)) {
ESP_LOGD(TAG, "No listeners registered"); ESP_LOGD(TAG, "No listeners registered");
return NULL; return NULL;
} }
// Iterate through the linked list of listeners // Iterate through the linked list of listeners
for (ListItem_t *pxListItem = listGET_HEAD_ENTRY(&listeners); for (ListItem_t *pxListItem = listGET_HEAD_ENTRY(&s_listeners);
pxListItem != listGET_END_MARKER(&listeners); pxListItem != listGET_END_MARKER(&s_listeners);
pxListItem = listGET_NEXT(pxListItem)) { pxListItem = listGET_NEXT(pxListItem)) {
relay_chn_listener_entry_t *entry = (relay_chn_listener_entry_t *) listGET_LIST_ITEM_OWNER(pxListItem); relay_chn_listener_entry_t *entry = (relay_chn_listener_entry_t *) listGET_LIST_ITEM_OWNER(pxListItem);
@@ -154,7 +154,7 @@ static void do_add_listener(relay_chn_state_listener_t listener)
entry->listener = listener; entry->listener = listener;
vListInitialiseItem(&(entry->list_item)); vListInitialiseItem(&(entry->list_item));
listSET_LIST_ITEM_OWNER(&(entry->list_item), (void *)entry); listSET_LIST_ITEM_OWNER(&(entry->list_item), (void *)entry);
vListInsertEnd(&listeners, &(entry->list_item)); vListInsertEnd(&s_listeners, &(entry->list_item));
ESP_LOGD(TAG, "Registered listener %p", listener); ESP_LOGD(TAG, "Registered listener %p", listener);
} }
@@ -170,19 +170,19 @@ static void do_remove_listener(relay_chn_state_listener_t listener)
ESP_LOGD(TAG, "Listener %p not found for unregistration.", listener); ESP_LOGD(TAG, "Listener %p not found for unregistration.", listener);
} }
if (listLIST_IS_EMPTY(&listeners)) { if (listLIST_IS_EMPTY(&s_listeners)) {
// Flush all pending notifications in the queue // Flush all pending notifications in the queue
xQueueReset(notify_msg_queue); xQueueReset(s_notify_queue);
} }
} }
esp_err_t relay_chn_notify_add_listener(relay_chn_state_listener_t listener) esp_err_t relay_chn_notify_add_listener(relay_chn_state_listener_t listener)
{ {
ESP_RETURN_ON_FALSE(listener, ESP_ERR_INVALID_ARG, TAG, "Listener cannot be NULL"); ESP_RETURN_ON_FALSE(listener, ESP_ERR_INVALID_ARG, TAG, "Listener cannot be NULL");
ESP_RETURN_ON_FALSE(notify_msg_queue, ESP_ERR_INVALID_STATE, TAG, "Notify module not initialized"); ESP_RETURN_ON_FALSE(s_notify_queue, ESP_ERR_INVALID_STATE, TAG, "Notify module not initialized");
relay_chn_notify_msg_t msg = { .cmd = RELAY_CHN_NOTIFY_CMD_ADD_LISTENER, .payload.listener = listener }; relay_chn_notify_msg_t msg = { .cmd = RELAY_CHN_NOTIFY_CMD_ADD_LISTENER, .payload.listener = listener };
if (xQueueSend(notify_msg_queue, &msg, 0) != pdTRUE) { if (xQueueSend(s_notify_queue, &msg, 0) != pdTRUE) {
ESP_LOGE(TAG, "Notify queue is full, failed to queue add_listener"); ESP_LOGE(TAG, "Notify queue is full, failed to queue add_listener");
return ESP_FAIL; return ESP_FAIL;
} }
@@ -195,20 +195,20 @@ void relay_chn_notify_remove_listener(relay_chn_state_listener_t listener)
ESP_LOGD(TAG, "Cannot unregister a NULL listener."); ESP_LOGD(TAG, "Cannot unregister a NULL listener.");
return; return;
} }
if (!notify_msg_queue) { if (!s_notify_queue) {
ESP_LOGE(TAG, "Notify module not initialized, cannot remove listener"); ESP_LOGE(TAG, "Notify module not initialized, cannot remove listener");
return; return;
} }
relay_chn_notify_msg_t msg = { .cmd = RELAY_CHN_NOTIFY_CMD_REMOVE_LISTENER, .payload.listener = listener }; relay_chn_notify_msg_t msg = { .cmd = RELAY_CHN_NOTIFY_CMD_REMOVE_LISTENER, .payload.listener = listener };
if (xQueueSendToFront(notify_msg_queue, &msg, 0) != pdTRUE) { if (xQueueSendToFront(s_notify_queue, &msg, 0) != pdTRUE) {
ESP_LOGW(TAG, "Notify queue is full, failed to queue remove_listener"); ESP_LOGW(TAG, "Notify queue is full, failed to queue remove_listener");
} }
} }
esp_err_t relay_chn_notify_state_change(uint8_t chn_id, relay_chn_state_t old_state, relay_chn_state_t new_state) esp_err_t relay_chn_notify_state_change(uint8_t chn_id, relay_chn_state_t old_state, relay_chn_state_t new_state)
{ {
if (!notify_msg_queue) { if (!s_notify_queue) {
return ESP_ERR_INVALID_STATE; return ESP_ERR_INVALID_STATE;
} }
@@ -220,7 +220,7 @@ esp_err_t relay_chn_notify_state_change(uint8_t chn_id, relay_chn_state_t old_st
}; };
// Try to send, do not wait if the queue is full // Try to send, do not wait if the queue is full
if (xQueueSend(notify_msg_queue, &msg, 0) != pdTRUE) { if (xQueueSend(s_notify_queue, &msg, 0) != pdTRUE) {
ESP_LOGW(TAG, "Notify queue is full, dropping event: %d -> %d for #%d", old_state, new_state, chn_id); ESP_LOGW(TAG, "Notify queue is full, dropping event: %d -> %d for #%d", old_state, new_state, chn_id);
return ESP_FAIL; return ESP_FAIL;
} }
@@ -231,8 +231,8 @@ static void do_notify(relay_chn_notify_event_data_t *event_data)
{ {
// Iterate through the linked list of listeners and notify them. // Iterate through the linked list of listeners and notify them.
// No mutex is needed as this is the only task accessing the list. // No mutex is needed as this is the only task accessing the list.
for (ListItem_t *pxListItem = listGET_HEAD_ENTRY(&listeners); for (ListItem_t *pxListItem = listGET_HEAD_ENTRY(&s_listeners);
pxListItem != listGET_END_MARKER(&listeners); pxListItem != listGET_END_MARKER(&s_listeners);
pxListItem = listGET_NEXT(pxListItem)) { pxListItem = listGET_NEXT(pxListItem)) {
relay_chn_listener_entry_t *entry = (relay_chn_listener_entry_t *) listGET_LIST_ITEM_OWNER(pxListItem); relay_chn_listener_entry_t *entry = (relay_chn_listener_entry_t *) listGET_LIST_ITEM_OWNER(pxListItem);
if (entry && entry->listener) { if (entry && entry->listener) {
@@ -247,7 +247,7 @@ static void relay_chn_notify_task(void *arg)
{ {
relay_chn_notify_msg_t msg; relay_chn_notify_msg_t msg;
for (;;) { for (;;) {
if (xQueueReceive(notify_msg_queue, &msg, portMAX_DELAY) == pdTRUE) { if (xQueueReceive(s_notify_queue, &msg, portMAX_DELAY) == pdTRUE) {
switch (msg.cmd) { switch (msg.cmd) {
case RELAY_CHN_NOTIFY_CMD_BROADCAST: { case RELAY_CHN_NOTIFY_CMD_BROADCAST: {
do_notify(&msg.payload.event_data); do_notify(&msg.payload.event_data);

View File

@@ -61,10 +61,10 @@ typedef struct {
static const char *TAG = "RELAY_CHN_NVS"; static const char *TAG = "RELAY_CHN_NVS";
static nvs_handle_t relay_chn_nvs; static nvs_handle_t s_relay_chn_nvs;
static QueueHandle_t nvs_queue_handle = NULL; static QueueHandle_t s_nvs_ops_queue = NULL;
static TaskHandle_t nvs_task_handle = NULL; static TaskHandle_t s_nvs_ops_task = NULL;
static SemaphoreHandle_t deinit_sem = NULL; static SemaphoreHandle_t s_nvs_deinit_sem = NULL;
static void relay_chn_nvs_task(void *arg); static void relay_chn_nvs_task(void *arg);
@@ -72,25 +72,25 @@ static void relay_chn_nvs_task(void *arg);
esp_err_t relay_chn_nvs_init() esp_err_t relay_chn_nvs_init()
{ {
// Already initialized? // Already initialized?
if (nvs_queue_handle != NULL) { if (s_nvs_ops_queue != NULL) {
return ESP_OK; return ESP_OK;
} }
deinit_sem = xSemaphoreCreateBinary(); s_nvs_deinit_sem = xSemaphoreCreateBinary();
if (!deinit_sem) { if (!s_nvs_deinit_sem) {
ESP_LOGE(TAG, "Failed to create deinit semaphore"); ESP_LOGE(TAG, "Failed to create deinit semaphore");
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
nvs_queue_handle = xQueueCreate(RELAY_CHN_NVS_QUEUE_LEN, sizeof(relay_chn_nvs_msg_t)); s_nvs_ops_queue = xQueueCreate(RELAY_CHN_NVS_QUEUE_LEN, sizeof(relay_chn_nvs_msg_t));
if (!nvs_queue_handle) { if (!s_nvs_ops_queue) {
ESP_LOGE(TAG, "Failed to create NVS queue"); ESP_LOGE(TAG, "Failed to create NVS queue");
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
BaseType_t res = xTaskCreate(relay_chn_nvs_task, "task_rlch_nvs", BaseType_t res = xTaskCreate(relay_chn_nvs_task, "task_rlch_nvs",
RELAY_CHN_NVS_TASK_STACK, NULL, RELAY_CHN_NVS_TASK_STACK, NULL,
RELAY_CHN_NVS_TASK_PRIO, &nvs_task_handle); RELAY_CHN_NVS_TASK_PRIO, &s_nvs_ops_task);
if (res != pdPASS) { if (res != pdPASS) {
ESP_LOGE(TAG, "Failed to create NVS task"); ESP_LOGE(TAG, "Failed to create NVS task");
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
@@ -101,7 +101,7 @@ esp_err_t relay_chn_nvs_init()
ret = nvs_open_from_partition(CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION_NAME, ret = nvs_open_from_partition(CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION_NAME,
CONFIG_RELAY_CHN_NVS_NAMESPACE, CONFIG_RELAY_CHN_NVS_NAMESPACE,
NVS_READWRITE, NVS_READWRITE,
&relay_chn_nvs); &s_relay_chn_nvs);
ESP_RETURN_ON_ERROR(ret, ESP_RETURN_ON_ERROR(ret,
TAG, TAG,
@@ -110,7 +110,7 @@ esp_err_t relay_chn_nvs_init()
CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION_NAME, CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION_NAME,
esp_err_to_name(ret)); esp_err_to_name(ret));
#else #else
ret = nvs_open(CONFIG_RELAY_CHN_NVS_NAMESPACE, NVS_READWRITE, &relay_chn_nvs); ret = nvs_open(CONFIG_RELAY_CHN_NVS_NAMESPACE, NVS_READWRITE, &s_relay_chn_nvs);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to open NVS namespace '%s'", CONFIG_RELAY_CHN_NVS_NAMESPACE); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to open NVS namespace '%s'", CONFIG_RELAY_CHN_NVS_NAMESPACE);
#endif // CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION #endif // CONFIG_RELAY_CHN_NVS_CUSTOM_PARTITION
return ESP_OK; return ESP_OK;
@@ -118,19 +118,19 @@ esp_err_t relay_chn_nvs_init()
static esp_err_t relay_chn_nvs_enqueue(relay_chn_nvs_msg_t *msg, const char *op_name) static esp_err_t relay_chn_nvs_enqueue(relay_chn_nvs_msg_t *msg, const char *op_name)
{ {
if (!nvs_queue_handle) { if (!s_nvs_ops_queue) {
return ESP_ERR_INVALID_STATE; return ESP_ERR_INVALID_STATE;
} }
if (msg->op == RELAY_CHN_NVS_OP_DEINIT || msg->op == RELAY_CHN_NVS_OP_ERASE_ALL) { if (msg->op == RELAY_CHN_NVS_OP_DEINIT || msg->op == RELAY_CHN_NVS_OP_ERASE_ALL) {
// Send DEINIT or ERASE_ALL to the front and wait up to 1 sec if needed // Send DEINIT or ERASE_ALL to the front and wait up to 1 sec if needed
if (xQueueSendToFront(nvs_queue_handle, msg, pdMS_TO_TICKS(1000)) != pdTRUE) { if (xQueueSendToFront(s_nvs_ops_queue, msg, pdMS_TO_TICKS(1000)) != pdTRUE) {
ESP_LOGW(TAG, "NVS queue is full, dropping %s for #%d", op_name, msg->ch); ESP_LOGW(TAG, "NVS queue is full, dropping %s for #%d", op_name, msg->ch);
return ESP_FAIL; return ESP_FAIL;
} }
} else { } else {
// Send async // Send async
if (xQueueSend(nvs_queue_handle, msg, 0) != pdTRUE) { if (xQueueSend(s_nvs_ops_queue, msg, 0) != pdTRUE) {
ESP_LOGW(TAG, "NVS queue is full, dropping %s for #%d", op_name, msg->ch); ESP_LOGW(TAG, "NVS queue is full, dropping %s for #%d", op_name, msg->ch);
return ESP_FAIL; return ESP_FAIL;
} }
@@ -151,13 +151,13 @@ esp_err_t relay_chn_nvs_set_direction(uint8_t ch, relay_chn_direction_t directio
static esp_err_t relay_chn_nvs_task_set_direction(uint8_t ch, uint8_t direction) static esp_err_t relay_chn_nvs_task_set_direction(uint8_t ch, uint8_t direction)
{ {
uint8_t direction_val = 0; uint8_t direction_val = 0;
esp_err_t ret = nvs_get_u8(relay_chn_nvs, RELAY_CHN_KEY_DIR, &direction_val); esp_err_t ret = nvs_get_u8(s_relay_chn_nvs, RELAY_CHN_KEY_DIR, &direction_val);
if (ret != ESP_OK && ret != ESP_ERR_NVS_NOT_FOUND) { if (ret != ESP_OK && ret != ESP_ERR_NVS_NOT_FOUND) {
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to get direction from NVS with error: %s", esp_err_to_name(ret)); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to get direction from NVS with error: %s", esp_err_to_name(ret));
} }
direction_val &= ~(1 << ch); // Clear the bit for the channel direction_val &= ~(1 << ch); // Clear the bit for the channel
direction_val |= (((uint8_t) direction) << ch); // Set the new direction bit direction_val |= (((uint8_t) direction) << ch); // Set the new direction bit
ret = nvs_set_u8(relay_chn_nvs, RELAY_CHN_KEY_DIR, direction_val); ret = nvs_set_u8(s_relay_chn_nvs, RELAY_CHN_KEY_DIR, direction_val);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to set direction for channel %d", ch); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to set direction for channel %d", ch);
return ESP_OK; return ESP_OK;
} }
@@ -167,7 +167,7 @@ esp_err_t relay_chn_nvs_get_direction(uint8_t ch, relay_chn_direction_t *directi
ESP_RETURN_ON_FALSE(direction != NULL, ESP_ERR_INVALID_ARG, TAG, "Direction pointer is NULL"); ESP_RETURN_ON_FALSE(direction != NULL, ESP_ERR_INVALID_ARG, TAG, "Direction pointer is NULL");
uint8_t direction_val; uint8_t direction_val;
esp_err_t ret = nvs_get_u8(relay_chn_nvs, RELAY_CHN_KEY_DIR, &direction_val); esp_err_t ret = nvs_get_u8(s_relay_chn_nvs, RELAY_CHN_KEY_DIR, &direction_val);
if (ret == ESP_ERR_NVS_NOT_FOUND) { if (ret == ESP_ERR_NVS_NOT_FOUND) {
*direction = default_val; *direction = default_val;
return ESP_OK; return ESP_OK;
@@ -196,9 +196,9 @@ static esp_err_t relay_chn_nvs_task_set_run_limit(uint8_t ch, uint16_t limit_sec
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
char key[NVS_KEY_NAME_MAX_SIZE]; char key[NVS_KEY_NAME_MAX_SIZE];
snprintf(key, sizeof(key), RELAY_CHN_KEY_RLIM_FMT, ch); snprintf(key, sizeof(key), RELAY_CHN_KEY_RLIM_FMT, ch);
ret = nvs_set_u16(relay_chn_nvs, key, limit_sec); ret = nvs_set_u16(s_relay_chn_nvs, key, limit_sec);
#else #else
ret = nvs_set_u16(relay_chn_nvs, RELAY_CHN_KEY_RLIM, limit_sec); ret = nvs_set_u16(s_relay_chn_nvs, RELAY_CHN_KEY_RLIM, limit_sec);
#endif #endif
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to set run limit for channel %d", ch); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to set run limit for channel %d", ch);
return ESP_OK; return ESP_OK;
@@ -212,9 +212,9 @@ esp_err_t relay_chn_nvs_get_run_limit(uint8_t ch, uint16_t *limit_sec, uint16_t
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
char key[NVS_KEY_NAME_MAX_SIZE]; char key[NVS_KEY_NAME_MAX_SIZE];
snprintf(key, sizeof(key), RELAY_CHN_KEY_RLIM_FMT, ch); snprintf(key, sizeof(key), RELAY_CHN_KEY_RLIM_FMT, ch);
ret = nvs_get_u16(relay_chn_nvs, key, limit_sec); ret = nvs_get_u16(s_relay_chn_nvs, key, limit_sec);
#else #else
ret = nvs_get_u16(relay_chn_nvs, RELAY_CHN_KEY_RLIM, limit_sec); ret = nvs_get_u16(s_relay_chn_nvs, RELAY_CHN_KEY_RLIM, limit_sec);
#endif #endif
if (ret == ESP_ERR_NVS_NOT_FOUND) { if (ret == ESP_ERR_NVS_NOT_FOUND) {
*limit_sec = default_val; *limit_sec = default_val;
@@ -241,9 +241,9 @@ static esp_err_t relay_chn_nvs_task_set_tilt_sensitivity(uint8_t ch, uint8_t sen
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
char key[NVS_KEY_NAME_MAX_SIZE]; char key[NVS_KEY_NAME_MAX_SIZE];
snprintf(key, sizeof(key), RELAY_CHN_KEY_TSENS_FMT, ch); snprintf(key, sizeof(key), RELAY_CHN_KEY_TSENS_FMT, ch);
ret = nvs_set_u8(relay_chn_nvs, key, sensitivity); ret = nvs_set_u8(s_relay_chn_nvs, key, sensitivity);
#else #else
ret = nvs_set_u8(relay_chn_nvs, RELAY_CHN_KEY_TSENS, sensitivity); ret = nvs_set_u8(s_relay_chn_nvs, RELAY_CHN_KEY_TSENS, sensitivity);
#endif #endif
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to set tilt sensitivity for channel %d", ch); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to set tilt sensitivity for channel %d", ch);
return ESP_OK; return ESP_OK;
@@ -257,9 +257,9 @@ esp_err_t relay_chn_nvs_get_tilt_sensitivity(uint8_t ch, uint8_t *sensitivity, u
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
char key[NVS_KEY_NAME_MAX_SIZE]; char key[NVS_KEY_NAME_MAX_SIZE];
snprintf(key, sizeof(key), RELAY_CHN_KEY_TSENS_FMT, ch); snprintf(key, sizeof(key), RELAY_CHN_KEY_TSENS_FMT, ch);
ret = nvs_get_u8(relay_chn_nvs, key, sensitivity); ret = nvs_get_u8(s_relay_chn_nvs, key, sensitivity);
#else #else
ret = nvs_get_u8(relay_chn_nvs, RELAY_CHN_KEY_TSENS, sensitivity); ret = nvs_get_u8(s_relay_chn_nvs, RELAY_CHN_KEY_TSENS, sensitivity);
#endif #endif
if (ret == ESP_ERR_NVS_NOT_FOUND) { if (ret == ESP_ERR_NVS_NOT_FOUND) {
*sensitivity = default_val; *sensitivity = default_val;
@@ -284,9 +284,9 @@ static esp_err_t relay_chn_nvs_task_set_tilt_count(uint8_t ch, uint16_t tilt_cou
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
char key[NVS_KEY_NAME_MAX_SIZE]; char key[NVS_KEY_NAME_MAX_SIZE];
snprintf(key, sizeof(key), RELAY_CHN_KEY_TCNT_FMT, ch); snprintf(key, sizeof(key), RELAY_CHN_KEY_TCNT_FMT, ch);
ret = nvs_set_u16(relay_chn_nvs, key, tilt_count); ret = nvs_set_u16(s_relay_chn_nvs, key, tilt_count);
#else #else
ret = nvs_set_u16(relay_chn_nvs, RELAY_CHN_KEY_TCNT, tilt_count); ret = nvs_set_u16(s_relay_chn_nvs, RELAY_CHN_KEY_TCNT, tilt_count);
#endif #endif
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to save tilt_count tilt counter"); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to save tilt_count tilt counter");
return ESP_OK; return ESP_OK;
@@ -300,9 +300,9 @@ esp_err_t relay_chn_nvs_get_tilt_count(uint8_t ch, uint16_t *tilt_count, uint16_
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
char key[NVS_KEY_NAME_MAX_SIZE]; char key[NVS_KEY_NAME_MAX_SIZE];
snprintf(key, sizeof(key), RELAY_CHN_KEY_TCNT_FMT, ch); snprintf(key, sizeof(key), RELAY_CHN_KEY_TCNT_FMT, ch);
ret = nvs_get_u16(relay_chn_nvs, key, tilt_count); ret = nvs_get_u16(s_relay_chn_nvs, key, tilt_count);
#else #else
ret = nvs_get_u16(relay_chn_nvs, RELAY_CHN_KEY_TCNT, tilt_count); ret = nvs_get_u16(s_relay_chn_nvs, RELAY_CHN_KEY_TCNT, tilt_count);
#endif #endif
if (ret == ESP_ERR_NVS_NOT_FOUND) { if (ret == ESP_ERR_NVS_NOT_FOUND) {
*tilt_count = default_val; *tilt_count = default_val;
@@ -331,38 +331,38 @@ static esp_err_t do_nvs_deinit()
static esp_err_t do_nvs_erase_all() static esp_err_t do_nvs_erase_all()
{ {
// Flush all pending SET operations since ERASE_ALL requested // Flush all pending SET operations since ERASE_ALL requested
xQueueReset(nvs_queue_handle); xQueueReset(s_nvs_ops_queue);
// Erase all key-value pairs in the relay_chn NVS namespace // Erase all key-value pairs in the relay_chn NVS namespace
esp_err_t ret = nvs_erase_all(relay_chn_nvs); esp_err_t ret = nvs_erase_all(s_relay_chn_nvs);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to erase all keys in NVS namespace '%s'", CONFIG_RELAY_CHN_NVS_NAMESPACE); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to erase all keys in NVS namespace '%s'", CONFIG_RELAY_CHN_NVS_NAMESPACE);
return ESP_OK; return ESP_OK;
} }
void relay_chn_nvs_deinit() void relay_chn_nvs_deinit()
{ {
if (nvs_task_handle) { if (s_nvs_ops_task) {
if (do_nvs_deinit() == ESP_OK) { if (do_nvs_deinit() == ESP_OK) {
if (deinit_sem && xSemaphoreTake(deinit_sem, pdMS_TO_TICKS(2000)) != pdTRUE) { if (s_nvs_deinit_sem && xSemaphoreTake(s_nvs_deinit_sem, pdMS_TO_TICKS(2000)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to get deinit confirmation from NVS task. Forcing deletion."); ESP_LOGE(TAG, "Failed to get deinit confirmation from NVS task. Forcing deletion.");
vTaskDelete(nvs_task_handle); // Last resort vTaskDelete(s_nvs_ops_task); // Last resort
} }
} else { } else {
ESP_LOGE(TAG, "Failed to send deinit message to NVS task. Forcing deletion."); ESP_LOGE(TAG, "Failed to send deinit message to NVS task. Forcing deletion.");
vTaskDelete(nvs_task_handle); vTaskDelete(s_nvs_ops_task);
} }
} }
if (nvs_queue_handle) { if (s_nvs_ops_queue) {
vQueueDelete(nvs_queue_handle); vQueueDelete(s_nvs_ops_queue);
nvs_queue_handle = NULL; s_nvs_ops_queue = NULL;
} }
if (deinit_sem) { if (s_nvs_deinit_sem) {
vSemaphoreDelete(deinit_sem); vSemaphoreDelete(s_nvs_deinit_sem);
deinit_sem = NULL; s_nvs_deinit_sem = NULL;
} }
// Close NVS handle here, after task has stopped and queue is deleted. // Close NVS handle here, after task has stopped and queue is deleted.
nvs_close(relay_chn_nvs); nvs_close(s_relay_chn_nvs);
nvs_task_handle = NULL; s_nvs_ops_task = NULL;
} }
static esp_err_t relay_chn_nvs_task_process_message(const relay_chn_nvs_msg_t *msg, bool *running, bool *dirty) static esp_err_t relay_chn_nvs_task_process_message(const relay_chn_nvs_msg_t *msg, bool *running, bool *dirty)
@@ -417,7 +417,7 @@ static void relay_chn_nvs_task(void *arg)
while (running) { while (running) {
// Block indefinitely waiting for the first message of a potential batch. // Block indefinitely waiting for the first message of a potential batch.
if (xQueueReceive(nvs_queue_handle, &msg, portMAX_DELAY) == pdTRUE) { if (xQueueReceive(s_nvs_ops_queue, &msg, portMAX_DELAY) == pdTRUE) {
// A batch of operations has started. Use a do-while to process the first message // A batch of operations has started. Use a do-while to process the first message
// and any subsequent messages that arrive within the timeout. // and any subsequent messages that arrive within the timeout.
do { do {
@@ -425,11 +425,11 @@ static void relay_chn_nvs_task(void *arg)
if (ret != ESP_OK) { if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to process operation %d for #%d with error %s", msg.op, msg.ch, esp_err_to_name(ret)); ESP_LOGE(TAG, "Failed to process operation %d for #%d with error %s", msg.op, msg.ch, esp_err_to_name(ret));
} }
} while (running && xQueueReceive(nvs_queue_handle, &msg, pdMS_TO_TICKS(RELAY_CHN_NVS_COMMIT_TIMEOUT_MS)) == pdTRUE); } while (running && xQueueReceive(s_nvs_ops_queue, &msg, pdMS_TO_TICKS(RELAY_CHN_NVS_COMMIT_TIMEOUT_MS)) == pdTRUE);
// The burst of messages is over (timeout occurred). Commit if anything changed. // The burst of messages is over (timeout occurred). Commit if anything changed.
if (dirty) { if (dirty) {
esp_err_t commit_ret = nvs_commit(relay_chn_nvs); esp_err_t commit_ret = nvs_commit(s_relay_chn_nvs);
if (commit_ret == ESP_OK) { if (commit_ret == ESP_OK) {
dirty = false; dirty = false;
} else { } else {
@@ -442,11 +442,11 @@ static void relay_chn_nvs_task(void *arg)
// Before exiting, do one final commit if there are pending changes. // Before exiting, do one final commit if there are pending changes.
if (dirty) { if (dirty) {
if (nvs_commit(relay_chn_nvs) != ESP_OK) { if (nvs_commit(s_relay_chn_nvs) != ESP_OK) {
ESP_LOGE(TAG, "Final NVS commit failed on deinit"); ESP_LOGE(TAG, "Final NVS commit failed on deinit");
} }
} }
xSemaphoreGive(deinit_sem); xSemaphoreGive(s_nvs_deinit_sem);
nvs_task_handle = NULL; s_nvs_ops_task = NULL;
vTaskDelete(NULL); vTaskDelete(NULL);
} }

View File

@@ -17,9 +17,9 @@
static const char *TAG = "RELAY_CHN_OUTPUT"; static const char *TAG = "RELAY_CHN_OUTPUT";
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
static relay_chn_output_t outputs[CONFIG_RELAY_CHN_COUNT]; static relay_chn_output_t s_outputs[CONFIG_RELAY_CHN_COUNT];
#else #else
static relay_chn_output_t output; static relay_chn_output_t s_output;
#endif #endif
@@ -90,7 +90,7 @@ esp_err_t relay_chn_output_init(const uint8_t* gpio_map, uint8_t gpio_count)
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
relay_chn_output_t* output = &outputs[i]; relay_chn_output_t* output = &s_outputs[i];
int gpio_index = i << 1; // gpio_index = i * 2 int gpio_index = i << 1; // gpio_index = i * 2
gpio_num_t forward_pin = (gpio_num_t) gpio_map[gpio_index]; gpio_num_t forward_pin = (gpio_num_t) gpio_map[gpio_index];
gpio_num_t reverse_pin = (gpio_num_t) gpio_map[gpio_index + 1]; gpio_num_t reverse_pin = (gpio_num_t) gpio_map[gpio_index + 1];
@@ -111,7 +111,7 @@ esp_err_t relay_chn_output_init(const uint8_t* gpio_map, uint8_t gpio_count)
ret = relay_chn_output_load_direction(0, &direction); ret = relay_chn_output_load_direction(0, &direction);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to load direction from storage for channel %d", 0); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to load direction from storage for channel %d", 0);
#endif #endif
ret = relay_chn_output_ctl_init(&output, gpio_map[0], gpio_map[1], direction); ret = relay_chn_output_ctl_init(&s_output, gpio_map[0], gpio_map[1], direction);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to initialize relay channel"); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to initialize relay channel");
#endif #endif
return ESP_OK; return ESP_OK;
@@ -127,10 +127,10 @@ void relay_chn_output_deinit()
{ {
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
relay_chn_output_ctl_deinit(&outputs[i]); relay_chn_output_ctl_deinit(&s_outputs[i]);
} }
#else #else
relay_chn_output_ctl_deinit(&output); relay_chn_output_ctl_deinit(&s_output);
#endif // CONFIG_RELAY_CHN_COUNT > 1 #endif // CONFIG_RELAY_CHN_COUNT > 1
} }
@@ -140,17 +140,17 @@ relay_chn_output_t *relay_chn_output_get(uint8_t chn_id)
if (!relay_chn_is_channel_id_valid(chn_id)) { if (!relay_chn_is_channel_id_valid(chn_id)) {
return NULL; return NULL;
} }
return &outputs[chn_id]; return &s_outputs[chn_id];
} }
relay_chn_output_t *relay_chn_output_get_all(void) relay_chn_output_t *relay_chn_output_get_all(void)
{ {
return outputs; return s_outputs;
} }
#else #else
relay_chn_output_t *relay_chn_output_get(void) relay_chn_output_t *relay_chn_output_get(void)
{ {
return &output; return &s_output;
} }
#endif // CONFIG_RELAY_CHN_COUNT > 1 #endif // CONFIG_RELAY_CHN_COUNT > 1
@@ -193,7 +193,7 @@ void relay_chn_output_flip(relay_chn_output_t *output)
uint8_t ch = 0; uint8_t ch = 0;
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
if (output == &outputs[i]) { if (output == &s_outputs[i]) {
ch = i; ch = i;
break; break;
} }

View File

@@ -9,21 +9,21 @@
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
static relay_chn_run_info_t run_infos[CONFIG_RELAY_CHN_COUNT]; static relay_chn_run_info_t s_run_infos[CONFIG_RELAY_CHN_COUNT];
#else #else
static relay_chn_run_info_t run_info; static relay_chn_run_info_t s_run_info;
#endif #endif
void relay_chn_run_info_init() void relay_chn_run_info_init()
{ {
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
run_infos[i].last_run_cmd = RELAY_CHN_CMD_NONE; s_run_infos[i].last_run_cmd = RELAY_CHN_CMD_NONE;
run_infos[i].last_run_cmd_time_ms = 0; s_run_infos[i].last_run_cmd_time_ms = 0;
} }
#else #else
run_info.last_run_cmd = RELAY_CHN_CMD_NONE; s_run_info.last_run_cmd = RELAY_CHN_CMD_NONE;
run_info.last_run_cmd_time_ms = 0; s_run_info.last_run_cmd_time_ms = 0;
#endif #endif
} }
@@ -33,17 +33,17 @@ relay_chn_run_info_t *relay_chn_run_info_get(uint8_t chn_id)
if (!relay_chn_is_channel_id_valid(chn_id)) { if (!relay_chn_is_channel_id_valid(chn_id)) {
return NULL; return NULL;
} }
return &run_infos[chn_id]; return &s_run_infos[chn_id];
} }
relay_chn_run_info_t *relay_chn_run_info_get_all() relay_chn_run_info_t *relay_chn_run_info_get_all()
{ {
return run_infos; return s_run_infos;
} }
#else #else
relay_chn_run_info_t *relay_chn_run_info_get() relay_chn_run_info_t *relay_chn_run_info_get()
{ {
return &run_info; return &s_run_info;
} }
#endif // CONFIG_RELAY_CHN_COUNT > 1 #endif // CONFIG_RELAY_CHN_COUNT > 1

View File

@@ -5,6 +5,7 @@
*/ */
#include "esp_check.h" #include "esp_check.h"
#include "relay_chn.h"
#include "relay_chn_core.h" #include "relay_chn_core.h"
#include "relay_chn_output.h" #include "relay_chn_output.h"
#include "relay_chn_run_info.h" #include "relay_chn_run_info.h"
@@ -71,9 +72,9 @@ typedef struct relay_chn_tilt_ctl {
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
static relay_chn_tilt_ctl_t tilt_ctls[CONFIG_RELAY_CHN_COUNT]; static relay_chn_tilt_ctl_t s_tilt_ctls[CONFIG_RELAY_CHN_COUNT];
#else #else
static relay_chn_tilt_ctl_t tilt_ctl; static relay_chn_tilt_ctl_t s_tilt_ctl;
#endif #endif
@@ -103,16 +104,47 @@ static void relay_chn_tilt_start_timer_or_stop(relay_chn_tilt_ctl_t *tilt_ctl, e
} }
} }
/**
* @brief Checks if the relay channel can perform the current tilt command.
*
* This function evaluates whether a tilt command can be executed based on the
* channel's history. The rules are as follows:
* - Tilting in the same direction as the last full run command (e.g., TILT_FORWARD
* after a FORWARD run) is always allowed.
* - Tilting in the opposite direction of the last full run (e.g., TILT_REVERSE
* after a FORWARD run) is only allowed if the tilt counter is greater than zero,
* which indicates that the channel has previously tilted in the primary direction.
* - If the channel has not been run before, tilting is not allowed.
*
* @param tilt_ctl Pointer to the tilt control structure for the channel.
* @param tilt_cmd The tilt command to check against.
*
* @return true if the tilt command is allowed, false otherwise.
*/
static bool relay_chn_can_perform_tilt_cmd(relay_chn_tilt_ctl_t *tilt_ctl, relay_chn_tilt_cmd_t tilt_cmd)
{
relay_chn_cmd_t last_run_cmd = relay_chn_run_info_get_last_run_cmd(tilt_ctl->chn_ctl->run_info);
if (last_run_cmd == RELAY_CHN_CMD_FORWARD) {
return (tilt_cmd == RELAY_CHN_TILT_CMD_FORWARD) ||
(tilt_cmd == RELAY_CHN_TILT_CMD_REVERSE && tilt_ctl->tilt_count > 0);
} else if (last_run_cmd == RELAY_CHN_CMD_REVERSE) {
return (tilt_cmd == RELAY_CHN_TILT_CMD_REVERSE) ||
(tilt_cmd == RELAY_CHN_TILT_CMD_FORWARD && tilt_ctl->tilt_count > 0);
}
return false;
}
// Issue a tilt command to a specific relay channel. // Issue a tilt command to a specific relay channel.
static void relay_chn_tilt_issue_cmd(relay_chn_tilt_ctl_t *tilt_ctl, relay_chn_tilt_cmd_t cmd) static void relay_chn_tilt_issue_cmd(relay_chn_tilt_ctl_t *tilt_ctl, relay_chn_tilt_cmd_t cmd)
{ {
// TILT_STOP is safe and high priority // TILT_STOP is safe and high priority
if (cmd == RELAY_CHN_TILT_CMD_STOP) { if (cmd == RELAY_CHN_TILT_CMD_STOP) {
if (tilt_ctl->chn_ctl->state == RELAY_CHN_STATE_STOPPED) { relay_chn_state_t state = tilt_ctl->chn_ctl->state;
return; // Do nothing if already stopped if (state == RELAY_CHN_STATE_TILT_FORWARD || state == RELAY_CHN_STATE_TILT_REVERSE) {
// If the command is TILT_STOP, issue it immediately
relay_chn_tilt_dispatch_cmd(tilt_ctl, cmd);
} }
// If the command is TILT_STOP, issue it immediately
relay_chn_tilt_dispatch_cmd(tilt_ctl, cmd);
return; return;
} }
@@ -127,6 +159,11 @@ static void relay_chn_tilt_issue_cmd(relay_chn_tilt_ctl_t *tilt_ctl, relay_chn_t
return; return;
} }
if (!relay_chn_can_perform_tilt_cmd(tilt_ctl, cmd)) {
ESP_LOGD(TAG, "Cannot perform tilt command: %d for #%d", cmd, tilt_ctl->chn_ctl->id);
return;
}
// Set the command that will be processed // Set the command that will be processed
tilt_ctl->cmd = cmd; tilt_ctl->cmd = cmd;
switch (tilt_ctl->chn_ctl->state) { switch (tilt_ctl->chn_ctl->state) {
@@ -184,7 +221,7 @@ static void relay_chn_tilt_issue_cmd(relay_chn_tilt_ctl_t *tilt_ctl, relay_chn_t
break; break;
default: default:
ESP_LOGD(TAG, "relay_chn_tilt_issue_cmd: Unexpected relay channel state: %s!", relay_chn_state_str(tilt_ctl->chn_ctl->state)); ESP_LOGD(TAG, "relay_chn_tilt_issue_cmd: Unexpected relay channel state: %s!", relay_chn_state_to_str(tilt_ctl->chn_ctl->state));
} }
} }
@@ -209,7 +246,7 @@ uint8_t relay_chn_tilt_get_default_sensitivity()
static void relay_chn_tilt_issue_cmd_on_all_channels(relay_chn_tilt_cmd_t cmd) static void relay_chn_tilt_issue_cmd_on_all_channels(relay_chn_tilt_cmd_t cmd)
{ {
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
relay_chn_tilt_ctl_t* tilt_ctl = &tilt_ctls[i]; relay_chn_tilt_ctl_t* tilt_ctl = &s_tilt_ctls[i];
relay_chn_tilt_issue_cmd(tilt_ctl, cmd); relay_chn_tilt_issue_cmd(tilt_ctl, cmd);
} }
} }
@@ -217,21 +254,21 @@ static void relay_chn_tilt_issue_cmd_on_all_channels(relay_chn_tilt_cmd_t cmd)
void relay_chn_tilt_auto(uint8_t chn_id) void relay_chn_tilt_auto(uint8_t chn_id)
{ {
if (relay_chn_is_channel_id_valid(chn_id)) { if (relay_chn_is_channel_id_valid(chn_id)) {
relay_chn_tilt_issue_auto(&tilt_ctls[chn_id]); relay_chn_tilt_issue_auto(&s_tilt_ctls[chn_id]);
} }
} }
void relay_chn_tilt_auto_all() void relay_chn_tilt_auto_all()
{ {
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
relay_chn_tilt_issue_auto(&tilt_ctls[i]); relay_chn_tilt_issue_auto(&s_tilt_ctls[i]);
} }
} }
void relay_chn_tilt_forward(uint8_t chn_id) void relay_chn_tilt_forward(uint8_t chn_id)
{ {
if (relay_chn_is_channel_id_valid(chn_id)) { if (relay_chn_is_channel_id_valid(chn_id)) {
relay_chn_tilt_issue_cmd(&tilt_ctls[chn_id], RELAY_CHN_TILT_CMD_FORWARD); relay_chn_tilt_issue_cmd(&s_tilt_ctls[chn_id], RELAY_CHN_TILT_CMD_FORWARD);
} }
} }
@@ -243,7 +280,7 @@ void relay_chn_tilt_forward_all()
void relay_chn_tilt_reverse(uint8_t chn_id) void relay_chn_tilt_reverse(uint8_t chn_id)
{ {
if (relay_chn_is_channel_id_valid(chn_id)) { if (relay_chn_is_channel_id_valid(chn_id)) {
relay_chn_tilt_issue_cmd(&tilt_ctls[chn_id], RELAY_CHN_TILT_CMD_REVERSE); relay_chn_tilt_issue_cmd(&s_tilt_ctls[chn_id], RELAY_CHN_TILT_CMD_REVERSE);
} }
} }
@@ -254,8 +291,8 @@ void relay_chn_tilt_reverse_all()
void relay_chn_tilt_stop(uint8_t chn_id) void relay_chn_tilt_stop(uint8_t chn_id)
{ {
if (!relay_chn_is_channel_id_valid(chn_id)) { if (relay_chn_is_channel_id_valid(chn_id)) {
relay_chn_tilt_dispatch_cmd(&tilt_ctls[chn_id], RELAY_CHN_TILT_CMD_STOP); relay_chn_tilt_dispatch_cmd(&s_tilt_ctls[chn_id], RELAY_CHN_TILT_CMD_STOP);
} }
} }
@@ -268,22 +305,22 @@ void relay_chn_tilt_stop_all()
void relay_chn_tilt_auto() void relay_chn_tilt_auto()
{ {
relay_chn_tilt_issue_auto(&tilt_ctl); relay_chn_tilt_issue_auto(&s_tilt_ctl);
} }
void relay_chn_tilt_forward() void relay_chn_tilt_forward()
{ {
relay_chn_tilt_issue_cmd(&tilt_ctl, RELAY_CHN_TILT_CMD_FORWARD); relay_chn_tilt_issue_cmd(&s_tilt_ctl, RELAY_CHN_TILT_CMD_FORWARD);
} }
void relay_chn_tilt_reverse() void relay_chn_tilt_reverse()
{ {
relay_chn_tilt_issue_cmd(&tilt_ctl, RELAY_CHN_TILT_CMD_REVERSE); relay_chn_tilt_issue_cmd(&s_tilt_ctl, RELAY_CHN_TILT_CMD_REVERSE);
} }
void relay_chn_tilt_stop() void relay_chn_tilt_stop()
{ {
relay_chn_tilt_dispatch_cmd(&tilt_ctl, RELAY_CHN_TILT_CMD_STOP); relay_chn_tilt_issue_cmd(&s_tilt_ctl, RELAY_CHN_TILT_CMD_STOP);
} }
#endif // CONFIG_RELAY_CHN_COUNT > 1 #endif // CONFIG_RELAY_CHN_COUNT > 1
@@ -335,7 +372,7 @@ void relay_chn_tilt_set_sensitivity(uint8_t chn_id, uint8_t sensitivity)
{ {
if (relay_chn_is_channel_id_valid(chn_id)) { if (relay_chn_is_channel_id_valid(chn_id)) {
ADJUST_TILT_SENS_BOUNDARIES(sensitivity); ADJUST_TILT_SENS_BOUNDARIES(sensitivity);
relay_chn_tilt_compute_set_sensitivity(&tilt_ctls[chn_id], sensitivity); relay_chn_tilt_compute_set_sensitivity(&s_tilt_ctls[chn_id], sensitivity);
#if CONFIG_RELAY_CHN_ENABLE_NVS #if CONFIG_RELAY_CHN_ENABLE_NVS
relay_chn_nvs_set_tilt_sensitivity(chn_id, sensitivity); relay_chn_nvs_set_tilt_sensitivity(chn_id, sensitivity);
@@ -354,7 +391,7 @@ esp_err_t relay_chn_tilt_set_sensitivity_all(uint8_t *sensitivities)
break; break;
} }
ADJUST_TILT_SENS_BOUNDARIES(*src_sensitivity); ADJUST_TILT_SENS_BOUNDARIES(*src_sensitivity);
relay_chn_tilt_compute_set_sensitivity(&tilt_ctls[i], *src_sensitivity); relay_chn_tilt_compute_set_sensitivity(&s_tilt_ctls[i], *src_sensitivity);
#if CONFIG_RELAY_CHN_ENABLE_NVS #if CONFIG_RELAY_CHN_ENABLE_NVS
relay_chn_nvs_set_tilt_sensitivity(i, *src_sensitivity); relay_chn_nvs_set_tilt_sensitivity(i, *src_sensitivity);
#endif // CONFIG_RELAY_CHN_ENABLE_NVS #endif // CONFIG_RELAY_CHN_ENABLE_NVS
@@ -366,7 +403,7 @@ void relay_chn_tilt_set_sensitivity_all_with(uint8_t sensitivity)
{ {
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
ADJUST_TILT_SENS_BOUNDARIES(sensitivity); ADJUST_TILT_SENS_BOUNDARIES(sensitivity);
relay_chn_tilt_compute_set_sensitivity(&tilt_ctls[i], sensitivity); relay_chn_tilt_compute_set_sensitivity(&s_tilt_ctls[i], sensitivity);
#if CONFIG_RELAY_CHN_ENABLE_NVS #if CONFIG_RELAY_CHN_ENABLE_NVS
relay_chn_nvs_set_tilt_sensitivity(i, sensitivity); relay_chn_nvs_set_tilt_sensitivity(i, sensitivity);
#endif // CONFIG_RELAY_CHN_ENABLE_NVS #endif // CONFIG_RELAY_CHN_ENABLE_NVS
@@ -376,7 +413,7 @@ void relay_chn_tilt_set_sensitivity_all_with(uint8_t sensitivity)
uint8_t relay_chn_tilt_get_sensitivity(uint8_t chn_id) uint8_t relay_chn_tilt_get_sensitivity(uint8_t chn_id)
{ {
return relay_chn_is_channel_id_valid(chn_id) ? return relay_chn_is_channel_id_valid(chn_id) ?
tilt_ctls[chn_id].tilt_timing.sensitivity : 0; s_tilt_ctls[chn_id].tilt_timing.sensitivity : 0;
} }
esp_err_t relay_chn_tilt_get_sensitivity_all(uint8_t *sensitivities) esp_err_t relay_chn_tilt_get_sensitivity_all(uint8_t *sensitivities)
@@ -389,7 +426,7 @@ esp_err_t relay_chn_tilt_get_sensitivity_all(uint8_t *sensitivities)
ESP_LOGW(TAG, "get_sensitivity_all: Sensitivites have been copied until channel %d since sensitivities[%d] is NULL", i, i); ESP_LOGW(TAG, "get_sensitivity_all: Sensitivites have been copied until channel %d since sensitivities[%d] is NULL", i, i);
break; break;
} }
*dest_sensitivity = tilt_ctls[i].tilt_timing.sensitivity; *dest_sensitivity = s_tilt_ctls[i].tilt_timing.sensitivity;
} }
return ESP_OK; return ESP_OK;
} }
@@ -399,7 +436,7 @@ esp_err_t relay_chn_tilt_get_sensitivity_all(uint8_t *sensitivities)
void relay_chn_tilt_set_sensitivity(uint8_t sensitivity) void relay_chn_tilt_set_sensitivity(uint8_t sensitivity)
{ {
ADJUST_TILT_SENS_BOUNDARIES(sensitivity); ADJUST_TILT_SENS_BOUNDARIES(sensitivity);
relay_chn_tilt_compute_set_sensitivity(&tilt_ctl, sensitivity); relay_chn_tilt_compute_set_sensitivity(&s_tilt_ctl, sensitivity);
#if CONFIG_RELAY_CHN_ENABLE_NVS #if CONFIG_RELAY_CHN_ENABLE_NVS
relay_chn_nvs_set_tilt_sensitivity(0, sensitivity); relay_chn_nvs_set_tilt_sensitivity(0, sensitivity);
@@ -408,7 +445,7 @@ void relay_chn_tilt_set_sensitivity(uint8_t sensitivity)
uint8_t relay_chn_tilt_get_sensitivity() uint8_t relay_chn_tilt_get_sensitivity()
{ {
return tilt_ctl.tilt_timing.sensitivity; return s_tilt_ctl.tilt_timing.sensitivity;
} }
#endif // CONFIG_RELAY_CHN_COUNT > 1 #endif // CONFIG_RELAY_CHN_COUNT > 1
@@ -707,7 +744,7 @@ esp_err_t relay_chn_tilt_init(relay_chn_ctl_t *chn_ctls)
sensitivity = RELAY_CHN_TILT_DEFAULT_SENSITIVITY; sensitivity = RELAY_CHN_TILT_DEFAULT_SENSITIVITY;
tilt_count = 0; tilt_count = 0;
#endif // CONFIG_RELAY_CHN_ENABLE_NVS == 1 #endif // CONFIG_RELAY_CHN_ENABLE_NVS == 1
ret = relay_chn_tilt_ctl_init(&tilt_ctls[i], &chn_ctls[i], tilt_count, sensitivity); ret = relay_chn_tilt_ctl_init(&s_tilt_ctls[i], &chn_ctls[i], tilt_count, sensitivity);
ESP_RETURN_ON_ERROR(ret, TAG, "Failed to init tilt control for channel %d", i); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to init tilt control for channel %d", i);
} }
return ESP_OK; return ESP_OK;
@@ -720,7 +757,7 @@ esp_err_t relay_chn_tilt_init(relay_chn_ctl_t *chn_ctls)
ret = relay_chn_tilt_load_tilt_count(0, &tilt_count); 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); ESP_RETURN_ON_ERROR(ret, TAG, "Failed to load tilt count for channel %d", 0);
#endif // CONFIG_RELAY_CHN_ENABLE_NVS == 1 #endif // CONFIG_RELAY_CHN_ENABLE_NVS == 1
return relay_chn_tilt_ctl_init(&tilt_ctl, chn_ctls, tilt_count, sensitivity); return relay_chn_tilt_ctl_init(&s_tilt_ctl, chn_ctls, tilt_count, sensitivity);
#endif // CONFIG_RELAY_CHN_COUNT > 1 #endif // CONFIG_RELAY_CHN_COUNT > 1
} }
@@ -742,9 +779,9 @@ void relay_chn_tilt_deinit()
{ {
#if CONFIG_RELAY_CHN_COUNT > 1 #if CONFIG_RELAY_CHN_COUNT > 1
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
relay_chn_tilt_ctl_deinit(&tilt_ctls[i]); relay_chn_tilt_ctl_deinit(&s_tilt_ctls[i]);
} }
#else #else
relay_chn_tilt_ctl_deinit(&tilt_ctl); relay_chn_tilt_ctl_deinit(&s_tilt_ctl);
#endif // CONFIG_RELAY_CHN_COUNT > 1 #endif // CONFIG_RELAY_CHN_COUNT > 1
} }

View File

@@ -3,7 +3,8 @@ set(srcs "test_common.c"
"test_relay_chn_notify_common.c" "test_relay_chn_notify_common.c"
"test_app_main.c") "test_app_main.c")
set(incdirs ".") set(incdirs "."
"../../private_include")
# === Selective compilation based on channel count === # === Selective compilation based on channel count ===
if(CONFIG_RELAY_CHN_COUNT GREATER 1) if(CONFIG_RELAY_CHN_COUNT GREATER 1)
@@ -23,7 +24,6 @@ if(CONFIG_RELAY_CHN_ENABLE_TILTING)
endif() endif()
if(CONFIG_RELAY_CHN_ENABLE_NVS) if(CONFIG_RELAY_CHN_ENABLE_NVS)
list(APPEND incdirs "../../private_include")
list(APPEND srcs "../../src/relay_chn_nvs.c") list(APPEND srcs "../../src/relay_chn_nvs.c")
if(CONFIG_RELAY_CHN_COUNT GREATER 1) if(CONFIG_RELAY_CHN_COUNT GREATER 1)
list(APPEND srcs "test_relay_chn_nvs_multi.c") list(APPEND srcs "test_relay_chn_nvs_multi.c")

View File

@@ -6,7 +6,10 @@
#include "test_common.h" #include "test_common.h"
#include "relay_chn_ctl.h" // For resetting the channels #include "relay_chn_ctl.h" // For resetting the channels
#if CONFIG_RELAY_CHN_ENABLE_TILTING
#include "relay_chn_tilt.h" // For resetting tilt count #include "relay_chn_tilt.h" // For resetting tilt count
#endif
const char *TEST_TAG = "RELAY_CHN_TEST"; const char *TEST_TAG = "RELAY_CHN_TEST";

View File

@@ -6,20 +6,20 @@
#include "test_common.h" #include "test_common.h"
relay_chn_state_t states[CONFIG_RELAY_CHN_COUNT], expect_states[CONFIG_RELAY_CHN_COUNT]; static relay_chn_state_t s_states[CONFIG_RELAY_CHN_COUNT], s_expect_states[CONFIG_RELAY_CHN_COUNT];
relay_chn_direction_t directions[CONFIG_RELAY_CHN_COUNT], expect_directions[CONFIG_RELAY_CHN_COUNT]; static relay_chn_direction_t s_directions[CONFIG_RELAY_CHN_COUNT], s_expect_directions[CONFIG_RELAY_CHN_COUNT];
static void test_set_expected_state_all(relay_chn_state_t state) static void test_set_expected_state_all(relay_chn_state_t state)
{ {
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
expect_states[i] = state; s_expect_states[i] = state;
} }
} }
static void test_set_expected_direction_all(relay_chn_direction_t direction) static void test_set_expected_direction_all(relay_chn_direction_t direction)
{ {
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
expect_directions[i] = direction; s_expect_directions[i] = direction;
} }
} }
@@ -231,24 +231,24 @@ TEST_CASE("Forward to Reverse transition with opposite inertia", "[relay_chn][co
// 1. Start in forward direction // 1. Start in forward direction
relay_chn_run_forward_all(); relay_chn_run_forward_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Short delay for state stabilization vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Short delay for state stabilization
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
test_set_expected_state_all(RELAY_CHN_STATE_FORWARD); test_set_expected_state_all(RELAY_CHN_STATE_FORWARD);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_states, states, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_states, s_states, CONFIG_RELAY_CHN_COUNT);
// 2. Issue reverse command // 2. Issue reverse command
relay_chn_run_reverse_all(); relay_chn_run_reverse_all();
// Immediately after the command, the motor should be stopped // Immediately after the command, the motor should be stopped
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
test_set_expected_state_all(RELAY_CHN_STATE_REVERSE_PENDING); test_set_expected_state_all(RELAY_CHN_STATE_REVERSE_PENDING);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_states, states, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_states, s_states, CONFIG_RELAY_CHN_COUNT);
// Wait for the inertia period (after which the reverse command will be dispatched) // Wait for the inertia period (after which the reverse command will be dispatched)
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
// Should now be in reverse state // Should now be in reverse state
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
test_set_expected_state_all(RELAY_CHN_STATE_REVERSE); test_set_expected_state_all(RELAY_CHN_STATE_REVERSE);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_states, states, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_states, s_states, CONFIG_RELAY_CHN_COUNT);
} }
// TEST_CASE: Test transition from reverse to forward with inertia and state checks // TEST_CASE: Test transition from reverse to forward with inertia and state checks
@@ -258,22 +258,22 @@ TEST_CASE("Reverse to Forward transition with opposite inertia", "[relay_chn][co
// 1. Start in reverse direction // 1. Start in reverse direction
relay_chn_run_reverse_all(); relay_chn_run_reverse_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
test_set_expected_state_all(RELAY_CHN_STATE_REVERSE); test_set_expected_state_all(RELAY_CHN_STATE_REVERSE);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_states, states, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_states, s_states, CONFIG_RELAY_CHN_COUNT);
// 2. Issue forward command // 2. Issue forward command
relay_chn_run_forward_all(); relay_chn_run_forward_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
test_set_expected_state_all(RELAY_CHN_STATE_FORWARD_PENDING); test_set_expected_state_all(RELAY_CHN_STATE_FORWARD_PENDING);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_states, states, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_states, s_states, CONFIG_RELAY_CHN_COUNT);
// Wait for inertia // Wait for inertia
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
test_set_expected_state_all(RELAY_CHN_STATE_FORWARD); test_set_expected_state_all(RELAY_CHN_STATE_FORWARD);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_states, states, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_states, s_states, CONFIG_RELAY_CHN_COUNT);
} }
// TEST_CASE: Test issuing the same run command while already running (no inertia expected) // TEST_CASE: Test issuing the same run command while already running (no inertia expected)
@@ -283,18 +283,18 @@ TEST_CASE("Running in same direction does not incur inertia", "[relay_chn][core]
// 1. Start in forward direction // 1. Start in forward direction
relay_chn_run_forward_all(); relay_chn_run_forward_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
test_set_expected_state_all(RELAY_CHN_STATE_FORWARD); test_set_expected_state_all(RELAY_CHN_STATE_FORWARD);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_states, states, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_states, s_states, CONFIG_RELAY_CHN_COUNT);
// 2. Issue the same forward command again // 2. Issue the same forward command again
relay_chn_run_forward_all(); relay_chn_run_forward_all();
// As per the code, is_direction_opposite_to_current_motion should return false, so no inertia. // 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. // Just a short delay to check state remains the same.
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
test_set_expected_state_all(RELAY_CHN_STATE_FORWARD); test_set_expected_state_all(RELAY_CHN_STATE_FORWARD);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_states, states, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_states, s_states, CONFIG_RELAY_CHN_COUNT);
} }
// ### Direction Flipping Tests // ### Direction Flipping Tests
@@ -336,44 +336,44 @@ TEST_CASE("All channels direction can be flipped simultaneously", "[relay_chn][c
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
// 2. Verify all channels are flipped // 2. Verify all channels are flipped
TEST_ESP_OK(relay_chn_get_direction_all(directions)); TEST_ESP_OK(relay_chn_get_direction_all(s_directions));
test_set_expected_direction_all(RELAY_CHN_DIRECTION_FLIPPED); test_set_expected_direction_all(RELAY_CHN_DIRECTION_FLIPPED);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_directions, directions, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_directions, s_directions, CONFIG_RELAY_CHN_COUNT);
// 3. Flip all back // 3. Flip all back
relay_chn_flip_direction_all(); relay_chn_flip_direction_all();
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
// 4. Verify all channels are back to default // 4. Verify all channels are back to default
TEST_ESP_OK(relay_chn_get_direction_all(directions)); TEST_ESP_OK(relay_chn_get_direction_all(s_directions));
test_set_expected_direction_all(RELAY_CHN_DIRECTION_DEFAULT); test_set_expected_direction_all(RELAY_CHN_DIRECTION_DEFAULT);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_directions, directions, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_directions, s_directions, CONFIG_RELAY_CHN_COUNT);
} }
TEST_CASE("Flipping a running channel stops it and flips direction", "[relay_chn][core][direction]") TEST_CASE("Flipping a running channel stops it and flips direction", "[relay_chn][core][direction]")
{ {
// 1. Start channel running and verify state // 1. Start channel running and verify state
relay_chn_run_forward_all(); relay_chn_run_forward_all();
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
test_set_expected_state_all(RELAY_CHN_STATE_FORWARD); test_set_expected_state_all(RELAY_CHN_STATE_FORWARD);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_states, states, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_states, s_states, CONFIG_RELAY_CHN_COUNT);
// 2. Flip the direction while running // 2. Flip the direction while running
relay_chn_flip_direction_all(); relay_chn_flip_direction_all();
// 3. The channel should stop as part of the flip process // 3. The channel should stop as part of the flip process
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
test_set_expected_state_all(RELAY_CHN_STATE_STOPPED); test_set_expected_state_all(RELAY_CHN_STATE_STOPPED);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_states, states, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_states, s_states, CONFIG_RELAY_CHN_COUNT);
// 4. Wait for the flip inertia to pass, after which it should be idle and FLIPPED // 4. Wait for the flip inertia to pass, after which it should be idle and FLIPPED
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
test_set_expected_state_all(RELAY_CHN_STATE_IDLE); test_set_expected_state_all(RELAY_CHN_STATE_IDLE);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_states, states, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_states, s_states, CONFIG_RELAY_CHN_COUNT);
TEST_ESP_OK(relay_chn_get_direction_all(directions)); TEST_ESP_OK(relay_chn_get_direction_all(s_directions));
test_set_expected_direction_all(RELAY_CHN_DIRECTION_FLIPPED); test_set_expected_direction_all(RELAY_CHN_DIRECTION_FLIPPED);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_directions, directions, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_directions, s_directions, CONFIG_RELAY_CHN_COUNT);
} }
TEST_CASE("Direction flip handles invalid channel ID gracefully", "[relay_chn][core][direction]") TEST_CASE("Direction flip handles invalid channel ID gracefully", "[relay_chn][core][direction]")
@@ -387,7 +387,7 @@ TEST_CASE("Direction flip handles invalid channel ID gracefully", "[relay_chn][c
TEST_CASE("get_state_all retrieves all channel states", "[relay_chn][core][batch]") TEST_CASE("get_state_all retrieves all channel states", "[relay_chn][core][batch]")
{ {
// 1. All should be IDLE initially // 1. All should be IDLE initially
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
test_set_expected_state_all(RELAY_CHN_STATE_IDLE); test_set_expected_state_all(RELAY_CHN_STATE_IDLE);
// 2. Set some states // 2. Set some states
@@ -403,30 +403,30 @@ TEST_CASE("get_state_all retrieves all channel states", "[relay_chn][core][batch
// 3. Get all states and verify // 3. Get all states and verify
for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (uint8_t i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
if (i % 2 == 0) { if (i % 2 == 0) {
expect_states[i] = RELAY_CHN_STATE_FORWARD; s_expect_states[i] = RELAY_CHN_STATE_FORWARD;
} else { } else {
expect_states[i] = RELAY_CHN_STATE_REVERSE; s_expect_states[i] = RELAY_CHN_STATE_REVERSE;
} }
} }
TEST_ESP_OK(relay_chn_get_state_all(states)); TEST_ESP_OK(relay_chn_get_state_all(s_states));
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_states, states, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_states, s_states, CONFIG_RELAY_CHN_COUNT);
} }
TEST_CASE("get_direction_all retrieves all channel directions", "[relay_chn][core][direction][batch]") TEST_CASE("get_direction_all retrieves all channel directions", "[relay_chn][core][direction][batch]")
{ {
// 1. All should be default initially // 1. All should be default initially
TEST_ESP_OK(relay_chn_get_direction_all(directions)); TEST_ESP_OK(relay_chn_get_direction_all(s_directions));
test_set_expected_direction_all(RELAY_CHN_DIRECTION_DEFAULT); test_set_expected_direction_all(RELAY_CHN_DIRECTION_DEFAULT);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_directions, directions, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_directions, s_directions, CONFIG_RELAY_CHN_COUNT);
// 2. Flip all // 2. Flip all
relay_chn_flip_direction_all(); relay_chn_flip_direction_all();
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
// 3. Get all directions and verify // 3. Get all directions and verify
TEST_ESP_OK(relay_chn_get_direction_all(directions)); TEST_ESP_OK(relay_chn_get_direction_all(s_directions));
test_set_expected_direction_all(RELAY_CHN_DIRECTION_FLIPPED); test_set_expected_direction_all(RELAY_CHN_DIRECTION_FLIPPED);
TEST_ASSERT_EQUAL_UINT_ARRAY(expect_directions, directions, CONFIG_RELAY_CHN_COUNT); TEST_ASSERT_EQUAL_UINT_ARRAY(s_expect_directions, s_directions, CONFIG_RELAY_CHN_COUNT);
} }
TEST_CASE("get_all functions handle NULL arguments", "[relay_chn][core][batch]") TEST_CASE("get_all functions handle NULL arguments", "[relay_chn][core][batch]")

View File

@@ -21,14 +21,13 @@
void check_all_channels_for_state(relay_chn_state_t state) void check_all_channels_for_state(relay_chn_state_t state)
{ {
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
// ESP_LOGI(TEST_TAG, "Checking channel %d for state %d", i, state);
TEST_ASSERT_EQUAL(state, relay_chn_get_state(i)); TEST_ASSERT_EQUAL(state, relay_chn_get_state(i));
} }
} }
// Helper function to prepare channel for tilt tests // Helper function to prepare channel for tilt tests
void prepare_channels_for_tilt_with_mixed_runs() { void prepare_channels_for_tilt_with_mixed_runs() {
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
// Ensure the channel has had a 'last_run_cmd' // Ensure the channel has had a 'last_run_cmd'
@@ -40,16 +39,19 @@ void prepare_channels_for_tilt_with_mixed_runs() {
} }
} }
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Allow command to process for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
relay_chn_stop_all(); // Stop it to set last_run_cmd but return to FREE for next test relay_chn_state_t expect_state;
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS)); if (i % 2 == 0) {
check_all_channels_for_state(RELAY_CHN_STATE_IDLE); expect_state = RELAY_CHN_STATE_FORWARD;
} else { // Assuming initial_cmd is RELAY_CHN_CMD_REVERSE
expect_state = RELAY_CHN_STATE_REVERSE;
}
TEST_ASSERT_EQUAL(expect_state, relay_chn_get_state(i));
}
} }
// Helper function to prepare channel for tilt tests // Helper function to prepare channel for tilt tests
void prepare_all_channels_for_tilt(int initial_cmd) { void prepare_all_channels_for_tilt(int initial_cmd) {
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
// If the channels are not IDLE yet, wait more // If the channels are not IDLE yet, wait more
@@ -64,6 +66,8 @@ void prepare_all_channels_for_tilt(int initial_cmd) {
if (not_idle) { if (not_idle) {
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS)); vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS));
} }
// Ensure all channels are IDLE
check_all_channels_for_state(RELAY_CHN_STATE_IDLE);
// Ensure the channel has had a 'last_run_cmd' // Ensure the channel has had a 'last_run_cmd'
if (initial_cmd == RELAY_CHN_CMD_FORWARD) { if (initial_cmd == RELAY_CHN_CMD_FORWARD) {
@@ -71,20 +75,17 @@ void prepare_all_channels_for_tilt(int initial_cmd) {
} else { // Assuming initial_cmd is RELAY_CHN_CMD_REVERSE } else { // Assuming initial_cmd is RELAY_CHN_CMD_REVERSE
relay_chn_run_reverse_all(); relay_chn_run_reverse_all();
} }
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Allow command to process
relay_chn_stop_all(); // Stop all to set last_run_cmd but return to FREE for next test
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
check_all_channels_for_state(RELAY_CHN_STATE_IDLE); relay_chn_state_t expect_state = initial_cmd == RELAY_CHN_CMD_FORWARD
? RELAY_CHN_STATE_FORWARD : RELAY_CHN_STATE_REVERSE;
check_all_channels_for_state(expect_state);
ESP_LOGI(TEST_TAG, "All channels prepared for tilt test");
} }
// TEST_CASE: Test transition from running forward to tilt forward // 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 // 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]") TEST_CASE("Run Forward to Tilt Forward transition with inertia", "[relay_chn][tilt][inertia]")
{ {
// Prepare channel by running forward first to set last_run_cmd
prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD);
// 1. Start in forward direction // 1. Start in forward direction
relay_chn_run_forward_all(); relay_chn_run_forward_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
@@ -99,15 +100,15 @@ TEST_CASE("Run Forward to Tilt Forward transition with inertia", "[relay_chn][ti
// Wait for the inertia period (after which the tilt command will be dispatched) // Wait for the inertia period (after which the tilt command will be dispatched)
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS)); vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS));
check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD); check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD);
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
} }
// TEST_CASE: Test transition from running reverse to tilt reverse // 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 // 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]") TEST_CASE("Run Reverse to Tilt Reverse transition with inertia", "[relay_chn][tilt][inertia]")
{ {
// Prepare channel by running reverse first to set last_run_cmd
prepare_all_channels_for_tilt(RELAY_CHN_CMD_REVERSE);
// 1. Start in reverse direction // 1. Start in reverse direction
relay_chn_run_reverse_all(); relay_chn_run_reverse_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
@@ -120,6 +121,9 @@ TEST_CASE("Run Reverse to Tilt Reverse transition with inertia", "[relay_chn][ti
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
check_all_channels_for_state(RELAY_CHN_STATE_TILT_REVERSE); check_all_channels_for_state(RELAY_CHN_STATE_TILT_REVERSE);
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
} }
// TEST_CASE: Test transition from FREE state to tilt forward (now with preparation) // TEST_CASE: Test transition from FREE state to tilt forward (now with preparation)
@@ -128,12 +132,19 @@ TEST_CASE("FREE to Tilt Forward transition with inertia (prepared)", "[relay_chn
{ {
// Prepare channel by running forward first to set last_run_cmd // Prepare channel by running forward first to set last_run_cmd
prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD); prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD);
relay_chn_stop_all(); // Stop to trigger IDLE
// Wait for the channel to transition to IDLE
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
check_all_channels_for_state(RELAY_CHN_STATE_IDLE); // Ensure we are back to IDLE
// Issue tilt forward command // Issue tilt forward command
relay_chn_tilt_forward_all(); relay_chn_tilt_forward_all();
// From FREE state, tilt command should still incur the inertia due to the internal timer logic // From FREE state, tilt command should still incur the inertia due to the internal timer logic
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD); check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD);
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
} }
// TEST_CASE: Test transition from FREE state to tilt reverse (now with preparation) // TEST_CASE: Test transition from FREE state to tilt reverse (now with preparation)
@@ -142,11 +153,18 @@ TEST_CASE("FREE to Tilt Reverse transition with inertia (prepared)", "[relay_chn
{ {
// Prepare channel by running reverse first to set last_run_cmd // Prepare channel by running reverse first to set last_run_cmd
prepare_all_channels_for_tilt(RELAY_CHN_CMD_REVERSE); prepare_all_channels_for_tilt(RELAY_CHN_CMD_REVERSE);
relay_chn_stop_all(); // Stop to trigger IDLE
// Wait for the channel to transition to IDLE
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
check_all_channels_for_state(RELAY_CHN_STATE_IDLE); // Ensure we are back to IDLE
// Issue tilt reverse command // Issue tilt reverse command
relay_chn_tilt_reverse_all(); relay_chn_tilt_reverse_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
check_all_channels_for_state(RELAY_CHN_STATE_TILT_REVERSE); check_all_channels_for_state(RELAY_CHN_STATE_TILT_REVERSE);
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
} }
// TEST_CASE: Test transition from tilt forward to run forward (inertia expected for run) // TEST_CASE: Test transition from tilt forward to run forward (inertia expected for run)
@@ -165,6 +183,9 @@ TEST_CASE("Tilt Forward to Run Forward transition with inertia", "[relay_chn][ti
check_all_channels_for_state(RELAY_CHN_STATE_FORWARD_PENDING); check_all_channels_for_state(RELAY_CHN_STATE_FORWARD_PENDING);
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
check_all_channels_for_state(RELAY_CHN_STATE_FORWARD); check_all_channels_for_state(RELAY_CHN_STATE_FORWARD);
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
} }
// TEST_CASE: Test transition from tilt reverse to run reverse (no inertia expected for run) // TEST_CASE: Test transition from tilt reverse to run reverse (no inertia expected for run)
@@ -182,6 +203,9 @@ TEST_CASE("Tilt Reverse to Run Reverse transition with inertia", "[relay_chn][ti
check_all_channels_for_state(RELAY_CHN_STATE_REVERSE_PENDING); check_all_channels_for_state(RELAY_CHN_STATE_REVERSE_PENDING);
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
check_all_channels_for_state(RELAY_CHN_STATE_REVERSE); check_all_channels_for_state(RELAY_CHN_STATE_REVERSE);
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
} }
// TEST_CASE: Test transition from tilt forward to run reverse (without inertia) // TEST_CASE: Test transition from tilt forward to run reverse (without inertia)
@@ -198,6 +222,9 @@ TEST_CASE("Tilt Forward to Run Reverse transition without inertia", "[relay_chn]
relay_chn_run_reverse_all(); relay_chn_run_reverse_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
check_all_channels_for_state(RELAY_CHN_STATE_REVERSE); check_all_channels_for_state(RELAY_CHN_STATE_REVERSE);
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
} }
// TEST_CASE: Test stopping from a tilt state (no inertia for stop command itself) // TEST_CASE: Test stopping from a tilt state (no inertia for stop command itself)
@@ -228,10 +255,14 @@ TEST_CASE("tilt_forward_all sets all channels to TILT_FORWARD", "[relay_chn][til
// 2. Issue tilt forward to all channels // 2. Issue tilt forward to all channels
relay_chn_tilt_forward_all(); relay_chn_tilt_forward_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Tilt from FREE doesn't have stop-inertia // Should incur inertia timer
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
// 3. Verify all channels are tilting forward // 3. Verify all channels are tilting forward
check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD); check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD);
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
} }
TEST_CASE("tilt_reverse_all sets all channels to TILT_REVERSE", "[relay_chn][tilt][batch]") TEST_CASE("tilt_reverse_all sets all channels to TILT_REVERSE", "[relay_chn][tilt][batch]")
@@ -241,10 +272,14 @@ TEST_CASE("tilt_reverse_all sets all channels to TILT_REVERSE", "[relay_chn][til
// 2. Issue tilt reverse to all channels // 2. Issue tilt reverse to all channels
relay_chn_tilt_reverse_all(); relay_chn_tilt_reverse_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Should incur inertia timer
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
// 3. Verify all channels are tilting reverse // 3. Verify all channels are tilting reverse
check_all_channels_for_state(RELAY_CHN_STATE_TILT_REVERSE); check_all_channels_for_state(RELAY_CHN_STATE_TILT_REVERSE);
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
} }
TEST_CASE("tilt_stop_all stops all tilting channels", "[relay_chn][tilt][batch]") TEST_CASE("tilt_stop_all stops all tilting channels", "[relay_chn][tilt][batch]")
@@ -253,7 +288,10 @@ TEST_CASE("tilt_stop_all stops all tilting channels", "[relay_chn][tilt][batch]"
prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD); prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD);
relay_chn_tilt_forward_all(); relay_chn_tilt_forward_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Should incur inertia timer
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
// 3. Verify all channels are tilting forward
check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD);
// 2. Stop tilting on all channels // 2. Stop tilting on all channels
relay_chn_tilt_stop_all(); relay_chn_tilt_stop_all();
@@ -265,15 +303,13 @@ TEST_CASE("tilt_stop_all stops all tilting channels", "[relay_chn][tilt][batch]"
TEST_CASE("tilt_auto_all tilts channels based on last run direction", "[relay_chn][tilt][batch]") TEST_CASE("tilt_auto_all tilts channels based on last run direction", "[relay_chn][tilt][batch]")
{ {
// This test requires at least 2 channels to demonstrate different behaviors
TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(2, CONFIG_RELAY_CHN_COUNT, "Test requires at least 2 channels");
// 1. Prepare channel 0 with last run FORWARD and channel 1 with last run REVERSE // 1. Prepare channel 0 with last run FORWARD and channel 1 with last run REVERSE
prepare_channels_for_tilt_with_mixed_runs(); prepare_channels_for_tilt_with_mixed_runs();
// 2. Issue auto tilt command to all channels // 2. Issue auto tilt command to all channels
relay_chn_tilt_auto_all(); relay_chn_tilt_auto_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Tilt from FREE state is dispatched immediately // Should incur inertia timer
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
// 3. Verify even channels tilt forward (last run was forward) and odd channels tilt reverse (last run was reverse) // 3. Verify even channels tilt forward (last run was forward) and odd channels tilt reverse (last run was reverse)
for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) { for (int i = 0; i < CONFIG_RELAY_CHN_COUNT; i++) {
@@ -282,6 +318,9 @@ TEST_CASE("tilt_auto_all tilts channels based on last run direction", "[relay_ch
TEST_ASSERT_EQUAL(state, relay_chn_get_state(i)); TEST_ASSERT_EQUAL(state, relay_chn_get_state(i));
} }
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
} }
// Test relay_chn_tilt_auto() chooses correct tilt direction // Test relay_chn_tilt_auto() chooses correct tilt direction
@@ -290,19 +329,24 @@ TEST_CASE("relay_chn_tilt_auto chooses correct direction", "[relay_chn][tilt][au
// Prepare FORWARD // Prepare FORWARD
prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD); prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD);
relay_chn_tilt_auto_all(); relay_chn_tilt_auto_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Should incur inertia timer
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
// Verify all tilt forward // Verify all tilt forward
check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD); check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD);
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all(); relay_chn_tilt_stop_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
// Prepare REVERSE // Prepare REVERSE
prepare_all_channels_for_tilt(RELAY_CHN_CMD_REVERSE); prepare_all_channels_for_tilt(RELAY_CHN_CMD_REVERSE);
relay_chn_tilt_auto_all(); relay_chn_tilt_auto_all();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Should incur inertia timer
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
// Verify all tilt reverse // Verify all tilt reverse
check_all_channels_for_state(RELAY_CHN_STATE_TILT_REVERSE); check_all_channels_for_state(RELAY_CHN_STATE_TILT_REVERSE);
// Ensure the channel reset tilt control
relay_chn_tilt_stop_all();
} }
// Test sensitivity set/get // Test sensitivity set/get
@@ -375,7 +419,14 @@ TEST_CASE("tilt counter logic: forward and reverse consumption", "[relay_chn][ti
#define TEST_TILT_EXECUTION_TIME_MS 100 #define TEST_TILT_EXECUTION_TIME_MS 100
prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD); prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD);
relay_chn_tilt_set_sensitivity_all_with(100); // Set sentivity to max for fastest execution relay_chn_tilt_set_sensitivity_all_with(100); // Set sentivity to max for fastest execution
// Ensure sensitivities are set correctly
uint8_t sensitivities[CONFIG_RELAY_CHN_COUNT];
uint8_t expect[CONFIG_RELAY_CHN_COUNT];
memset(expect, 100, CONFIG_RELAY_CHN_COUNT);
relay_chn_tilt_get_sensitivity_all(sensitivities);
TEST_ASSERT_EQUAL_UINT8_ARRAY(expect, sensitivities, CONFIG_RELAY_CHN_COUNT);
// Tilt forward 3 times // Tilt forward 3 times
relay_chn_tilt_forward_all(); relay_chn_tilt_forward_all();
@@ -393,12 +444,12 @@ TEST_CASE("tilt counter logic: forward and reverse consumption", "[relay_chn][ti
// Now tilt reverse 3 times (should succeed) // Now tilt reverse 3 times (should succeed)
relay_chn_tilt_reverse_all(); relay_chn_tilt_reverse_all();
vTaskDelay(pdMS_TO_TICKS(TEST_TILT_EXECUTION_TIME_MS * 3 + TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_TILT_EXECUTION_TIME_MS));
check_all_channels_for_state(RELAY_CHN_STATE_TILT_REVERSE); check_all_channels_for_state(RELAY_CHN_STATE_TILT_REVERSE);
// Let it execute 2 at least, or more
// One more reverse tilt should fail (counter exhausted)
vTaskDelay(pdMS_TO_TICKS(TEST_TILT_EXECUTION_TIME_MS * 3)); vTaskDelay(pdMS_TO_TICKS(TEST_TILT_EXECUTION_TIME_MS * 3));
// Should not enter TILT_REVERSE, should remain IDLE
// More reverse tilt should fail (counter exhausted)
check_all_channels_for_state(RELAY_CHN_STATE_IDLE); check_all_channels_for_state(RELAY_CHN_STATE_IDLE);
} }
@@ -423,12 +474,12 @@ TEST_CASE("run_all command during active tilt cycle stops tilt", "[relay_chn][ti
// Set a known sensitivity for predictable timing. // Set a known sensitivity for predictable timing.
// For sensitivity=50, move_time=30ms, pause_time=270ms. // For sensitivity=50, move_time=30ms, pause_time=270ms.
relay_chn_tilt_set_sensitivity_all_with(50); relay_chn_tilt_set_sensitivity_all_with(50);
const uint32_t move_time_ms = 30;
// --- Test interrupting during MOVE step --- // --- Test interrupting during MOVE step ---
prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD); prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD);
relay_chn_tilt_forward_all(); relay_chn_tilt_forward_all();
vTaskDelay(pdMS_TO_TICKS(move_time_ms / 2)); // Wait for half of the move time // Should incur inertia timer
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD); check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD);
// Interrupt with run_reverse_all while in the MOVE part of the cycle // Interrupt with run_reverse_all while in the MOVE part of the cycle
@@ -438,9 +489,12 @@ TEST_CASE("run_all command during active tilt cycle stops tilt", "[relay_chn][ti
check_all_channels_for_state(RELAY_CHN_STATE_REVERSE); check_all_channels_for_state(RELAY_CHN_STATE_REVERSE);
// --- Test interrupting during PAUSE step --- // --- Test interrupting during PAUSE step ---
relay_chn_stop_all(); // Stop the reverse runs
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD); prepare_all_channels_for_tilt(RELAY_CHN_CMD_FORWARD);
relay_chn_tilt_forward_all(); relay_chn_tilt_forward_all();
vTaskDelay(pdMS_TO_TICKS(move_time_ms + TEST_DELAY_MARGIN_MS)); // Wait past MOVE, into PAUSE // Should incur inertia timer
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS)); // Wait past MOVE, into PAUSE
check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD); check_all_channels_for_state(RELAY_CHN_STATE_TILT_FORWARD);
// Interrupt with run_forward_all while in the PAUSE part of the cycle // Interrupt with run_forward_all while in the PAUSE part of the cycle

View File

@@ -30,19 +30,15 @@ void prepare_channel_for_tilt(int initial_cmd) {
} else { // Assuming initial_cmd is RELAY_CHN_CMD_REVERSE } else { // Assuming initial_cmd is RELAY_CHN_CMD_REVERSE
relay_chn_run_reverse(); relay_chn_run_reverse();
} }
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Allow command to process
relay_chn_stop(); // Stop it to set last_run_cmd but return to FREE for next test
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state()); relay_chn_state_t expect_state = initial_cmd == RELAY_CHN_CMD_FORWARD ? RELAY_CHN_STATE_FORWARD : RELAY_CHN_STATE_REVERSE;
TEST_ASSERT_EQUAL(expect_state, relay_chn_get_state());
} }
// Test transition from running forward to tilt forward // 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 // 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]") TEST_CASE("Run Forward to Tilt Forward transition with inertia", "[relay_chn][tilt][inertia]")
{ {
// Prepare channel by running forward first to set last_run_cmd
prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD);
// 1. Start in forward direction // 1. Start in forward direction
relay_chn_run_forward(); relay_chn_run_forward();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
@@ -63,9 +59,6 @@ TEST_CASE("Run Forward to Tilt Forward transition with inertia", "[relay_chn][ti
// Scenario: RELAY_CHN_STATE_REVERSE -> (relay_chn_tilt_reverse) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_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]") TEST_CASE("Run Reverse to Tilt Reverse transition with inertia", "[relay_chn][tilt][inertia]")
{ {
// Prepare channel by running reverse first to set last_run_cmd
prepare_channel_for_tilt(RELAY_CHN_CMD_REVERSE);
// 1. Start in reverse direction // 1. Start in reverse direction
relay_chn_run_reverse(); relay_chn_run_reverse();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
@@ -86,6 +79,9 @@ TEST_CASE("FREE to Tilt Forward transition with inertia (prepared)", "[relay_chn
{ {
// Prepare channel by running forward first to set last_run_cmd // Prepare channel by running forward first to set last_run_cmd
prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD);
relay_chn_stop(); // Stop to trigger IDLE
// Wait for the channel to transition to IDLE
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state()); // Ensure we are back to FREE TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state()); // Ensure we are back to FREE
// Issue tilt forward command // Issue tilt forward command
@@ -101,6 +97,9 @@ TEST_CASE("FREE to Tilt Reverse transition with inertia (prepared)", "[relay_chn
{ {
// Prepare channel by running reverse first to set last_run_cmd // Prepare channel by running reverse first to set last_run_cmd
prepare_channel_for_tilt(RELAY_CHN_CMD_REVERSE); prepare_channel_for_tilt(RELAY_CHN_CMD_REVERSE);
relay_chn_stop(); // Stop to trigger IDLE
// Wait for the channel to transition to IDLE
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state()); // Ensure we are back to FREE TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state()); // Ensure we are back to FREE
// Issue tilt reverse command // Issue tilt reverse command
@@ -183,7 +182,8 @@ TEST_CASE("relay_chn_tilt_auto chooses correct direction", "[relay_chn][tilt][au
// Prepare FORWARD // Prepare FORWARD
prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD);
relay_chn_tilt_auto(); relay_chn_tilt_auto();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Should incur inertia timer
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state()); TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state());
relay_chn_tilt_stop(); relay_chn_tilt_stop();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
@@ -191,7 +191,8 @@ TEST_CASE("relay_chn_tilt_auto chooses correct direction", "[relay_chn][tilt][au
// Prepare REVERSE // Prepare REVERSE
prepare_channel_for_tilt(RELAY_CHN_CMD_REVERSE); prepare_channel_for_tilt(RELAY_CHN_CMD_REVERSE);
relay_chn_tilt_auto(); relay_chn_tilt_auto();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Should incur inertia timer
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state()); TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state());
} }
@@ -237,31 +238,25 @@ TEST_CASE("relay_chn_tilt_set_sensitivity handles upper boundary", "[relay_chn][
// Test tilt counter logic: forward x3, reverse x3, extra reverse fails // Test tilt counter logic: forward x3, reverse x3, extra reverse fails
TEST_CASE("tilt counter logic: forward and reverse consumption", "[relay_chn][tilt][counter]") TEST_CASE("tilt counter logic: forward and reverse consumption", "[relay_chn][tilt][counter]")
{ {
// Tilt execution time at 100% sensitivity in milliseconds (10 + 90)
#define TEST_TILT_EXECUTION_TIME_MS 100
prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD);
// Tilt forward 3 times // Tilt forward 3 times
for (int i = 0; i < 3; ++i) { relay_chn_tilt_forward();
relay_chn_tilt_forward(); vTaskDelay(pdMS_TO_TICKS(TEST_TILT_EXECUTION_TIME_MS * 3 + TEST_DELAY_MARGIN_MS));
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state());
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state()); relay_chn_tilt_stop();
relay_chn_tilt_stop();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
}
// Now tilt reverse 3 times (should succeed) // Now tilt reverse 3 times (should succeed)
for (int i = 0; i < 3; ++i) {
relay_chn_tilt_reverse();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
if (i < 3) {
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state());
relay_chn_tilt_stop();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS));
}
}
// Extra reverse tilt should fail (counter exhausted)
relay_chn_tilt_reverse(); relay_chn_tilt_reverse();
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Let it execute one time
vTaskDelay(pdMS_TO_TICKS(TEST_TILT_EXECUTION_TIME_MS));
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state());
// Let it execute 2 at least, or more
vTaskDelay(pdMS_TO_TICKS(TEST_TILT_EXECUTION_TIME_MS * 3));
// Should not enter TILT_REVERSE, should remain FREE or STOPPED // Should not enter TILT_REVERSE, should remain FREE or STOPPED
relay_chn_state_t state = relay_chn_get_state(); relay_chn_state_t state = relay_chn_get_state();
TEST_ASSERT(state != RELAY_CHN_STATE_TILT_REVERSE); TEST_ASSERT(state != RELAY_CHN_STATE_TILT_REVERSE);
@@ -289,12 +284,12 @@ TEST_CASE("run command during active tilt cycle stops tilt", "[relay_chn][tilt][
// Set a known sensitivity for predictable timing. // Set a known sensitivity for predictable timing.
// For sensitivity=50, move_time=30ms, pause_time=270ms. // For sensitivity=50, move_time=30ms, pause_time=270ms.
relay_chn_tilt_set_sensitivity(50); relay_chn_tilt_set_sensitivity(50);
const uint32_t move_time_ms = 30;
// --- Test interrupting during MOVE step --- // --- Test interrupting during MOVE step ---
prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD);
relay_chn_tilt_forward(); relay_chn_tilt_forward();
vTaskDelay(pdMS_TO_TICKS(move_time_ms / 2)); // Wait for half of the move time // Should incur inertia timer
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state()); TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state());
// Interrupt with run_reverse while in the MOVE part of the cycle // Interrupt with run_reverse while in the MOVE part of the cycle
@@ -304,9 +299,14 @@ TEST_CASE("run command during active tilt cycle stops tilt", "[relay_chn][tilt][
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state()); TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state());
// --- Test interrupting during PAUSE step --- // --- Test interrupting during PAUSE step ---
prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); relay_chn_stop(); // Stop the reverse run
// Wait the channel to be IDLE
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); // Prepare channel again
relay_chn_tilt_forward(); relay_chn_tilt_forward();
vTaskDelay(pdMS_TO_TICKS(move_time_ms + TEST_DELAY_MARGIN_MS)); // Wait past MOVE, into PAUSE // Should incur inertia timer
vTaskDelay(pdMS_TO_TICKS(CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS + TEST_DELAY_MARGIN_MS));
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state()); TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state());
// Interrupt with run_forward while in the PAUSE part of the cycle // Interrupt with run_forward while in the PAUSE part of the cycle

View File

@@ -1,6 +1,9 @@
# Disable task WDT for tests # Disable task WDT for tests
CONFIG_ESP_TASK_WDT_INIT=n CONFIG_ESP_TASK_WDT_INIT=n
CONFIG_LOG_MAXIMUM_LEVEL_DEBUG=y
CONFIG_LOG_MAXIMUM_LEVEL=4
# Relay Channel Driver Default Configuration for Testing # Relay Channel Driver Default Configuration for Testing
# Keep this as short as possible for tests # Keep this as short as possible for tests
CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200 CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200

File diff suppressed because it is too large Load Diff