diff --git a/examples/relay_chn_multi/.gitignore b/examples/relay_chn_multi/.gitignore new file mode 100644 index 0000000..51c9513 --- /dev/null +++ b/examples/relay_chn_multi/.gitignore @@ -0,0 +1,3 @@ +build/ +sdkconfig +sdkconfig.old \ No newline at end of file diff --git a/examples/relay_chn_multi/.vscode/c_cpp_properties.json b/examples/relay_chn_multi/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..badb83d --- /dev/null +++ b/examples/relay_chn_multi/.vscode/c_cpp_properties.json @@ -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 +} diff --git a/examples/relay_chn_multi/.vscode/launch.json b/examples/relay_chn_multi/.vscode/launch.json new file mode 100644 index 0000000..2511a38 --- /dev/null +++ b/examples/relay_chn_multi/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "gdbtarget", + "request": "attach", + "name": "Eclipse CDT GDB Adapter" + }, + { + "type": "espidf", + "name": "Launch", + "request": "launch" + } + ] +} \ No newline at end of file diff --git a/examples/relay_chn_multi/.vscode/settings.json b/examples/relay_chn_multi/.vscode/settings.json new file mode 100644 index 0000000..9fa2d52 --- /dev/null +++ b/examples/relay_chn_multi/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "C_Cpp.intelliSenseEngine": "default", + "files.associations": { + "led_indicator_blink_default.h": "c" + } +} diff --git a/examples/relay_chn_multi/CMakeLists.txt b/examples/relay_chn_multi/CMakeLists.txt new file mode 100644 index 0000000..c64f60a --- /dev/null +++ b/examples/relay_chn_multi/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/examples/relay_chn_multi/README.md b/examples/relay_chn_multi/README.md new file mode 100644 index 0000000..54b6f47 --- /dev/null +++ b/examples/relay_chn_multi/README.md @@ -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 +``` \ No newline at end of file diff --git a/examples/relay_chn_multi/example_schematic.png b/examples/relay_chn_multi/example_schematic.png new file mode 100644 index 0000000..45c4aff Binary files /dev/null and b/examples/relay_chn_multi/example_schematic.png differ diff --git a/examples/relay_chn_multi/main/CMakeLists.txt b/examples/relay_chn_multi/main/CMakeLists.txt new file mode 100644 index 0000000..0c53fb8 --- /dev/null +++ b/examples/relay_chn_multi/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "relay_chn_multi_main.c" + PRIV_REQUIRES button led_indicator relay_chn) diff --git a/examples/relay_chn_multi/main/Kconfig.projbuild b/examples/relay_chn_multi/main/Kconfig.projbuild new file mode 100644 index 0000000..f294079 --- /dev/null +++ b/examples/relay_chn_multi/main/Kconfig.projbuild @@ -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 \ No newline at end of file diff --git a/examples/relay_chn_multi/main/idf_component.yml b/examples/relay_chn_multi/main/idf_component.yml new file mode 100644 index 0000000..60c7cac --- /dev/null +++ b/examples/relay_chn_multi/main/idf_component.yml @@ -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: ../../../ diff --git a/examples/relay_chn_multi/main/relay_chn_multi_main.c b/examples/relay_chn_multi/main/relay_chn_multi_main.c new file mode 100644 index 0000000..e209ed2 --- /dev/null +++ b/examples/relay_chn_multi/main/relay_chn_multi_main.c @@ -0,0 +1,427 @@ +/* + * SPDX-FileCopyrightText: 2025 Kozmotronik Tech + * + * SPDX-License-Identifier: MIT + */ + +#include +#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; +} diff --git a/examples/relay_chn_multi/sdkconfig.defaults b/examples/relay_chn_multi/sdkconfig.defaults new file mode 100644 index 0000000..2eeaa43 --- /dev/null +++ b/examples/relay_chn_multi/sdkconfig.defaults @@ -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