Add ESP-NOW bridge example and necessary support

This commit is contained in:
Zhang Wei
2023-04-26 23:57:57 +08:00
committed by Hrishikesh Dhayagude
parent 0b1762e511
commit 853b2666d4
19 changed files with 1390 additions and 1 deletions
+5
View File
@@ -104,6 +104,11 @@ variables:
- cd $ESP_MATTER_PATH/examples/controller
- idf.py set-target esp32
- idf.py build
- cd $ESP_MATTER_PATH/examples/esp-now_bridge_light
- idf.py set-target esp32
- idf.py build
- idf.py set-target esp32c3
- idf.py build
.build_external_platform_example: &build_external_platform_example
- rm -rf $ESP_MATTER_PATH/../platform
@@ -209,6 +209,11 @@ esp_err_t set_device_type(device_t *bridged_device, uint32_t device_type_id)
bridged_device->endpoint = extended_color_light::add(bridged_device->endpoint, &extended_color_light_conf);
break;
}
case ESP_MATTER_ON_OFF_SWITCH_DEVICE_TYPE_ID: {
on_off_switch::config_t switch_config;
bridged_device->endpoint = on_off_switch::add(bridged_device->endpoint, &switch_config);
break;
}
default: {
ESP_LOGE(TAG, "Unsupported bridged matter device type");
return ESP_ERR_INVALID_ARG;
@@ -116,6 +116,16 @@ app_bridged_device_address_t app_bridge_blemesh_address(uint16_t blemesh_addr)
return bridged_address;
}
app_bridged_device_address_t app_bridge_espnow_address(uint8_t espnow_macaddr[6], uint16_t espnow_initiator_attr)
{
app_bridged_device_address_t bridged_address = {
.espnow_macaddr = {0},
};
memcpy(bridged_address.espnow_macaddr, espnow_macaddr, 6);
ESP_LOGI(TAG, "espnow_initiator_attr: %d", espnow_initiator_attr);
return bridged_address;
}
/** Bridged Device APIs */
app_bridged_device_t *app_bridge_create_bridged_device(node_t *node, uint16_t parent_endpoint_id,
uint32_t matter_device_type_id,
@@ -309,4 +319,46 @@ uint16_t app_bridge_get_blemesh_addr_by_matter_endpointid(uint16_t matter_endpoi
}
return 0xFFFF;
}
/** ESP-NOW Device APIs */
app_bridged_device_t *app_bridge_get_device_by_espnow_macaddr(uint8_t espnow_macaddr[6])
{
app_bridged_device_t *current_dev = g_bridged_device_list;
while (current_dev) {
if ((current_dev->dev_type == ESP_MATTER_BRIDGED_DEVICE_TYPE_ESPNOW) && current_dev->dev
&& !memcmp(current_dev->dev_addr.espnow_macaddr, espnow_macaddr, 6)
) {
return current_dev;
}
current_dev = current_dev->next;
}
return NULL;
}
uint16_t app_bridge_get_matter_endpointid_by_espnow_macaddr(uint8_t espnow_macaddr[6])
{
app_bridged_device_t *current_dev = g_bridged_device_list;
while (current_dev) {
if ((current_dev->dev_type == ESP_MATTER_BRIDGED_DEVICE_TYPE_ESPNOW) && current_dev->dev
&& !memcmp(current_dev->dev_addr.espnow_macaddr, espnow_macaddr, 6)
) {
return esp_matter::endpoint::get_id(current_dev->dev->endpoint);
}
current_dev = current_dev->next;
}
return chip::kInvalidEndpointId;
}
uint8_t* app_bridge_get_espnow_macaddr_by_matter_endpointid(uint16_t matter_endpointid)
{
app_bridged_device_t *current_dev = g_bridged_device_list;
while (current_dev) {
if ((current_dev->dev_type == ESP_MATTER_BRIDGED_DEVICE_TYPE_ESPNOW) && current_dev->dev &&
(esp_matter::endpoint::get_id(current_dev->dev->endpoint) == matter_endpointid)) {
return current_dev->dev_addr.espnow_macaddr;
}
current_dev = current_dev->next;
}
return NULL;
}
#endif
@@ -13,7 +13,6 @@
// limitations under the License.
#pragma once
#include <sdkconfig.h>
#include <esp_matter_bridge.h>
@@ -25,6 +24,8 @@ typedef enum {
ESP_MATTER_BRIDGED_DEVICE_TYPE_ZIGBEE = 0,
/** BLE Mesh */
ESP_MATTER_BRIDGED_DEVICE_TYPE_BLEMESH,
/** ESP-NOW */
ESP_MATTER_BRIDGED_DEVICE_TYPE_ESPNOW,
} app_bridged_device_type_t;
/* Bridged Device Address */
@@ -38,6 +39,10 @@ typedef union {
struct {
uint16_t blemesh_addr;
};
/** ESP-NOW */
struct {
uint8_t espnow_macaddr[6];
};
} app_bridged_device_address_t;
/* Bridged Device */
@@ -57,6 +62,8 @@ app_bridged_device_address_t app_bridge_zigbee_address(uint8_t zigbee_endpointid
app_bridged_device_address_t app_bridge_blemesh_address(uint16_t blemesh_addr);
app_bridged_device_address_t app_bridge_espnow_address(uint8_t espnow_macaddr[6], uint16_t espnow_initiator_attr);
/** Bridged Device APIs */
app_bridged_device_t *app_bridge_create_bridged_device(node_t *node, uint16_t parent_endpoint_id,
uint32_t matter_device_type_id,
@@ -80,3 +87,10 @@ app_bridged_device_t *app_bridge_get_device_by_blemesh_addr(uint16_t blemesh_add
uint16_t app_bridge_get_matter_endpointid_by_blemesh_addr(uint16_t blemesh_addr);
uint16_t app_bridge_get_blemesh_addr_by_matter_endpointid(uint16_t matter_endpointid);
/** ESP-NOW Device APIs */
app_bridged_device_t *app_bridge_get_device_by_espnow_macaddr(uint8_t espnow_macaddr[6]);
uint16_t app_bridge_get_matter_endpointid_by_espnow_macaddr(uint8_t espnow_macaddr[6]);
uint8_t* app_bridge_get_espnow_macaddr_by_matter_endpointid(uint16_t matter_endpointid);
@@ -0,0 +1,46 @@
# 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)
if(NOT DEFINED ENV{ESP_MATTER_PATH})
message(FATAL_ERROR "Please set ESP_MATTER_PATH to the path of esp-matter repo")
endif(NOT DEFINED ENV{ESP_MATTER_PATH})
if(NOT DEFINED ENV{ESP_MATTER_DEVICE_PATH})
if("${IDF_TARGET}" STREQUAL "esp32" OR "${IDF_TARGET}" STREQUAL "")
set(ENV{ESP_MATTER_DEVICE_PATH} $ENV{ESP_MATTER_PATH}/device_hal/device/esp32_devkit_c)
elseif("${IDF_TARGET}" STREQUAL "esp32c3")
set(ENV{ESP_MATTER_DEVICE_PATH} $ENV{ESP_MATTER_PATH}/device_hal/device/esp32c3_devkit_m)
elseif("${IDF_TARGET}" STREQUAL "esp32s3")
set(ENV{ESP_MATTER_DEVICE_PATH} $ENV{ESP_MATTER_PATH}/device_hal/device/esp32s3_devkit_c)
else()
message(FATAL_ERROR "Unsupported IDF_TARGET")
endif()
endif(NOT DEFINED ENV{ESP_MATTER_DEVICE_PATH})
set(PROJECT_VER "v1.0")
set(PROJECT_VER_NUMBER 1)
set(ESP_MATTER_PATH $ENV{ESP_MATTER_PATH})
set(MATTER_SDK_PATH ${ESP_MATTER_PATH}/connectedhomeip/connectedhomeip)
set(ZAP_GENERATED_PATH ${CMAKE_CURRENT_LIST_DIR}/main/zap-generated)
# This should be done before using the IDF_TARGET variable.
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
include($ENV{ESP_MATTER_DEVICE_PATH}/esp_matter_device.cmake)
set(EXTRA_COMPONENT_DIRS
"${ESP_MATTER_PATH}/examples/common"
"${MATTER_SDK_PATH}/config/esp32/components"
"${ESP_MATTER_PATH}/components"
"${ESP_MATTER_PATH}/device_hal/device"
${extra_components_dirs_append})
project(espnow_light)
idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++14;-Os;-DCHIP_HAVE_CONFIG_H" APPEND)
idf_build_set_property(C_COMPILE_OPTIONS "-Os" APPEND)
# For RISCV chips, project_include.cmake sets -Wno-format, but does not clear various
# flags that depend on -Wformat
idf_build_set_property(COMPILE_OPTIONS "-Wno-format-nonliteral;-Wno-format-security;-Wformat=0" APPEND)
+167
View File
@@ -0,0 +1,167 @@
# ESP-NOW Matter Bridge Light
This example demonstrates a Matter to ESP-NOW bridge that bridges an ESP-NOW switch device to Matter fabric. It integrates ESP-NOW into a Matter light device to create a ESP-NOW bridge light.
In the ESP-NOW part, it acts as a responder whereas a ESP-NOW switch is an initiator.
In the Matter part, it creates a Color Temperature Light device and an aggregator device using the ESP Matter data model. When binding with an ESP-NOW switch is complete, it also creates a dynamic OnOff Switch device that works like a normal Matter switch. When unbind from the ESP-NOW switch, the dynamic switch device is removed.
See the [docs](https://docs.espressif.com/projects/esp-matter/en/main/esp32/developing.html) for more information about building and flashing the firmware.
## 1. Additional Environment Setup
* Use a DevKit with LED, for example ESP32-C3-DevKitM to get more status indication.
* Use the [switch example](https://github.com/espressif/esp-now/tree/master/examples/coin_cell_demo/switch) in ESP-NOW repository as the ESP-NOW switch.
* Use the light example in this repository as the light device.
## 2. Post Commissioning Setup
> Note: Matter Wi-Fi devices enters modem sleep after commissioning. This will impact the ESP-NOW operation as ESP-NOW low power is not yet integrated. A responder device cannot enter sleep mode in order to receive messages from an initiator any time. This is done by setting WIFI_PS_NONE. Reboot the bridge after commissioning to apply the setting and let the bridge exit sleep mode.
When esp-matter is updated to work in IDFv5.0, this example will be updated to enable ESP-NOW low power. Then disabling sleep is not required.
{.is-info}
### 2.1 Discovering bridge endpoint
You can read the parts list from the Bridge to get the number of the bridged devices.
```
descriptor read parts-list 0x7283 0x0
```
If binding with ESP-NOW switch is not done, you will get 2 entries 1 and 2. Example:
```
PartsList: 2 entries
[1]: 1
[2]: 2
```
You can read the device type list for each of the endpoints.
```
descriptor read device-type-list 0x7283 1
descriptor read device-type-list 0x7283 2
```
Endpoint 1:
```
DeviceTypeList: 1 entries
[1]: {
Type: 268 <-------------------- Color temperature light device type
Revision: 2
}
```
Endpoint 2:
```
DeviceTypeList: 1 entries
[1]: {
Type: 14 <-------------------- Aggregator device type
Revision: 1
}
```
### 2.x Bind bridge to a ESP-NOW switch
Trigger binding from the ESP-NOW switch. If you are using a C3 DevKit, double click the Boot button. If binding is successful, the LED on the DevKit running bridge example will toggle. A dynamic endpoint will be added on the Bridge device. You can read the parts list again to get the dynamic endpoint ID.
```
descriptor read parts-list 0x7283 0
```
There is a new endpoint added to the device. Example:
```
PartsList: 3 entries
[1]: 1
[2]: 2
[3]: 3
```
You can read the device type list for the new endpoint.
```
descriptor read device-type-list 0x7283 3
```
Endpoint 3:
```
DeviceTypeList: 2 entries
[1]: {
Type: 19 <-------------------- Bridged node device type
Revision: 1
}
[2]: {
Type: 259 <-------------------- ONOFF switch device type
Revision: 2
}
```
There are two device types in this endpoint: 19 (0x0013) is bridged node device type, and 259 (0x0103) is ONOFF switch device type. You can read the cluster servers and clients list on the dynamic endpoint.
```
descriptor read server-list 0x7283 3
descriptor read client-list 0x7283 3
```
Server list:
```
ServerList: 5 entries
[1]: 29
[2]: 57 <-------------------- Bridged device basic information cluster
[3]: 3
[4]: 30 <-------------------- Binding cluster
[5]: 4
```
Client list:
```
ClientList: 3 entries
[1]: 3
[2]: 6 <---------------------- OnOff Cluster
[3]: 4
```
### 2.3 Bind light to bridge
Follow the [instruction](../light_switch/README.md#21-bind-light-to-switch) in the light switch example to create a binding between the bridge and a Matter light. After that, pressing on the ESP-NOW switch will toggle both the LED on the bridge and the Matter light.
## 3. Device Perfromance
### 3.1 Memory Usage
The following is the Memory and Flash Usage.
- `Bootup` == Device just finished booting up. Device is not commissioned or connected to wifi yet.
- `After Commissioning` == Device is connected to wifi and is also commissioned and rebooted.
- `After Creating Bridge` == Device is bound to an ESP-NOW switch.
- device used: esp32c3_devkit_m
- tested on: [3d643be](https://github.com/espressif/esp-matter/commit/3d643befa5d78344321f09a0280655f1297d5757)
(2023-04-06)
- IDF: release/v5.1
| | Bootup | After Commissioning | After Creating Bridge |
|:- |:-: |:-: |:-: |
|**Free Internal Memory** |62KB |127KB |124KB |
**Flash Usage**: Firmware binary size: 1.3MB
Note that the steps of commissioning and creating bridge are interchangable. This should give you a good idea about the amount of free memory that is
available for you to run your application's code.
Applications that do not require BLE post commissioning, can disable it using app_ble_disable() once commissioning is complete.
## 4. Pending Issues
### 4.1 Responder Forwarding
Go to ESP-NOW component source, in src/control/src/espnow_ctrl.c, comment the call to `espnow_ctrl_responder_forward()`. The purpose of this function is to allow responders to forward received messages to other responders, increasing the range and reliability of message reception in ESP-NOW network. This function does not assume which channel the receivers are in, so it will switch to every Wi-Fi channel and perform an espnow send. However in the Matter network, after the bridge is commissioned, it is not allowed to switch channel. The call to this function will return error. To prevent unnecessary errors, it is recommend to disable the forwarding.
### 4.2 Channel for ESP-NOW Button
If the ESP-NOW switch used is an ESP-NOW button, it has little capability to switch channel when sending messages. If the bridge switches channel after commissioning, it is difficult for the ESP-NOW button to scan all channels to search for the bridge. In this case, set the Wi-Fi channel of the AP to channel 1 or 6, as these are the first two channels when the button performs scanning, hence more likely to finish scanning successfully before it loses power.
@@ -0,0 +1,8 @@
set(PRIV_REQUIRES_LIST device esp_matter esp_matter_console app_bridge app_reset)
idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES ${PRIV_REQUIRES_LIST})
set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 14)
target_compile_options(${COMPONENT_LIB} PRIVATE "-DCHIP_HAVE_CONFIG_H")
@@ -0,0 +1,400 @@
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <esp_log.h>
#include <stdlib.h>
#include <string.h>
#include <device.h>
#include <esp_matter.h>
#include <esp_matter_console.h>
#include <led_driver.h>
#include <app_priv.h>
using namespace chip::app::Clusters;
using namespace esp_matter;
using namespace esp_matter::cluster;
static const char *TAG = "app_driver";
extern uint16_t light_endpoint_id;
#if CONFIG_ENABLE_CHIP_SHELL
static char console_buffer[101] = {0};
static esp_err_t app_driver_bound_console_handler(int argc, char **argv)
{
if (argc == 1 && strncmp(argv[0], "help", sizeof("help")) == 0) {
printf("Bound commands:\n"
"\thelp: Print help\n"
"\tinvoke: <local_endpoint_id> <cluster_id> <command_id> parameters ... \n"
"\t\tExample: matter esp bound invoke 0x0001 0x0008 0x0000 0x50 0x0 0x1 0x1.\n"
"\tinvoke-group: <local_endpoint_id> <cluster_id> <command_id> parameters ...\n"
"\t\tExample: matter esp bound invoke-group 0x0001 0x0008 0x0000 0x50 0x0 0x1 0x1.\n");
} else if (argc >= 4 && strncmp(argv[0], "invoke", sizeof("invoke")) == 0) {
client::command_handle_t cmd_handle;
uint16_t local_endpoint_id = strtol((const char *)&argv[1][2], NULL, 16);
cmd_handle.cluster_id = strtol((const char *)&argv[2][2], NULL, 16);
cmd_handle.command_id = strtol((const char *)&argv[3][2], NULL, 16);
cmd_handle.is_group = false;
if (argc > 4) {
console_buffer[0] = argc - 4;
for (int i = 0; i < (argc - 4); i++) {
if ((argv[4+i][0] != '0') || (argv[4+i][1] != 'x') || (strlen((const char*)&argv[4+i][2]) > 10)) {
ESP_LOGE(TAG, "Incorrect arguments. Check help for more details.");
return ESP_ERR_INVALID_ARG;
}
strcpy((console_buffer + 1 + 10*i), &argv[4+i][2]);
}
cmd_handle.command_data = console_buffer;
}
client::cluster_update(local_endpoint_id, &cmd_handle);
} else if (argc >= 4 && strncmp(argv[0], "invoke-group", sizeof("invoke-group")) == 0) {
client::command_handle_t cmd_handle;
uint16_t local_endpoint_id = strtol((const char *)&argv[1][2], NULL, 16);
cmd_handle.cluster_id = strtol((const char *)&argv[2][2], NULL, 16);
cmd_handle.command_id = strtol((const char *)&argv[3][2], NULL, 16);
cmd_handle.is_group = true;
if (argc > 4) {
console_buffer[0] = argc - 4;
for (int i = 0; i < (argc - 4); i++) {
if ((argv[4+i][0] != '0') || (argv[4+i][1] != 'x') || (strlen((const char*)&argv[4+i][2]) > 10)) {
ESP_LOGE(TAG, "Incorrect arguments. Check help for more details.");
return ESP_ERR_INVALID_ARG;
}
strcpy((console_buffer + 1 + 10*i), &argv[4+i][2]);
}
cmd_handle.command_data = console_buffer;
}
client::cluster_update(local_endpoint_id, &cmd_handle);
}
else {
ESP_LOGE(TAG, "Incorrect arguments. Check help for more details.");
return ESP_ERR_INVALID_ARG;
}
console_buffer[0] = 0;
return ESP_OK;
}
static esp_err_t app_driver_client_console_handler(int argc, char **argv)
{
if (argc == 1 && strncmp(argv[0], "help", sizeof("help")) == 0) {
printf("Client commands:\n"
"\thelp: Print help\n"
"\tinvoke: <fabric_index> <remote_node_id> <remote_endpoint_id> <cluster_id> <command_id> parameters ... \n"
"\t\tExample: matter esp client invoke 0x0001 0xBC5C01 0x0001 0x0008 0x0000 0x50 0x0 0x1 0x1.\n"
"\tinvoke-group: <fabric_index> <group_id> <cluster_id> <command_id> parameters ... \n"
"\t\tExample: matter esp client invoke-group 0x0001 0x257 0x0008 0x0000 0x50 0x0 0x1 0x1.\n");
} else if (argc >= 6 && strncmp(argv[0], "invoke", sizeof("invoke")) == 0) {
client::command_handle_t cmd_handle;
uint8_t fabric_index = strtol((const char *)&argv[1][2], NULL, 16);
uint64_t node_id = strtol((const char *)&argv[2][2], NULL, 16);
cmd_handle.endpoint_id = strtol((const char *)&argv[3][2], NULL, 16);
cmd_handle.cluster_id = strtol((const char *)&argv[4][2], NULL, 16);
cmd_handle.command_id = strtol((const char *)&argv[5][2], NULL, 16);
cmd_handle.is_group = false;
if (argc > 6) {
console_buffer[0] = argc - 6;
for (int i = 0; i < (argc - 6); i++) {
if ((argv[6+i][0] != '0') || (argv[6+i][1] != 'x') || (strlen((const char*)&argv[6+i][2]) > 10)) {
ESP_LOGE(TAG, "Incorrect arguments. Check help for more details.");
return ESP_ERR_INVALID_ARG;
}
strcpy((console_buffer + 1 + 10*i), &argv[6+i][2]);
}
cmd_handle.command_data = console_buffer;
}
client::connect(fabric_index, node_id, &cmd_handle);
} else if (argc >= 5 && strncmp(argv[0], "invoke-group", sizeof("invoke-group")) == 0) {
client::command_handle_t cmd_handle;
uint8_t fabric_index = strtol((const char *)&argv[1][2], NULL, 16);
cmd_handle.group_id = strtol((const char *)&argv[2][2], NULL, 16);
cmd_handle.cluster_id = strtol((const char *)&argv[3][2], NULL, 16);
cmd_handle.command_id = strtol((const char *)&argv[4][2], NULL, 16);
cmd_handle.is_group = true;
if (argc > 5) {
console_buffer[0] = argc - 5;
for (int i = 0; i < (argc - 5); i++) {
if ((argv[5+i][0] != '0') || (argv[5+i][1] != 'x') || (strlen((const char*)&argv[5+i][2]) > 10)) {
ESP_LOGE(TAG, "Incorrect arguments. Check help for more details.");
return ESP_ERR_INVALID_ARG;
}
strcpy((console_buffer + 1 + 10*i), &argv[5+i][2]);
}
cmd_handle.command_data = console_buffer;
}
client::group_command_send(fabric_index, &cmd_handle);
}else {
ESP_LOGE(TAG, "Incorrect arguments. Check help for more details.");
return ESP_ERR_INVALID_ARG;
}
console_buffer[0] = 0;
return ESP_OK;
}
static void app_driver_register_commands()
{
/* Add console command for bound devices */
static const esp_matter::console::command_t bound_command = {
.name = "bound",
.description = "This can be used to simulate on-device control for bound devices."
"Usage: matter esp bound <bound_command>. "
"Bound commands: help, invoke",
.handler = app_driver_bound_console_handler,
};
esp_matter::console::add_commands(&bound_command, 1);
/* Add console command for client to control non-bound devices */
static const esp_matter::console::command_t client_command = {
.name = "client",
.description = "This can be used to simulate on-device control for client devices."
"Usage: matter esp client <client_command>. "
"Client commands: help, invoke",
.handler = app_driver_client_console_handler,
};
esp_matter::console::add_commands(&client_command, 1);
}
#endif // CONFIG_ENABLE_CHIP_SHELL
void app_driver_client_command_callback(client::peer_device_t *peer_device, client::command_handle_t *cmd_handle,
void *priv_data)
{
if (cmd_handle->cluster_id == OnOff::Id) {
switch(cmd_handle->command_id) {
case OnOff::Commands::Off::Id:
{
on_off::command::send_off(peer_device, cmd_handle->endpoint_id);
break;
};
case OnOff::Commands::On::Id:
{
on_off::command::send_on(peer_device, cmd_handle->endpoint_id);
break;
};
case OnOff::Commands::Toggle::Id:
{
on_off::command::send_toggle(peer_device, cmd_handle->endpoint_id);
break;
};
default:
break;
}
} else if (cmd_handle->cluster_id == Identify::Id) {
if (cmd_handle->command_id == Identify::Commands::Identify::Id) {
if (((char *)cmd_handle->command_data)[0] != 1) {
ESP_LOGE(TAG, "Number of parameters error");
return;
}
identify::command::send_identify(peer_device, cmd_handle->endpoint_id,
strtol((const char *)(cmd_handle->command_data) + 1, NULL, 16));
} else {
ESP_LOGE(TAG, "Unsupported command");
}
} else {
ESP_LOGE(TAG, "Unsupported cluster");
}
}
void app_driver_client_group_command_callback(uint8_t fabric_index, client::command_handle_t *cmd_handle, void *priv_data)
{
if (cmd_handle->cluster_id == OnOff::Id) {
switch(cmd_handle->command_id) {
case OnOff::Commands::Off::Id:
{
on_off::command::group_send_off(fabric_index, cmd_handle->group_id);
break;
};
case OnOff::Commands::On::Id:
{
on_off::command::group_send_on(fabric_index, cmd_handle->group_id);
break;
};
case OnOff::Commands::Toggle::Id:
{
on_off::command::group_send_toggle(fabric_index, cmd_handle->group_id);
break;
};
default:
break;
}
} else if (cmd_handle->cluster_id == Identify::Id) {
if (cmd_handle->command_id == Identify::Commands::Identify::Id) {
if (((char *)cmd_handle->command_data)[0] != 1) {
ESP_LOGE(TAG, "Number of parameters error");
return;
}
identify::command::group_send_identify(fabric_index, cmd_handle->group_id,
strtol((const char *)(cmd_handle->command_data) + 1, NULL, 16));
} else {
ESP_LOGE(TAG, "Unsupported command");
}
} else {
ESP_LOGE(TAG, "Unsupported cluster");
}
}
/* Do any conversions/remapping for the actual value here */
static esp_err_t app_driver_light_set_power(led_driver_handle_t handle, esp_matter_attr_val_t *val)
{
return led_driver_set_power(handle, val->val.b);
}
static esp_err_t app_driver_light_set_brightness(led_driver_handle_t handle, esp_matter_attr_val_t *val)
{
int value = REMAP_TO_RANGE(val->val.u8, MATTER_BRIGHTNESS, STANDARD_BRIGHTNESS);
return led_driver_set_brightness(handle, value);
}
static esp_err_t app_driver_light_set_hue(led_driver_handle_t handle, esp_matter_attr_val_t *val)
{
int value = REMAP_TO_RANGE(val->val.u8, MATTER_HUE, STANDARD_HUE);
return led_driver_set_hue(handle, value);
}
static esp_err_t app_driver_light_set_saturation(led_driver_handle_t handle, esp_matter_attr_val_t *val)
{
int value = REMAP_TO_RANGE(val->val.u8, MATTER_SATURATION, STANDARD_SATURATION);
return led_driver_set_saturation(handle, value);
}
static esp_err_t app_driver_light_set_temperature(led_driver_handle_t handle, esp_matter_attr_val_t *val)
{
uint32_t value = REMAP_TO_RANGE_INVERSE(val->val.u16, STANDARD_TEMPERATURE_FACTOR);
return led_driver_set_temperature(handle, value);
}
static void app_driver_button_toggle_cb(void *arg, void *data)
{
ESP_LOGI(TAG, "Toggle button pressed");
uint16_t endpoint_id = light_endpoint_id;
uint32_t cluster_id = OnOff::Id;
uint32_t attribute_id = OnOff::Attributes::OnOff::Id;
node_t *node = node::get();
endpoint_t *endpoint = endpoint::get(node, endpoint_id);
cluster_t *cluster = cluster::get(endpoint, cluster_id);
attribute_t *attribute = attribute::get(cluster, attribute_id);
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);
val.val.b = !val.val.b;
attribute::update(endpoint_id, cluster_id, attribute_id, &val);
}
esp_err_t app_driver_attribute_update(app_driver_handle_t driver_handle, uint16_t endpoint_id, uint32_t cluster_id,
uint32_t attribute_id, esp_matter_attr_val_t *val)
{
esp_err_t err = ESP_OK;
if (endpoint_id == light_endpoint_id) {
led_driver_handle_t handle = (led_driver_handle_t)driver_handle;
if (cluster_id == OnOff::Id) {
if (attribute_id == OnOff::Attributes::OnOff::Id) {
err = app_driver_light_set_power(handle, val);
}
} else if (cluster_id == LevelControl::Id) {
if (attribute_id == LevelControl::Attributes::CurrentLevel::Id) {
err = app_driver_light_set_brightness(handle, val);
}
} else if (cluster_id == ColorControl::Id) {
if (attribute_id == ColorControl::Attributes::CurrentHue::Id) {
err = app_driver_light_set_hue(handle, val);
} else if (attribute_id == ColorControl::Attributes::CurrentSaturation::Id) {
err = app_driver_light_set_saturation(handle, val);
} else if (attribute_id == ColorControl::Attributes::ColorTemperatureMireds::Id) {
err = app_driver_light_set_temperature(handle, val);
}
}
}
return err;
}
esp_err_t app_driver_light_set_defaults(uint16_t endpoint_id)
{
esp_err_t err = ESP_OK;
void *priv_data = endpoint::get_priv_data(endpoint_id);
led_driver_handle_t handle = (led_driver_handle_t)priv_data;
node_t *node = node::get();
endpoint_t *endpoint = endpoint::get(node, endpoint_id);
cluster_t *cluster = NULL;
attribute_t *attribute = NULL;
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
/* Setting brightness */
cluster = cluster::get(endpoint, LevelControl::Id);
attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id);
attribute::get_val(attribute, &val);
err |= app_driver_light_set_brightness(handle, &val);
/* Setting color */
cluster = cluster::get(endpoint, ColorControl::Id);
attribute = attribute::get(cluster, ColorControl::Attributes::ColorMode::Id);
attribute::get_val(attribute, &val);
if (val.val.u8 == EMBER_ZCL_COLOR_MODE_CURRENT_HUE_AND_CURRENT_SATURATION) {
/* Setting hue */
attribute = attribute::get(cluster, ColorControl::Attributes::CurrentHue::Id);
attribute::get_val(attribute, &val);
err |= app_driver_light_set_hue(handle, &val);
/* Setting saturation */
attribute = attribute::get(cluster, ColorControl::Attributes::CurrentSaturation::Id);
attribute::get_val(attribute, &val);
err |= app_driver_light_set_saturation(handle, &val);
} else if (val.val.u8 == EMBER_ZCL_COLOR_MODE_COLOR_TEMPERATURE) {
/* Setting temperature */
attribute = attribute::get(cluster, ColorControl::Attributes::ColorTemperatureMireds::Id);
attribute::get_val(attribute, &val);
err |= app_driver_light_set_temperature(handle, &val);
} else {
ESP_LOGE(TAG, "Color mode not supported");
}
/* Setting power */
cluster = cluster::get(endpoint, OnOff::Id);
attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id);
attribute::get_val(attribute, &val);
err |= app_driver_light_set_power(handle, &val);
return err;
}
app_driver_handle_t app_driver_light_init()
{
/* Initialize led */
led_driver_config_t config = led_driver_get_config();
led_driver_handle_t handle = led_driver_init(&config);
return (app_driver_handle_t)handle;
}
app_driver_handle_t app_driver_button_init()
{
/* Initialize button */
button_config_t config = button_driver_get_config();
button_handle_t handle = iot_button_create(&config);
iot_button_register_cb(handle, BUTTON_PRESS_DOWN, app_driver_button_toggle_cb, NULL);
#if CONFIG_ENABLE_CHIP_SHELL
app_driver_register_commands();
#endif // CONFIG_ENABLE_CHIP_SHELL
client::set_command_callback(app_driver_client_command_callback, app_driver_client_group_command_callback, NULL);
return (app_driver_handle_t)handle;
}
@@ -0,0 +1,166 @@
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <esp_log.h>
#include <stdlib.h>
#include <string.h>
#include <led_driver.h>
#include <esp_matter.h>
#include "esp_mac.h"
#include "app_bridged_device.h"
#include "espnow_bridge.h"
#include "espnow.h"
#include "espnow_ctrl.h"
#include "app_espnow.h"
using namespace chip::app::Clusters;
using namespace esp_matter;
static const char *TAG = "app_espnow";
static bool light_status = false;
extern uint16_t light_endpoint_id;
extern uint16_t aggregator_endpoint_id;
static void espnow_ctrl_onoff(espnow_addr_t src_addr, bool status)
{
ESP_LOGI(TAG, "ESP-NOW button pressed");
// Update bound light
client::command_handle_t cmd_handle;
cmd_handle.cluster_id = OnOff::Id;
if (status) {
cmd_handle.command_id = OnOff::Commands::On::Id;
} else {
cmd_handle.command_id = OnOff::Commands::Off::Id;
}
cmd_handle.is_group = false;
uint16_t bridged_switch_endpoint_id = app_bridge_get_matter_endpointid_by_espnow_macaddr(src_addr);
ESP_LOGI(TAG, "Using bridge endpoint: %d", bridged_switch_endpoint_id);
if (bridged_switch_endpoint_id != chip::kInvalidEndpointId) {
lock::chip_stack_lock(portMAX_DELAY);
client::cluster_update(bridged_switch_endpoint_id, &cmd_handle);
lock::chip_stack_unlock();
} else {
ESP_LOGE(TAG, "Can't find endpoint for bridged device: " MACSTR, MAC2STR(src_addr));
}
// Update local light
uint16_t endpoint_id = light_endpoint_id;
uint32_t cluster_id = OnOff::Id;
uint32_t attribute_id = OnOff::Attributes::OnOff::Id;
node_t *node = node::get();
endpoint_t *endpoint = endpoint::get(node, endpoint_id);
cluster_t *cluster = cluster::get(endpoint, cluster_id);
attribute_t *attribute = attribute::get(cluster, attribute_id);
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);
if (val.type == ESP_MATTER_VAL_TYPE_BOOLEAN) {
val.val.b = status;
attribute::update(endpoint_id, cluster_id, attribute_id, &val);
}
}
static void espnow_ctrl_responder_raw_data_cb(espnow_addr_t src_addr, espnow_ctrl_data_t *data, wifi_pkt_rx_ctrl_t *rx_ctrl)
{
ESP_LOGI(TAG, "espnow_ctrl_responder_raw_data_cb, from initiator: " MACSTR
", initiator_attribute: %d, responder_attribute: %d, value: %d",
MAC2STR(src_addr),
data->initiator_attribute,
data->responder_attribute,
data->responder_value_i);
light_status = !light_status;
ESP_LOGI(TAG, "Toggle Status to %d", light_status);
espnow_ctrl_onoff(src_addr, light_status);
}
static void espnow_ctrl_responder_data_cb(espnow_attribute_t initiator_attribute,
espnow_attribute_t responder_attribute,
uint32_t status)
{
ESP_LOGI(TAG, "espnow_ctrl_responder_recv, initiator_attribute: %d, responder_attribute: %d, value: %d",
initiator_attribute, responder_attribute, status);
}
static void espnow_event_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
if (base != ESP_EVENT_ESPNOW) {
return;
}
// Update local light
uint16_t endpoint_id = light_endpoint_id;
uint32_t cluster_id = OnOff::Id;
uint32_t attribute_id = OnOff::Attributes::OnOff::Id;
node_t *node = node::get();
endpoint_t *endpoint = endpoint::get(node, endpoint_id);
cluster_t *cluster = cluster::get(endpoint, cluster_id);
attribute_t *attribute = attribute::get(cluster, attribute_id);
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
switch (id) {
case ESP_EVENT_ESPNOW_CTRL_BIND: {
espnow_ctrl_bind_info_t *info = (espnow_ctrl_bind_info_t *)event_data;
ESP_LOGI(TAG, "bind, uuid: " MACSTR ", initiator_type: %d", MAC2STR(info->mac), info->initiator_attribute);
ESP_LOGI(TAG, "Create bridged switch type: 0x%04x", ESP_MATTER_ON_OFF_SWITCH_DEVICE_TYPE_ID);
espnow_bridge_match_bridged_switch(info->mac, info->initiator_attribute, ESP_MATTER_ON_OFF_SWITCH_DEVICE_TYPE_ID);
attribute::get_val(attribute, &val);
val.val.b = !val.val.b;
attribute::update(endpoint_id, cluster_id, attribute_id, &val);
break;
}
case ESP_EVENT_ESPNOW_CTRL_UNBIND: {
espnow_ctrl_bind_info_t *info = (espnow_ctrl_bind_info_t *)event_data;
ESP_LOGI(TAG, "unbind, uuid: " MACSTR ", initiator_type: %d", MAC2STR(info->mac), info->initiator_attribute);
espnow_bridge_remove_bridged_switch(info->mac);
attribute::get_val(attribute, &val);
val.val.b = !val.val.b;
attribute::update(endpoint_id, cluster_id, attribute_id, &val);
break;
}
default:
break;
}
light_status = val.val.b;
}
void app_espnow_init()
{
// ESPNOW_INIT_CONFIG_DEFAULT()
espnow_config_t espnow_config = {
"ESP_NOW",
1,
0,
0,
0,
32,
10,
pdMS_TO_TICKS(3000),
{1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
esp_err_t ret = espnow_init(&espnow_config);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "espnow init success.");
} else {
ESP_LOGI(TAG, "espnow init fail.");
}
esp_event_handler_register(ESP_EVENT_ESPNOW, ESP_EVENT_ANY_ID, espnow_event_handler, NULL);
ESP_ERROR_CHECK(espnow_ctrl_responder_bind(30 * 1000, -55, NULL));
// Without registering this callback, it crashes when calling the callback espnow_ctrl_responder_raw_data_cb
espnow_ctrl_responder_data(espnow_ctrl_responder_data_cb);
espnow_ctrl_recv(espnow_ctrl_responder_raw_data_cb);
ESP_LOGI(TAG, "espnow init completed.");
}
@@ -0,0 +1,11 @@
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
void app_espnow_init();
@@ -0,0 +1,186 @@
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <esp_err.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <esp_wifi.h>
#include <esp_matter.h>
#include <esp_matter_console.h>
#include <esp_matter_ota.h>
#include <app_bridged_device.h>
#include <app_priv.h>
#include <app_espnow.h>
#include <app_reset.h>
static const char *TAG = "app_main";
uint16_t light_endpoint_id = 0;
uint16_t aggregator_endpoint_id = 0;
using namespace esp_matter;
using namespace esp_matter::attribute;
using namespace esp_matter::endpoint;
using namespace chip::app::Clusters;
static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg)
{
switch (event->Type) {
case chip::DeviceLayer::DeviceEventType::kInterfaceIpAddressChanged:
ESP_LOGI(TAG, "Interface IP Address changed");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningComplete:
ESP_LOGI(TAG, "Commissioning complete");
break;
case chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired:
ESP_LOGI(TAG, "Commissioning failed, fail safe timer expired");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStarted:
ESP_LOGI(TAG, "Commissioning session started");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStopped:
ESP_LOGI(TAG, "Commissioning session stopped");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowOpened:
ESP_LOGI(TAG, "Commissioning window opened");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowClosed:
ESP_LOGI(TAG, "Commissioning window closed");
break;
default:
break;
}
}
static esp_err_t app_identification_cb(identification::callback_type_t type, uint16_t endpoint_id, uint8_t effect_id,
uint8_t effect_variant, void *priv_data)
{
ESP_LOGI(TAG, "Identification callback: type: %d, effect: %d", type, effect_id);
return ESP_OK;
}
static esp_err_t app_attribute_update_cb(attribute::callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id,
uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data)
{
esp_err_t err = ESP_OK;
if (type == PRE_UPDATE) {
/* Driver update */
app_driver_handle_t driver_handle = (app_driver_handle_t)priv_data;
err = app_driver_attribute_update(driver_handle, endpoint_id, cluster_id, attribute_id, val);
}
return err;
}
esp_err_t wifi_is_provisioned(bool *provisioned)
{
if (!provisioned) {
return ESP_ERR_INVALID_ARG;
}
*provisioned = false;
/* Get Wi-Fi Station configuration */
wifi_config_t wifi_cfg;
if (esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) {
return ESP_FAIL;
}
if (strlen((const char *) wifi_cfg.sta.ssid)) {
*provisioned = true;
}
return ESP_OK;
}
extern "C" void app_main()
{
esp_err_t err = ESP_OK;
/* Initialize the ESP NVS layer */
nvs_flash_init();
/* Initialize driver */
app_driver_handle_t light_handle = app_driver_light_init();
app_driver_handle_t button_handle = app_driver_button_init();
app_reset_button_register(button_handle);
/* Create a Matter node and add the mandatory Root Node device type on endpoint 0 */
node::config_t node_config;
node_t *node = node::create(&node_config, app_attribute_update_cb, app_identification_cb);
/* This node handle can be used to create/add other endpoints and clusters. */
if (!node) {
ESP_LOGE(TAG, "Matter node creation failed");
}
color_temperature_light::config_t light_config;
light_config.on_off.on_off = DEFAULT_POWER;
light_config.on_off.lighting.start_up_on_off = nullptr;
light_config.level_control.current_level = DEFAULT_BRIGHTNESS;
light_config.level_control.lighting.start_up_current_level = DEFAULT_BRIGHTNESS;
light_config.color_control.color_mode = EMBER_ZCL_COLOR_MODE_COLOR_TEMPERATURE;
light_config.color_control.enhanced_color_mode = EMBER_ZCL_COLOR_MODE_COLOR_TEMPERATURE;
light_config.color_control.color_temperature.startup_color_temperature_mireds = nullptr;
endpoint_t *endpoint = color_temperature_light::create(node, &light_config, ENDPOINT_FLAG_NONE, light_handle);
if (!endpoint) {
ESP_LOGE(TAG, "Matter color temperature light endpoint creation failed");
}
endpoint_t *aggregator = endpoint::aggregator::create(node, ENDPOINT_FLAG_NONE, NULL);
if (!aggregator) {
ESP_LOGE(TAG, "Matter aggregator endpoint creation failed");
}
light_endpoint_id = endpoint::get_id(endpoint);
ESP_LOGI(TAG, "Light created with endpoint_id %d", light_endpoint_id);
aggregator_endpoint_id = endpoint::get_id(aggregator);
ESP_LOGI(TAG, "Switch created with endpoint id %d", aggregator_endpoint_id);
/* Add additional features to the node */
cluster_t *cluster = cluster::get(endpoint, ColorControl::Id);
cluster::color_control::feature::hue_saturation::config_t hue_saturation_config;
hue_saturation_config.current_hue = DEFAULT_HUE;
hue_saturation_config.current_saturation = DEFAULT_SATURATION;
cluster::color_control::feature::hue_saturation::add(cluster, &hue_saturation_config);
/* Matter start */
err = esp_matter::start(app_event_cb);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Matter start failed: %d", err);
}
err = app_bridge_initialize(node);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to resume the bridged endpoints: %d", err);
}
bool provisioned = false;
/* Let's find out if the device is provisioned previously */
ESP_ERROR_CHECK(wifi_is_provisioned(&provisioned));
if (provisioned) {
ESP_LOGI(TAG, "WiFi already provisioned previously, disable PS");
esp_wifi_set_ps(WIFI_PS_NONE);
}
app_espnow_init();
/* Starting driver with default values */
app_driver_light_set_defaults(light_endpoint_id);
#if CONFIG_ENABLE_CHIP_SHELL
esp_matter::console::diagnostics_register_commands();
esp_matter::console::init();
#endif
}
@@ -0,0 +1,79 @@
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#include <esp_err.h>
#include <esp_matter.h>
/** Standard max values (used for remapping attributes) */
#define STANDARD_BRIGHTNESS 100
#define STANDARD_HUE 360
#define STANDARD_SATURATION 100
#define STANDARD_TEMPERATURE_FACTOR 1000000
/** Matter max values (used for remapping attributes) */
#define MATTER_BRIGHTNESS 254
#define MATTER_HUE 255
#define MATTER_SATURATION 255
#define MATTER_TEMPERATURE_FACTOR 1000000
/** Default attribute values used during initialization */
#define DEFAULT_POWER true
#define DEFAULT_BRIGHTNESS 64
#define DEFAULT_HUE 128
#define DEFAULT_SATURATION 255
typedef void *app_driver_handle_t;
/** Initialize the light driver
*
* This initializes the light driver associated with the selected board.
*
* @return Handle on success.
* @return NULL in case of failure.
*/
app_driver_handle_t app_driver_light_init();
/** Initialize the button driver
*
* This initializes the button driver associated with the selected board.
*
* @return Handle on success.
* @return NULL in case of failure.
*/
app_driver_handle_t app_driver_button_init();
/** Driver Update
*
* This API should be called to update the driver for the attribute being updated.
* This is usually called from the common `app_attribute_update_cb()`.
*
* @param[in] endpoint_id Endpoint ID of the attribute.
* @param[in] cluster_id Cluster ID of the attribute.
* @param[in] attribute_id Attribute ID of the attribute.
* @param[in] val Pointer to `esp_matter_attr_val_t`. Use appropriate elements as per the value type.
*
* @return ESP_OK on success.
* @return error in case of failure.
*/
esp_err_t app_driver_attribute_update(app_driver_handle_t driver_handle, uint16_t endpoint_id, uint32_t cluster_id,
uint32_t attribute_id, esp_matter_attr_val_t *val);
/** Set defaults for light driver
*
* Set the attribute drivers to their default values from the created data model.
*
* @param[in] endpoint_id Endpoint ID of the driver.
*
* @return ESP_OK on success.
* @return error in case of failure.
*/
esp_err_t app_driver_light_set_defaults(uint16_t endpoint_id);
// esp_err_t app_driver_light_onoff(bool status);
@@ -0,0 +1,62 @@
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <esp_err.h>
#include <esp_check.h>
#include <esp_log.h>
#include <esp_mac.h>
#include <esp_matter.h>
#include <esp_matter_core.h>
#include <esp_matter_bridge.h>
#include <espnow_bridge.h>
#include <app_bridged_device.h>
static const char *TAG = "espnow_bridge";
using namespace chip::app::Clusters;
using namespace esp_matter;
using namespace esp_matter::cluster;
extern uint16_t aggregator_endpoint_id;
esp_err_t espnow_bridge_match_bridged_switch(uint8_t espnow_macaddr[6], uint16_t espnow_initiator_attr, uint32_t matter_device_type_id)
{
ESP_LOGI(TAG, "espnow switch found: " MACSTR ", initiator type: %d", MAC2STR(espnow_macaddr), espnow_initiator_attr);
node_t *node = node::get();
ESP_RETURN_ON_FALSE(node, ESP_ERR_INVALID_STATE, TAG, "Could not find esp_matter node");
if (app_bridge_get_device_by_espnow_macaddr(espnow_macaddr)) {
ESP_LOGI(TAG, "Bridged node for " MACSTR " ESP-NOW device on endpoint %d has been created", MAC2STR(espnow_macaddr),
app_bridge_get_matter_endpointid_by_espnow_macaddr(espnow_macaddr));
} else {
app_bridged_device_t *bridged_device =
app_bridge_create_bridged_device(node, aggregator_endpoint_id, matter_device_type_id,
ESP_MATTER_BRIDGED_DEVICE_TYPE_ESPNOW,
app_bridge_espnow_address(espnow_macaddr, espnow_initiator_attr));
ESP_RETURN_ON_FALSE(bridged_device, ESP_FAIL, TAG, "Failed to create bridged device (espnow switch)");
ESP_LOGI(TAG, "Create/Update bridged node for " MACSTR " bridged device on endpoint %d", MAC2STR(espnow_macaddr),
app_bridge_get_matter_endpointid_by_espnow_macaddr(espnow_macaddr));
}
return ESP_OK;
}
esp_err_t espnow_bridge_remove_bridged_switch(uint8_t espnow_macaddr[6])
{
node_t *node = node::get();
ESP_RETURN_ON_FALSE(node, ESP_ERR_INVALID_STATE, TAG, "Could not find esp_matter node");
app_bridged_device_t *bridged_device = app_bridge_get_device_by_espnow_macaddr(espnow_macaddr);
if (bridged_device) {
app_bridge_remove_device(bridged_device);
ESP_LOGI(TAG, "Bridged ESP-NOW switch removed: " MACSTR, MAC2STR(espnow_macaddr));
} else {
ESP_LOGI(TAG, "Bridged ESP-NOW switch not found: " MACSTR, MAC2STR(espnow_macaddr));
}
return ESP_OK;
}
@@ -0,0 +1,34 @@
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#include <stdint.h>
#include <string.h>
#include <esp_log.h>
#include <esp_matter_attribute_utils.h>
/**
* @brief
*
* @param espnow_macaddr
*
* @return void
*/
esp_err_t espnow_bridge_match_bridged_switch(uint8_t espnow_macaddr[6], uint16_t espnow_initiator_attr, uint32_t matter_device_type_id);
/**
* @brief
*
* @param espnow_macaddr
*
* @return void
*/
esp_err_t espnow_bridge_remove_bridged_switch(uint8_t espnow_macaddr[6]);
@@ -0,0 +1,17 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp-now: "==2.2.0"
## Required IDF version
idf:
version: ">=4.1.0"
# # Put list of dependencies here
# # For components maintained by Espressif:
# component: "~1.0.0"
# # For 3rd party components:
# username/component: ">=1.0.0,<2.0.0"
# username2/component2:
# version: "~1.0.0"
# # For transient dependencies `public` flag can be set.
# # `public` flag doesn't have an effect dependencies of the `main` component.
# # All dependencies of `main` are public by default.
# public: true
@@ -0,0 +1,7 @@
/** Empty File
*
* This file is just present to prevent cmake warnings about:
* No source files found for SRC_DIRS entry '/Users/chirag/work/gitlab/esp-matter/examples/<name>/main/zap-generated'.
*
* We need to keep the path in SRC_DIRS to be compatible with the zap data model.
*/
@@ -0,0 +1,78 @@
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// THIS FILE IS GENERATED BY ZAP
// Prevent multiple inclusion
#pragma once
#include <lib/core/CHIPConfig.h>
#define GENERATED_ATTRIBUTES \
{}
#define GENERATED_CLUSTERS \
{}
#define GENERATED_ENDPOINT_TYPES \
{}
#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 0
// Largest attribute size is needed for various buffers
#define ATTRIBUTE_LARGEST (259)
static_assert(ATTRIBUTE_LARGEST <= CHIP_CONFIG_MAX_ATTRIBUTE_STORE_ELEMENT_SIZE, "ATTRIBUTE_LARGEST larger than expected");
// Total size of attribute storage
#define ATTRIBUTE_MAX_SIZE (0)
// Number of fixed endpoints
#define FIXED_ENDPOINT_COUNT (0)
#ifdef CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT
#undef CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT
#endif
#define CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT (16)
// Array of endpoints that are supported, the data inside
// the array is the endpoint number.
#define FIXED_ENDPOINT_ARRAY \
{0}
// Array of profile ids
#define FIXED_PROFILE_IDS \
{0}
// Array of device types
#define FIXED_DEVICE_TYPES \
{0}
// Array of device type offsets
#define FIXED_DEVICE_TYPE_OFFSETS \
{0}
// Array of device type lengths
#define FIXED_DEVICE_TYPE_LENGTHS \
{0}
// Array of endpoint types supported on each endpoint
#define FIXED_ENDPOINT_TYPES \
{0}
// Array of networks supported on each endpoint
#define FIXED_NETWORKS \
{0}
@@ -0,0 +1,10 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: Firmware partition offset needs to be 64K aligned, initial 36K (9 sectors) are reserved for bootloader and partition table
esp_secure_cert, 0x3F, ,0xd000, 0x2000, , # Never mark this as an encrypted partition
nvs, data, nvs, 0x10000, 0x6000,
nvs_keys, data, nvs_keys,, 0x1000,
otadata, data, ota, , 0x2000
phy_init, data, phy, , 0x1000,
ota_0, app, ota_0, 0x20000, 0x1E0000,
ota_1, app, ota_1, 0x200000, 0x1E0000,
fctry, data, nvs, 0x3E0000, 0x6000
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: Firmware partition offset needs to be 64K aligned, initial 36K (9 sectors) are reserved for bootloader and partition table
3 esp_secure_cert, 0x3F, ,0xd000, 0x2000, , # Never mark this as an encrypted partition
4 nvs, data, nvs, 0x10000, 0x6000,
5 nvs_keys, data, nvs_keys,, 0x1000,
6 otadata, data, ota, , 0x2000
7 phy_init, data, phy, , 0x1000,
8 ota_0, app, ota_0, 0x20000, 0x1E0000,
9 ota_1, app, ota_1, 0x200000, 0x1E0000,
10 fctry, data, nvs, 0x3E0000, 0x6000
@@ -0,0 +1,42 @@
# Default to 921600 baud when flashing and monitoring device
CONFIG_ESPTOOLPY_BAUD_921600B=y
CONFIG_ESPTOOLPY_BAUD=921600
CONFIG_ESPTOOLPY_COMPRESSED=y
CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y
CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
#enable BT
CONFIG_BT_ENABLED=y
CONFIG_BT_NIMBLE_ENABLED=y
#enable lwip ipv6 autoconfig
CONFIG_LWIP_IPV6_AUTOCONFIG=y
# Use a custom partition table
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_OFFSET=0xC000
# Enable chip shell
CONFIG_ENABLE_CHIP_SHELL=y
#enable lwIP route hooks
CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y
CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y
# Button
CONFIG_BUTTON_PERIOD_TIME_MS=20
CONFIG_BUTTON_LONG_PRESS_TIME_MS=5000
# disable softap by default
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n
# Disable DS Peripheral
CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL=n
# ESP-NOW Control Configuration
CONFIG_ESPNOW_CONTROL_AUTO_CHANNEL_SENDING=y
CONFIG_ESPNOW_CONTROL_RETRANSMISSION_TIMES=5
CONFIG_ESPNOW_CONTROL_FORWARD_TTL=10
CONFIG_ESPNOW_CONTROL_FORWARD_RSSI=-55