15 Commits

Author SHA1 Message Date
Kozmotronik
f5481f79e7 Merge pull request 'dev' (#7) from dev into main
Reviewed-on: https://kozmotronik.nohost.me/gitea/KozmotronikTech/relay_chn/pulls/7
2025-02-21 14:38:48 +03:00
Kozmotronik
7d3f08b56b Merge pull request 'Add tilt feature, fix bugs, improve code.' (#6) from feature-tilt into dev
Reviewed-on: https://kozmotronik.nohost.me/gitea/KozmotronikTech/relay_chn/pulls/6
2025-02-21 12:46:10 +03:00
a694938224 Add tilt feature, fix bugs, improve code.
* Add tilt feature.

* Fix the following bugs:
  * warning: comparison is always true due to limited range of data type.
  * Remove unnecessary esp_timer checks.
  * The scheduled FREE command disrupts the current command.
  * Fatal pin mapping issue.

* Make code optimizations and improvements:
  * Optimize event loop queue size depending on channel count.
  * Change the channels' starting state to FREE.
  * Remove the unnecessary relay_chn_invalidate_inertia_timer function.
  * Change the relay_chn_start_inertia_timer function as relay_chn_start_esp_timer_once and modify the function so that it be a generic esp timer start function.
  * Optimize the if statement that checks the last run cmd in the relay_chn_execute_stop.
2025-02-21 12:43:00 +03:00
dcb5453522 Resolve unseen conflicts. 2025-02-14 17:01:22 +03:00
feb1f4ac81 Update docs for the state_listener_manager API. 2025-02-14 15:12:37 +03:00
069363205a Rename inertia timer to distinguish timers.
Rename timer member and relevant functions for the purpose they used,
in order to distinguish between timers.
2025-02-14 15:00:02 +03:00
d4fdff949a Rename inertia timer to distinguish timers. 2025-02-14 14:50:35 +03:00
dc2dcfec7d Add support for addressing all relay channels. 2025-02-12 16:50:00 +03:00
8517993358 Update file name according to the extension guide. 2025-02-12 08:56:51 +03:00
e21bfb5b26 Update the root gitignore to ignore the build and unity-app. 2025-02-12 08:44:38 +03:00
27c669066a Add headers and test functions, fix gpio definition 2025-02-11 17:45:17 +03:00
d64370c925 Unify test source files. 2025-02-11 14:41:24 +03:00
b47244b5d1 Move relay_chn.c into src directory. 2025-02-11 14:40:37 +03:00
6158dce3b4 Update readme file. 2025-02-11 12:29:44 +03:00
d3fb1e6620 Add source files. 2025-02-11 12:28:32 +03:00
12 changed files with 1571 additions and 2 deletions

5
.gitignore vendored
View File

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

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

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

4
CMakeLists.txt Normal file
View File

@@ -0,0 +1,4 @@
idf_component_register(SRCS "src/relay_chn.c"
INCLUDE_DIRS include
REQUIRES driver
PRIV_REQUIRES esp_timer esp_event)

29
Kconfig Normal file
View File

@@ -0,0 +1,29 @@
menu "Relay Channel Driver Configuration"
config RELAY_CHN_OPPOSITE_INERTIA_MS
int "Inertia time before it runs opposite direction (ms)"
range 200 1500
default 800
help
Time to wait after changing the direction of the output before
starting the output. This is useful for the motors or some other
mechanical actuators to allow them to stop and settle before
changing the direction.
config RELAY_CHN_COUNT
int "Number of relay channels"
range 1 8
default 1
help
Number of relay channels between 1 and 8.
config RELAY_CHN_ENABLE_TILTING
bool "Enable tilting on relay channels"
default n
help
This option controls enabling tilting on channels. Tilting makes
a channel move with a specific pattern moving with small steps
at a time. Tilting is specifically designed for controlling some
types of curtains that need to be adjusted to let enter specific
amount of day light.
endmenu

View File

@@ -1,3 +1,82 @@
# relay_chn # relay_chn - Relay Channel Controller
A custom ESP-IDF component for controlling a relay channel An ESP-IDF component for controlling relay channels, specifically designed for driving bipolar motors through relay pairs.
## Features
- Controls multiple relay channel pairs (up to 8 channels)
- Built-in direction change inertia protection
- Automatic command sequencing and timing
- Event-driven architecture for reliable operation
- Forward/Reverse direction control
- Direction flipping capability
- State monitoring and reporting
## Description
Each relay channel consists of 2 output relays controlled by 2 GPIO pins. The component provides APIs to control these relay pairs while ensuring safe operation, particularly for driving bipolar motors. It prevents short-circuits by automatically managing direction changes with configurable inertia timing.
## Configuration
Configure the component through menuconfig under "Relay Channel Driver Configuration":
- `CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS`: Time to wait before changing direction (200-1500ms, default: 800ms)
- `CONFIG_RELAY_CHN_COUNT`: Number of relay channels (1-8, default: 1)
## Installation
1. Copy the component to your project's components directory
2. Add dependency to your project's `idf_component.yml`:
```yaml
dependencies:
relay_chn:
version: "^0.1.0"
```
## Usage
### 1. Initialize relay channels
```c
// Define GPIO pins for relay channels
const gpio_num_t gpio_map[] = {GPIO_NUM_4, GPIO_NUM_5}; // One channel example
const uint8_t gpio_count = sizeof(gpio_map) / sizeof(gpio_map[0]);
// Create and initialize relay channels
esp_err_t ret = relay_chn_create(gpio_map, gpio_count);
if (ret != ESP_OK) {
// Handle error
}
```
### 2. Control relay channels
```c
// Run channel 0 forward
relay_chn_run_forward(0);
// Run channel 0 reverse
relay_chn_run_reverse(0);
// Stop channel 0
relay_chn_stop(0);
// Flip direction of channel 0
relay_chn_flip_direction(0);
```
### 3. Monitor channel state
```c
// Get channel state
relay_chn_state_t state = relay_chn_get_state(0);
char *state_str = relay_chn_get_state_str(0);
// Get channel direction
relay_chn_direction_t direction = relay_chn_get_direction(0);
```
## License
[MIT License](LICENSE) - Copyright (c) 2025 kozmotronik.

7
idf_component.yml Normal file
View File

@@ -0,0 +1,7 @@
name: relay_chn
version: 0.1.0
description: Custom component for relay channel control
dependencies:
idf:
version: ">=4.0"
# TODO: Repo ve belgelendirme bağlantılarını ekle.

228
include/relay_chn.h Normal file
View File

@@ -0,0 +1,228 @@
#ifndef RELAY_CHN_H
#define RELAY_CHN_H
/**
* @file relay_chn.h
*
* @author
* Ismail Sahillioglu <ismailsahillioglu@gmail.com>
*
* @date 2025.02.08
*
* @defgroup relay_chn Relay Channel Controller
* @ingroup components
* @{
* One relay channel consists of 2 output relays, hence 2 GPIO pins are required for each relay channel.
* This module provides an API to control the relay channels, specifically to drive bipolar motors.
* It also provides APIs to control the direction of the relay channel, bipolar motors in mind.
* The module also automatically manages the direction change inertia to prevent short-circuiting the motor.
* The STOP command overrides any other command and clears the pending command if any.
*
* The module internally uses a custom esp event loop to handle relay commands serially to ensure
* reliability and prevent conflict operations. Also, the esp timer is used to manage the direction change inertia.
*/
#include "esp_err.h"
#include "driver/gpio.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define RELAY_CHN_ID_ALL CONFIG_RELAY_CHN_COUNT ///< Special ID to address all channels
/**
* @brief Enumeration for relay channel direction.
*/
enum relay_chn_direction_enum {
RELAY_CHN_DIRECTION_DEFAULT, ///< Default direction of the relay channel.
RELAY_CHN_DIRECTION_FLIPPED ///< Flipped direction of the relay channel.
};
/**
* @brief Alias for the enum type relay_chn_direction_enum.
*/
typedef enum relay_chn_direction_enum relay_chn_direction_t;
/**
* @brief Enums that represent the state of a relay channel.
*/
enum relay_chn_state_enum {
RELAY_CHN_STATE_FREE, ///< The relay channel is free to run or execute commands.
RELAY_CHN_STATE_STOPPED, ///< The relay channel is stopped and not running.
RELAY_CHN_STATE_FORWARD, ///< The relay channel is running in the forward direction.
RELAY_CHN_STATE_REVERSE, ///< The relay channel is running in the reverse direction.
RELAY_CHN_STATE_FORWARD_PENDING, ///< The relay channel is pending to run in the forward direction.
RELAY_CHN_STATE_REVERSE_PENDING, ///< The relay channel is pending to run in the reverse direction.
#if CONFIG_RELAY_CHN_ENABLE_TILTING == 1
RELAY_CHN_STATE_TILT_FORWARD, ///< The relay channel is tilting for forward.
RELAY_CHN_STATE_TILT_REVERSE, ///< The relay channel is tilting for reverse.
#endif
};
/**
* @brief Alias for the enum type relay_chn_state_enum.
*/
typedef enum relay_chn_state_enum relay_chn_state_t;
/**
* @brief Relay channel state change listener.
*
* An optional interface to listen to the channel state change events.
* The listeners SHOULD be implemented as light functions and SHOULD NOT contain
* any blocking calls. Otherwise the relay_chn module would not function properly
* since it is designed as event driven.
*
* @param chn_id The ID of the channel whose state has changed.
* @param old_state The old state of the channel.
* @param new_state The new state of the channel.
*/
typedef void (*relay_chn_state_listener_t)(uint8_t chn_id, relay_chn_state_t old_state, relay_chn_state_t new_state);
/**
* @brief Create and initialize relay channels.
*
* This function initializes the relay channels based on the provided GPIO map.
*
* @param gpio_map Pointer to an array of GPIO numbers that correspond to the relay channels.
* @param gpio_count The number of GPIOs in the gpio_map array.
*
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_FAIL: General failure
*/
esp_err_t relay_chn_create(const gpio_num_t* gpio_map, uint8_t gpio_count);
/**
* @brief Register a channel state change listener.
*
* @param listener A function that implements relay_chn_state_listener_t interface.
*
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NO_MEM: No enough memory
* - ESP_FAIL: General failure
*/
esp_err_t relay_chn_register_listener(relay_chn_state_listener_t listener);
/**
* @brief Unregister a channel state change listener.
*
* @param listener A function that implements relay_chn_state_listener_t interface.
*/
void relay_chn_unregister_listener(relay_chn_state_listener_t listener);
/**
* @brief Get the state of the specified relay channel.
*
* This function retrieves the current state of the relay channel identified by the given channel ID.
*
* @param chn_id The ID of the relay channel whose state is to be retrieved.
* @return The current state of the specified relay channel.
*/
relay_chn_state_t relay_chn_get_state(uint8_t chn_id);
/**
* @brief Get the state string of the specified relay channel.
*
* This function returns a string representation of the state of the relay
* channel identified by the given channel ID.
*
* @param chn_id The ID of the relay channel whose state is to be retrieved.
* The valid range of channel IDs depends on the specific hardware
* and implementation.
*
* @return A pointer to a string representing the state of the specified relay
* channel. The returned string is managed internally and should not be
* modified or freed by the caller.
*/
char *relay_chn_get_state_str(uint8_t chn_id);
/**
* @brief Return the text presentation of an state.
*
* @param state A state with type of relay_chn_state_t.
* @return char* The text presentation of the state. "UNKNOWN" if the state is not known.
*/
char *relay_chn_state_str(relay_chn_state_t state);
/**
* @brief Runs the relay channel in the forward direction.
*
* This function activates the specified relay channel to run in the forward direction.
*
* @param chn_id The ID of the relay channel to be activated.
*/
void relay_chn_run_forward(uint8_t chn_id);
/**
* @brief Runs the relay channel in reverse.
*
* This function activates the specified relay channel to run in reverse.
*
* @param chn_id The ID of the relay channel to be reversed.
*/
void relay_chn_run_reverse(uint8_t chn_id);
/**
* @brief Stops the relay channel specified by the channel ID.
*
* This function stops the operation of the relay channel identified by the
* provided channel ID. It is typically used to turn off or disable the relay
* channel.
*
* @param chn_id The ID of the relay channel to stop.
*/
void relay_chn_stop(uint8_t chn_id);
/**
* @brief Flips the direction of the specified relay channel.
*
* This function toggles the direction of the relay channel identified by the
* given channel ID. It is typically used to change the state of the relay
* from its current direction to the opposite direction.
*
* @param chn_id The ID of the relay channel to flip. This should be a valid
* channel ID within the range of available relay channels.
*/
void relay_chn_flip_direction(uint8_t chn_id);
/**
* @brief Get the direction of the specified relay channel.
*
* This function retrieves the direction configuration of a relay channel
* identified by the given channel ID.
*
* @param chn_id The ID of the relay channel to query.
* @return The direction of the specified relay channel as a value of type
* relay_chn_direction_t.
*/
relay_chn_direction_t relay_chn_get_direction(uint8_t chn_id);
#if CONFIG_RELAY_CHN_ENABLE_TILTING == 1
void relay_chn_tilt_auto(uint8_t chn_id);
void relay_chn_tilt_forward(uint8_t chn_id);
void relay_chn_tilt_reverse(uint8_t chn_id);
void relay_chn_tilt_stop(uint8_t chn_id);
void relay_chn_tilt_sensitivity_set(uint8_t chn_id, uint8_t sensitivity);
uint8_t relay_chn_tilt_sensitivity_get(uint8_t chn_id);
#endif
#ifdef __cplusplus
}
#endif
/// @}
#endif // RELAY_CHN_H

1101
src/relay_chn.c Normal file

File diff suppressed because it is too large Load Diff

8
test/CMakeLists.txt Normal file
View File

@@ -0,0 +1,8 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components"
"../../relay_chn")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(relay_chn_test)

