Control device - button - implementation of hardware device for OpenThread app.
More...
|
| void | ad_button_Init (char *deviceNameGroup) |
| | Initialize button device driver with custom device name group.
|
Control device - button - implementation of hardware device for OpenThread app.
Description
Three-button control device with multi-function capabilities for OpenThread networks.
This device features three buttons with multi-function capabilities:
- One-click: Turns the light on or off
- Double-click: Changes the light color
- Long-click: Adjusts brightness continuously (cycles between dimming and brightening)
OneButton Library Integration
The button detection and event handling is implemented using a ported version of the OneButton library originally created for Arduino, adapted for embedded systems.
OneButton Library Features
The library provides:
- Click detection: Single-click, double-click events
- Long-press detection: Continuous press with configurable timeout
- Multi-click counting: Track number of consecutive clicks
- Debouncing: Hardware debouncing for reliable button detection
- Non-blocking operation: Poll-based event detection
Platform Selection
The OneButton library supports multiple microcontroller platforms through compile-time platform selection using preprocessor macros.
To select your platform, define ONE of the following in OneButton.h:
- OB_STM32_PLATFORM - For STM32 microcontrollers with HAL
- OB_ESP32_PLATFORM - For ESP32 microcontrollers with ESP-IDF
Example in OneButton.h:
#define OB_ESP32_PLATFORM
- Warning
- Define ONLY ONE platform macro at a time
Platform-Specific Configuration
Based on the selected platform macro, OneButton.h automatically configures the appropriate GPIO and timing functions.
STM32 Platform Configuration (OB_STM32_PLATFORM):
#ifdef OB_STM32_PLATFORM
#define OB_READ_PIN() HAL_GPIO_ReadPin(Btn->GpioPort, Btn->GpioPin)
#define OB_GET_TICK() HAL_GetTick()
#define OB_BUTTON_PRESSED GPIO_PIN_RESET
#define OB_BUTTON_NOT_PRESSED GPIO_PIN_SET
#endif
ESP32 Platform Configuration (OB_ESP32_PLATFORM):
#ifdef OB_ESP32_PLATFORM
#include "xtimers.h"
#define OB_READ_PIN() gpio_get_level(Btn->GpioPin)
#define OB_GET_TICK() xTim_getTick()
#define OB_BUTTON_PRESSED 0
#define OB_BUTTON_NOT_PRESSED 1
#endif
Core types, global configuration macros, and framework initialization routines.
Macro Definitions by Platform
| Macro | Description | STM32 Implementation | ESP32 Implementation |
| OB_READ_PIN() | Read current GPIO pin state | HAL_GPIO_ReadPin() | gpio_get_level() |
| OB_GET_TICK() | Get current system tick (milliseconds) | HAL_GetTick() | xTim_getTick() |
| OB_BUTTON_PRESSED | Logic level when button is pressed | GPIO_PIN_RESET (0) | 0 (active low) |
| OB_BUTTON_NOT_PRESSED | Logic level when button is released | GPIO_PIN_SET (1) | 1 (pull-up) |
Porting to New Platforms
To add support for a new platform:
- Add platform definition macro in OneButton.h:
- Add platform-specific configuration block in OneButton.h:
#ifdef OB_YOUR_PLATFORM
#include "your_platform_gpio.h"
#include "your_platform_timer.h"
#define OB_READ_PIN() your_gpio_read(Btn->GpioPin)
#define OB_GET_TICK() your_get_milliseconds()
#define OB_BUTTON_PRESSED 0
#define OB_BUTTON_NOT_PRESSED 1
#endif
- Update OneButton_t structure if needed (e.g., add platform-specific fields):
typedef struct {
#ifdef OB_STM32_PLATFORM
GPIO_TypeDef* GpioPort;
#endif
- Add platform-specific initialization in OneButton.c:
#ifdef OB_YOUR_PLATFORM
}
#endif
Hardware Considerations
Button Connection Types:
Active Low (Pull-up) - Button connects GPIO to ground when pressed:
- STM32: Configure pin with GPIO_PULLUP in GPIO_InitTypeDef
- ESP32: Configure pin with GPIO_PULLUP_ENABLE in gpio_config_t
- Set: OB_BUTTON_PRESSED = 0, OB_BUTTON_NOT_PRESSED = 1
Active High (Pull-down) - Button connects GPIO to VCC when pressed:
- STM32: Configure pin with GPIO_PULLDOWN in GPIO_InitTypeDef
- ESP32: Configure pin with GPIO_PULLDOWN_ENABLE in gpio_config_t
- Set: OB_BUTTON_PRESSED = 1, OB_BUTTON_NOT_PRESSED = 0
- Note
- Default configuration uses active low with internal pull-up resistors
-
Ensure GPIO pins support input mode with pull-up/pull-down configuration
- Warning
- Platform macro definitions must match actual hardware configuration to ensure reliable button detection
- See also
- OneButton.h for complete library API and platform configuration
-
ad_btn_assign.h for button event handling implementation
GPIO Configuration for Buttons
Default GPIO Pin Assignment
The button device uses three GPIO pins defined in the GPIO button configuration group.
Default configuration for ESP32-C6:
- Button 1 (index 0): GPIO 3
- Button 2 (index 1): GPIO 9
- Button 3 (index 2): GPIO 15
Customizing GPIO Configuration
To modify GPIO pin assignments:
- Edit the ot_btn_gpioList[] array in GPIO button configuration
- Update AD_BUTTON_NUM_OF_BUTTONS if changing the number of buttons
- Ensure selected pins support input mode with pull-up resistors
Example - 4-button configuration:
#define AD_BUTTON_NUM_OF_BUTTONS 4
const static gpio_num_t ot_btn_gpioList[] = {GPIO_NUM_2, GPIO_NUM_4, GPIO_NUM_5, GPIO_NUM_18};
Hardware Reference (ESP32-C6)
From technical reference manual Table 7-3. IO MUX Functions List
| GPIO | Pin Name | Function 0 | Function 1 | Function 2 | Function 3 | DRV | Reset | Notes |
| 3 | GPIO3 | GPIO3 | GPIO3 | — | — | 2 | 1 | R VDDPST1 domain, ADC1_CH3 |
| 8 | GPIO8 | GPIO8 | GPIO8 | — | — | 2 | 1 | reserved for WS2812B |
| 9 | GPIO9 | GPIO9 | GPIO9 | — | — | 2 | 3 | — |
| 15 | GPIO15 | GPIO15 | GPIO15 | — | — | 2 | 1 | — |
Reset column legend:
- 1 = IE = 1 (input enabled)
- 3 = IE = 1, WPU = 1 (input enabled, pull-up resistor enabled)
- Warning
- Avoid using GPIO 8 for buttons as it is reserved for WS2812B LED control.
- See also
- GPIO button configuration
Procedure for Assigning a New Device to a Button
- Press the selected button briefly.
- Turn on the corresponding device (button → light).
- After a short moment, the device will be assigned to the selected button.
- Note
- To successfully assign a new device, it must be in the same device name group (API: drv->deviceName) and be on the list of allowed devices (API: drv->pairRuleGetList_clb, otapp_pair_rule_t).
Device Pairing Rules Configuration
The button device supports configurable pairing rules that control which OpenThread devices can be paired with the buttons. Three predefined rule sets are available:
Custom Device Rules (Default)
Variable: ad_button_deviceRules
Custom rule set allowing pairing only with specific device types.
Default configuration allows:
Example configuration:
.allowed = {
}
};
@ OTAPP_LIGHTING_ON_OFF
Simple On/Off light.
Definition ot_app.h:87
@ OTAPP_LIGHTING
Generic lighting device.
Definition ot_app.h:86
@ OTAPP_LIGHTING_DIMM
Dimmable light.
Definition ot_app.h:88
@ OTAPP_LIGHTING_RGB
Color changing light (RGB).
Definition ot_app.h:89
#define OTAPP_PAIR_END_OF_RULES
Definition ot_app_pair.h:85
Rules structure defining which device types are allowed to pair.
Definition ot_app_pair.h:129
Allow All Devices
Variable: ad_button_deviceRules_all_allowed
Special rule set that allows pairing with any OpenThread device without restrictions.
Configuration:
};
#define OTAPP_PAIR_NO_RULES
Definition ot_app_pair.h:83
Use case: Development, testing, or flexible deployment scenarios.
Block All Devices
Variable: ad_button_deviceRules_no_allowed
Special rule set that blocks pairing with all devices.
Configuration:
};
#define OTAPP_PAIR_NO_ALLOWED
Definition ot_app_pair.h:84
Use case: Security lock-down, maintenance mode, or when pairing should be temporarily disabled.
Selecting Active Rule Set
During initialization in ad_button_Init(), choose which rule set to use:
Device Pairing and Status Update Callbacks
The button device provides two important callbacks for handling device pairing events and receiving status updates from subscribed devices.
Paired Device Callback
Function: ad_button_pairedCallback(otapp_pair_Device_t *device)
This callback is invoked when a new device is successfully paired and passes the configured device pairing rules (deviceRules).
Parameters:
- device - Pointer to structure containing paired device information
Device Information Structure:
typedef struct {
#define OTAPP_PAIR_NAME_FULL_SIZE
Definition ot_app_pair.h:66
#define OTAPP_PAIR_URI_MAX
Max number of URIs per device (from ot_app.h).
Definition ot_app_pair.h:61
Represents a fully paired remote device.
Definition ot_app_pair.h:114
Represents a single URI endpoint belonging to a paired device.
Definition ot_app_pair.h:104
Callback Behavior:
- Receives device information immediately after successful pairing
- Device has already passed the pairing rule filter (deviceRules)
- Device is ready for assignment to a button
- URI list contains all CoAP resources available on the paired device
Implementation Example:
{
printf(
"New device paired: %s\n", device->
devNameFull);
printf(
"IPv6 Address: %s\n", ipv6_to_string(&device->
ipAddr));
}
}
}
int8_t ad_btn_assignDevice(otapp_pair_Device_t *newDevice)
Definition ad_btn_assign.c:406
otIp6Address ipAddr
Mesh-Local EID IPv6 address.
Definition ot_app_pair.h:116
char devNameFull[OTAPP_PAIR_NAME_FULL_SIZE]
Full hostname (e.g. "device1_1_588c...service.arpa").
Definition ot_app_pair.h:115
otapp_pair_uris_t urisList[OTAPP_PAIR_URI_MAX]
List of discovered services/URIs on this device.
Definition ot_app_pair.h:117
char uri[OTAPP_URI_MAX_NAME_LENGHT]
URI path.
Definition ot_app_pair.h:105
Subscribed URI Status Update Callback
Function: ad_button_subscribedUrisCallback(oac_uri_dataPacket_t *data)
This callback receives status updates from devices that have been subscribed to via CoAP Observe mechanism. For example, when a light's state changes (on/off, brightness, color), this callback receives the updated status information.
Parameters:
- data - Pointer to structure containing status update data
Data Packet Structure:
typedef struct {
uint8_t oacu_token_t
Definition ot_app_coap_uri_obs.h:53
#define OAC_URI_OBS_BUFFER_SIZE
Definition ot_app_coap_uri_obs.h:43
#define OAC_URI_OBS_TOKEN_LENGTH
Definition ot_app_coap_uri_obs.h:38
Definition ot_app_coap_uri_obs.h:71
Callback Behavior:
- Receives status updates from all subscribed device URIs
- Token identifies which specific URI/device sent the update
- Buffer contains device-specific status data (format depends on device type)
- Typically used to update UI, trigger actions, or synchronize state
Common Status Data Formats:
- Lighting devices: on/off state, brightness level, RGB color values
- Switch devices: switch state, position
- Sensor devices: temperature, humidity, motion detection
Implementation Example:
{
uint32_t uriState = 0;
uriState |= (uint32_t)data->buffer[0];
uriState |= ((uint32_t)data->buffer[1] << 8);
uriState |= ((uint32_t)data->buffer[2] << 16);
uriState |= ((uint32_t)data->buffer[3] << 24);
drv->api.pair.uriStateSet(pairHandle, data->token, &uriState);
printf("URI status update received\n");
printf(" Token: 0x%02x%02x%02x%02x\n",
data->token[0], data->token[1], data->token[2], data->token[3]);
printf(" State: %lu\n", uriState);
}
Definition ot_app_pair.c:46
Callback Flow Diagram
Device Discovery
↓
Check Pairing Rules (deviceRules)
↓
↓
Subscribe to device URIs (CoAP Observe)
↓
Device status changes
↓
- Note
- Callbacks are invoked by the OpenThread framework asynchronously
-
Callbacks should be non-blocking and complete quickly
-
Heavy processing should be deferred to application task
- See also
- ad_button_deviceRules for pairing rule configuration
-
otapp_pair_Device_t for paired device structure definition
-
oac_uri_dataPacket_t for status update data structure
Procedure for Restoring the Device to Factory Condition
- Press and hold the first button (OT_BTN_RESET_LONGPRESS_GPIO) continuously.
- While holding first button, click the second button (OT_BTN_RESET_ONE_CLICK_GPIO) one-click 5 (OT_BTN_RESET_OC_CNT_RESET) times within a specified short time window (10 seconds OT_BTN_RESET_TIME_MS).
- Upon detecting this combination, the device will signal (blink an LED) that the factory reset process has started.
- After the signal, the device will erase all user settings regarding devices assigned to buttons.
- See also
- Defines for factory reset settings
-
ad_btn_assign.h for button function assignment API
-
ad_btn_uri.h for CoAP URI configuration
-
ad_btn_dimControl.h for brightness control configuration
- Version
- 0.1
- Date
- 06-09-2025
- Author
- Jan Łukaszewicz (plhar.nosp@m.eo@g.nosp@m.mail..nosp@m.com)
- Copyright
- © 2025 MIT LICENCE
◆ ad_button_Init()
| void ad_button_Init |
( |
char * | deviceNameGroup | ) |
|
Initialize button device driver with custom device name group.
- Parameters
-
| [in] | deviceNameGroup | Device name group identifier for network pairing |
Performs complete initialization of the button device including:
Step 1: Validate device name group
- Checks if deviceNameGroup parameter is valid (not NULL)
- Returns immediately if NULL to prevent crashes
Step 2: Set device name group
- Copies deviceNameGroup string to internal buffer (ad_button_deviceNameTab)
- Maximum length: 9 characters (OTAPP_DEVICENAME_SIZE - 1)
- Uses strcpy() for string copy
Step 3: Get driver instance
- Retrieves singleton device driver instance
Step 4: Initialize NVS (Non-Volatile Storage)
- Required for storing paired device information persistently
Step 5: Initialize sub-modules
Step 6: Register pairing rules callback Choose one of three options:
Step 7: Register URI callbacks
- uriGetList_clb: Provides list of supported CoAP URIs
- uriGetListSize: Returns number of supported URIs
Step 8: Register observer callbacks
- obs_pairedDevice_clb: Called when device is paired
- obs_subscribedUri_clb: Called when subscribed URI status updates
Step 9: Set device metadata
- deviceName: Points to ad_button_deviceNameTab containing the name group
- deviceType: OTAPP_SWITCH type
- task: Periodic task function pointer
Device Name Group
The device name group is used for:
- Network Discovery: Identifies this device on the OpenThread network
- Pairing Filter: Only devices with matching name group can be paired
- Device Grouping: Groups related devices together (e.g., "living_room", "bedroom")
Naming Conventions:
- Maximum length: 9 characters (OTAPP_DEVICENAME_SIZE - 1)
- Valid characters: a-z, A-Z, 0-9, underscore (_)
- No spaces or special characters
- Case-sensitive
Examples:
- Note
- Device name group must match between button device and target devices for pairing
-
Devices can only pair with others in the same name group
- Warning
- Must be called before any button operations
-
deviceNameGroup must not be NULL
-
deviceNameGroup length must not exceed 9 characters
-
NVS must be initialized or function will return early
- See also
- ad_btn_init() for button module initialization
-
ad_btn_uri_init() for URI module initialization
-
ad_button_pairedCallback() for device pairing handler
-
ad_button_subscribedUrisCallback() for status update handler