3
test/main/CMakeLists.txt Normal file
View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES unity test_utils relay_chn)

View File

@@ -0,0 +1,95 @@
#include "driver/gpio.h"
#include "unity.h"
#include "unity_test_utils.h"
#include "relay_chn.h"
const gpio_num_t gpio_map[] = {GPIO_NUM_4, GPIO_NUM_5, GPIO_NUM_18, GPIO_NUM_19};
const uint8_t gpio_count = sizeof(gpio_map) / sizeof(gpio_map[0]);
const uint8_t relay_chn_count = gpio_count / 2;
TEST_CASE("relay chn inits correctly", "[relay_chn]")
{
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
}
TEST_CASE("Relay channels run forward and update state", "[relay_chn][forward]")
{
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
// Test forward run on all channels
for (uint8_t i = 0; i < relay_chn_count; i++) {
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
relay_chn_run_forward(i); // Run the channel forward
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(i));
relay_chn_stop(i); // Stop the channel
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
relay_chn_flip_direction(i); // Flip the direction
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_FLIPPED, relay_chn_get_direction(i));
relay_chn_run_forward(i); // Run the channel forward
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(i));
relay_chn_stop(i); // Stop the channel
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
}
}
TEST_CASE("Relay channels run reverse and update state", "[relay_chn][reverse]")
{
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
// Test reverse run on all channels
for (uint8_t i = 0; i < relay_chn_count; i++) {
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
relay_chn_run_reverse(i); // Run the channel reverse
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(i));
relay_chn_stop(i); // Stop the channel
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
relay_chn_flip_direction(i); // Flip the direction
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_FLIPPED, relay_chn_get_direction(i));
relay_chn_run_reverse(i); // Run the channel forward
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(i));
relay_chn_stop(i); // Stop the channel
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
}
}
static void check_channels_state_unchanged(void)
{
for (uint8_t i = 0; i < relay_chn_count; i++) {
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_DEFAULT, relay_chn_get_direction(i));
}
}
TEST_CASE("Relay channels do not change state for invalid channel", "[relay_chn][invalid]")
{
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
// Test invalid channel run
relay_chn_run_forward(relay_chn_count + 1); // Run the channel forward
check_channels_state_unchanged();
relay_chn_run_reverse(relay_chn_count + 1); // Run the channel reverse
check_channels_state_unchanged();
relay_chn_stop(relay_chn_count + 1); // Stop the channel
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(relay_chn_count + 1));
check_channels_state_unchanged();
relay_chn_flip_direction(relay_chn_count + 1); // Flip the direction
check_channels_state_unchanged();
}
void setUp(void)
{
// Run before each test
}
void tearDown(void)
{
// Run after each test
}
// Test app entry point
void app_main(void)
{
// Run the Unity tests menu
unity_run_menu();
}

5
test/sdkconfig.defaults Normal file
View File

@@ -0,0 +1,5 @@
# For IDF 5.0
CONFIG_ESP_TASK_WDT_EN=n
# For IDF4.4
CONFIG_ESP_TASK_WDT=n