mirror of
https://github.com/espressif/esp-matter.git
synced 2026-04-27 19:13:13 +00:00
example/bridges: Add rainmaker bridge example
This commit is contained in:
@@ -134,3 +134,9 @@ examples/sensors:
|
||||
- if: IDF_TARGET in ["esp32c3"]
|
||||
temporary: true
|
||||
reason: the other targets are not tested yet
|
||||
|
||||
examples/bridge_apps/esp_rainmaker_bridge:
|
||||
enable:
|
||||
- if: IDF_TARGET in ["esp32s3"]
|
||||
temporary: true
|
||||
reason: the other targets are not tested yet
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# 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})
|
||||
|
||||
set(PROJECT_VER "1.0")
|
||||
set(PROJECT_VER_NUMBER 1)
|
||||
|
||||
set(ESP_MATTER_PATH $ENV{ESP_MATTER_PATH})
|
||||
set(MATTER_SDK_PATH ${ESP_MATTER_PATH}/connectedhomeip/connectedhomeip)
|
||||
|
||||
# This should be done before using the IDF_TARGET variable.
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
"${ESP_MATTER_PATH}/examples/common"
|
||||
"${MATTER_SDK_PATH}/config/esp32/components"
|
||||
"${ESP_MATTER_PATH}/components"
|
||||
${extra_components_dirs_append}
|
||||
$ENV{IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
"./example_components/rainmaker_api"
|
||||
"./example_components/app_network")
|
||||
|
||||
project(rainmaker_bridge)
|
||||
|
||||
idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++17;-Os;-DCHIP_HAVE_CONFIG_H;-fpermissive;-Wno-overloaded-virtual" 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;-Wno-error=cpp" APPEND)
|
||||
@@ -0,0 +1,115 @@
|
||||
# Rainmaker Bridge
|
||||
|
||||
This example demonstrates a Matter-Rainmaker Bridge that bridges Rainmaker devices to Matter fabric.
|
||||
|
||||
The Matter Bridge device is running on ESP32-S3.
|
||||
|
||||
See the [docs](https://docs.espressif.com/projects/esp-matter/en/latest/esp32/developing.html) for more information about building and flashing the firmware.
|
||||
|
||||
💡 Important: `create_bridge_devices` callback can be used to add data model elements (e.g., attributes, commands, etc.) to the bridge endpoint.
|
||||
|
||||
## 1. Additional Environment Setup
|
||||
|
||||
### 1.1 Hardware connection
|
||||
|
||||
This example run on ESP32S3 devkit by default.
|
||||
|
||||
### 1.2 Build and flash the Bridge (ESP32-S3)
|
||||
|
||||
For Standalone DevKit boards:
|
||||
|
||||
```
|
||||
cd ${ESP_MATTER_PATH}/examples/bridge_apps/esp_rainmaker_bridge
|
||||
idf.py set-target esp32s3
|
||||
idf.py -p <port> build flash
|
||||
```
|
||||
|
||||
## 2. Commissioning Setup
|
||||
|
||||
### 2.1 Use Rainmaker App pairing Bridge
|
||||
|
||||
Use Rainmaker App scan the qrcode print in device log to pairing the bridge
|
||||
|
||||
```
|
||||
I (16104) NimBLE: GAP procedure initiated: advertise;
|
||||
I (16104) NimBLE: disc_mode=2
|
||||
I (16114) NimBLE: adv_channel_map=0 own_addr_type=0 adv_filter_policy=0 adv_itvl_min=256 adv_itvl_max=256
|
||||
I (16124) NimBLE:
|
||||
|
||||
I (16104) network_prov_mgr: Provisioning started with service name : PROV_54a900
|
||||
I (16134) app_wifi: Provisioning started
|
||||
I (16134) app_network: Scan this QR code from the ESP RainMaker phone app for Provisioning.
|
||||
I (16144) QRCODE: Encoding below text with ECC LVL 0 & QR Code Version 5
|
||||
I (16144) QRCODE: {"ver":"v1","name":"PROV_54a900","pop":"47d4fb71","transport":"ble"}
|
||||
|
||||
█▀▀▀▀▀█ █ ▄▄█ ▀▀ ▄█ ▀▄ █▀▀▀▀▀█
|
||||
█ ███ █ ██▄ █▄ ▄▀▀▄ ▀▀▄██ █ ███ █
|
||||
█ ▀▀▀ █ ▄██ ▀▀█▀█▄▀▄ ▀▀▀▀ █ ▀▀▀ █
|
||||
▀▀▀▀▀▀▀ ▀ █▄▀ ▀ ▀▄▀▄█▄▀▄█ ▀▀▀▀▀▀▀
|
||||
██ ▄█▀▀▄▄ ▀ ▀ ▀█▄▀▄▀▄▀▄▄▄▄ █ ▀█▀▀
|
||||
▄▀█▀█▀▀ █▀ ▄▀ ██▄ ▄██▀██▀▄██▀█
|
||||
▄█▀█▄▀▀▀▄ ▄▀▄▄█▄█▀▀▀▄▀▄▀ ▄▀▄▀ ▄▄▀
|
||||
▀█ ▀▄▀▄█▀██▀▀ ▀▄▄█▄ ██▀▄█ ▀ █▀▀▄
|
||||
▀█▀▄█▀▀██▀ ▀▄▄▄█▀▀▀█▀ █▀▄▀▀ ▀
|
||||
▄█▄▀▀▀▀ ▄██ ▄▀ ▀▀█▄▄ ▄█▀█ ▄█▄█▀▀▄
|
||||
███ █▀▀█▀ ▀▄ ▄█▄▀█ ▄█ ▀▀▀▀ ▀ ▄█▀
|
||||
█▀▀ ▀▄▄█▀ ▀█▄▄ ▄ █▄▀ █▀▀▀▀▄▄
|
||||
▀▀▀▀ ▀▀▀█ █ ▀▄▄▄▄██▀█▀ ▄█▀▀▀██▄▀
|
||||
█▀▀▀▀▀█ ▄▀▀ ▄ █▀██▀ ▄█▀█ ▀ ██▀▀
|
||||
█ ███ █ ▀▀▄▀█▄█▄█▀█▀ ▀█ ███▀█▀▄▄█
|
||||
█ ▀▀▀ █ ▄▄███▀ █▀▄ ▄███▀ ▀█▀▄ ▀
|
||||
▀▀▀▀▀▀▀ ▀ ▀▀ ▀▀ ▀▀▀▀▀▀ ▀
|
||||
|
||||
|
||||
I (16344) app_network: If QR code is not visible, copy paste the below URL in a browser.
|
||||
https://rainmaker.espressif.com/qrcode.html?data={"ver":"v1","name":"PROV_54a900","pop":"47d4fb71","transport":"ble"}
|
||||
I (16364) app_network: Provisioning will auto stop after 30 minute(s).
|
||||
|
||||
```
|
||||
|
||||
### 2.2 Use Rainmaker App pairing rainmaker end device
|
||||
|
||||
Follow this [guide](https://github.com/espressif/esp-rainmaker/blob/master/README.md) to setup Rainmaker device
|
||||
|
||||
### 2.3 Use chip-tool pairing bridge through onnetwork method
|
||||
|
||||
Use below command to pairing bridge
|
||||
|
||||
```
|
||||
./chip-tool pairing onnetwork 0x1234 20202021
|
||||
```
|
||||
|
||||
### 2.4 Control the bulb with chip-tool
|
||||
|
||||
Now you can control the Rainmaker device using the chip tool.
|
||||
|
||||
```
|
||||
./chip-tool onoff toggle 0x1234 0x2
|
||||
```
|
||||
|
||||
## 3. Device Performance
|
||||
|
||||
### 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 is rebooted.
|
||||
- `After Adding a Bridged device` == A Rainmaker Color Light is added
|
||||
on the Bridge.
|
||||
- device used: ESP32-S3-DevKitC-1
|
||||
- tested on:
|
||||
[d0faa92c](https://github.com/espressif/esp-matter/commit/d0faa92c9336205de21a4b325c956893736c4d64)
|
||||
(2025-09-29)
|
||||
- IDF: v5.4.1 [4c2820d3](https://github.com/espressif/esp-idf/tree/v5.4.1)
|
||||
|
||||
| | Bootup | After Rainmaker Commissioning | After Matter Commissioning | After Adding a Bridged device |
|
||||
|:- |:-: |:-: |:-: |:-: |
|
||||
|**Free Internal Memory** |162KB |118KB |118KB |106KB |
|
||||
|
||||
**Flash Usage**: Firmware binary size: 1.65MB
|
||||
|
||||
This should give you a good idea about the amount of free memory that is
|
||||
available for you to run your application's code.
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
set(priv_req qrcode nvs_flash esp_event rmaker_common vfs network_provisioning openthread)
|
||||
|
||||
idf_component_register(SRCS "app_wifi_internal.c" "app_network.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_INCLUDE_DIRS "private_include"
|
||||
PRIV_REQUIRES ${priv_req})
|
||||
|
||||
if(CONFIG_APP_WIFI_SHOW_DEMO_INTRO_TEXT)
|
||||
target_compile_definitions(${COMPONENT_TARGET} PRIVATE "-D RMAKER_DEMO_PROJECT_NAME=\"${CMAKE_PROJECT_NAME}\"")
|
||||
endif()
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
menu "ESP RainMaker App Wi-Fi Provisioning"
|
||||
|
||||
config APP_NETWORK_PROV_SHOW_QR
|
||||
bool "Show provisioning QR code"
|
||||
default y
|
||||
help
|
||||
Show the QR code for provisioning.
|
||||
|
||||
config APP_NETWORK_PROV_MAX_POP_MISMATCH
|
||||
int
|
||||
default 5
|
||||
range 0 20
|
||||
prompt "Max wrong pop attempts allowed"
|
||||
help
|
||||
Set the maximum wrong pop attempts allowed before stopping provisioning.
|
||||
Set 0 for the feature to be disabled.
|
||||
This safeguards the device from brute-force attempt by limiting the wrong pop allowed.
|
||||
|
||||
choice APP_NETWORK_PROV_TRANSPORT
|
||||
bool "Provisioning Transport method"
|
||||
default APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
help
|
||||
Wi-Fi/Network provisioning component offers both, SoftAP and BLE transports. Choose any one.
|
||||
|
||||
config APP_NETWORK_PROV_TRANSPORT_SOFTAP
|
||||
bool "Soft AP"
|
||||
depends on !IDF_TARGET_ESP32H2
|
||||
config APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
bool "BLE"
|
||||
select BT_ENABLED
|
||||
depends on !IDF_TARGET_ESP32S2
|
||||
endchoice
|
||||
|
||||
config APP_NETWORK_PROV_TRANSPORT
|
||||
int
|
||||
default 1 if APP_NETWORK_PROV_TRANSPORT_SOFTAP
|
||||
default 2 if APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
|
||||
config APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
bool
|
||||
default y
|
||||
prompt "Reset provisioned credentials and state machine after session failure"
|
||||
help
|
||||
Enable reseting provisioned credentials and state machine after session failure.
|
||||
This will restart the provisioning service after retries are exhausted.
|
||||
|
||||
config APP_NETWORK_PROV_MAX_RETRY_CNT
|
||||
int
|
||||
default 5
|
||||
prompt "Max retries before reseting provisioning state machine"
|
||||
depends on APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
help
|
||||
Set the Maximum retry to avoid reconnecting to an inexistent network or if credentials
|
||||
are misconfigured. Provisioned credentials are erased and internal state machine
|
||||
is reset after this threshold is reached.
|
||||
|
||||
config APP_NETWORK_SHOW_DEMO_INTRO_TEXT
|
||||
bool "Show intro text for demos"
|
||||
default n
|
||||
help
|
||||
Show some intro text for demos in order to help users understand more about ESP RainMaker.
|
||||
|
||||
config APP_NETWORK_PROV_TIMEOUT_PERIOD
|
||||
int "Provisioning Timeout"
|
||||
default 30
|
||||
help
|
||||
Timeout (in minutes) after which the provisioning will auto stop. A reboot will be required
|
||||
to restart provisioning. It is always recommended to set this to some non zero value, especially
|
||||
if you are not using PoP. Set to 0 if you do not want provisioning to auto stop.
|
||||
|
||||
config APP_NETWORK_PROV_NAME_PREFIX
|
||||
string "Provisioning Name Prefix"
|
||||
default "PROV"
|
||||
help
|
||||
Provisioning Name Prefix.
|
||||
|
||||
config APP_WIFI_PROV_COMPAT
|
||||
bool "Stay compatible with App Wi-Fi component"
|
||||
depends on ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
default y
|
||||
help
|
||||
Stay compatible with Previous App Wi-Fi component
|
||||
|
||||
endmenu
|
||||
+435
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
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 <sdkconfig.h>
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <app_network.h>
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
#include <app_wifi_internal.h>
|
||||
#include <esp_netif_types.h>
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
|
||||
#include <esp_mac.h>
|
||||
|
||||
#include <network_provisioning/manager.h>
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
#include <network_provisioning/scheme_ble.h>
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
#include <network_provisioning/scheme_softap.h>
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_SHOW_QR
|
||||
#include <qrcode.h>
|
||||
#endif
|
||||
|
||||
#include <nvs.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <esp_timer.h>
|
||||
#include <app_network.h>
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(APP_NETWORK_EVENT);
|
||||
static const char *TAG = "app_network";
|
||||
static const int NETWORK_CONNECTED_EVENT = BIT0;
|
||||
static EventGroupHandle_t network_event_group;
|
||||
|
||||
#define PROV_QR_VERSION "v1"
|
||||
|
||||
#define PROV_TRANSPORT_SOFTAP "softap"
|
||||
#define PROV_TRANSPORT_BLE "ble"
|
||||
#define QRCODE_BASE_URL "https://rainmaker.espressif.com/qrcode.html"
|
||||
|
||||
#define CREDENTIALS_NAMESPACE "rmaker_creds"
|
||||
#define RANDOM_NVS_KEY "random"
|
||||
|
||||
#define POP_STR_SIZE 9
|
||||
static esp_timer_handle_t prov_stop_timer;
|
||||
/* Timeout period in minutes */
|
||||
#define APP_NETWORK_PROV_TIMEOUT_PERIOD CONFIG_APP_NETWORK_PROV_TIMEOUT_PERIOD
|
||||
/* Autofetch period in micro-seconds */
|
||||
static uint64_t prov_timeout_period = (APP_NETWORK_PROV_TIMEOUT_PERIOD * 60 * 1000000LL);
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 3)
|
||||
#define APP_PROV_STOP_ON_CREDS_MISMATCH
|
||||
#elif (CONFIG_APP_NETWORK_PROV_MAX_RETRY_CNT > 0)
|
||||
#warning "Provisioning window stop on max credentials failures, needs IDF version >= 5.1.3"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_APP_NETWORK_SHOW_DEMO_INTRO_TEXT
|
||||
|
||||
#define ESP_RAINMAKER_GITHUB_EXAMPLES_PATH "https://github.com/espressif/esp-rainmaker/blob/master/examples"
|
||||
#define ESP_RAINMAKER_INTRO_LINK "https://rainmaker.espressif.com"
|
||||
#define ESP_RMAKER_PHONE_APP_LINK "http://bit.ly/esp-rmaker"
|
||||
char esp_rainmaker_ascii_art[] = \
|
||||
" ______ _____ _____ _____ _____ _ _ __ __ _ ________ _____\n"\
|
||||
" | ____|/ ____| __ \\ | __ \\ /\\ |_ _| \\ | | \\/ | /\\ | |/ / ____| __ \\\n"\
|
||||
" | |__ | (___ | |__) | | |__) | / \\ | | | \\| | \\ / | / \\ | ' /| |__ | |__) |\n"\
|
||||
" | __| \\___ \\| ___/ | _ / / /\\ \\ | | | . ` | |\\/| | / /\\ \\ | < | __| | _ /\n"\
|
||||
" | |____ ____) | | | | \\ \\ / ____ \\ _| |_| |\\ | | | |/ ____ \\| . \\| |____| | \\ \\\n"\
|
||||
" |______|_____/|_| |_| \\_\\/_/ \\_\\_____|_| \\_|_| |_/_/ \\_\\_|\\_\\______|_| \\_\\\n";
|
||||
|
||||
static void intro_print(bool provisioned)
|
||||
{
|
||||
printf("####################################################################################################\n");
|
||||
printf("%s\n", esp_rainmaker_ascii_art);
|
||||
printf("Welcome to ESP RainMaker %s demo application!\n", RMAKER_DEMO_PROJECT_NAME);
|
||||
if (!provisioned) {
|
||||
printf("Follow these steps to get started:\n");
|
||||
printf("1. Download the ESP RainMaker phone app by visiting this link from your phone's browser:\n\n");
|
||||
printf(" %s\n\n", ESP_RMAKER_PHONE_APP_LINK);
|
||||
printf("2. Sign up and follow the steps on screen to add the device to your Wi-Fi/Thread network.\n");
|
||||
printf("3. You are now ready to use the device and control it locally as well as remotely.\n");
|
||||
printf(" You can also use the Boot button on the board to control your device.\n");
|
||||
}
|
||||
printf("\nIf you want to reset network credentials, or reset to factory, press and hold the Boot button.\n");
|
||||
printf("\nThis application uses ESP RainMaker, which is based on ESP IDF.\n");
|
||||
printf("Check out the source code for this application here:\n %s/%s\n",
|
||||
ESP_RAINMAKER_GITHUB_EXAMPLES_PATH, RMAKER_DEMO_PROJECT_NAME);
|
||||
printf("\nPlease visit %s for additional information.\n\n", ESP_RAINMAKER_INTRO_LINK);
|
||||
printf("####################################################################################################\n");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void intro_print(bool provisioned)
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
#endif /* !APP_NETWORK_SHOW_DEMO_INTRO_TEXT */
|
||||
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_SHOW_QR
|
||||
static esp_err_t qrcode_display(const char *text)
|
||||
{
|
||||
#define MAX_QRCODE_VERSION 5
|
||||
esp_qrcode_config_t cfg = ESP_QRCODE_CONFIG_DEFAULT();
|
||||
cfg.max_qrcode_version = MAX_QRCODE_VERSION;
|
||||
return esp_qrcode_generate(&cfg, text);
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint8_t *custom_mfg_data = NULL;
|
||||
static size_t custom_mfg_data_len = 0;
|
||||
|
||||
esp_err_t app_network_set_custom_mfg_data(uint16_t device_type, uint8_t device_subtype)
|
||||
{
|
||||
int8_t mfg_data[] = {MFG_DATA_HEADER, MGF_DATA_APP_ID, MFG_DATA_VERSION, MFG_DATA_CUSTOMER_ID};
|
||||
size_t mfg_data_len = sizeof(mfg_data) + 4; // 4 bytes of device type, subtype, and extra-code
|
||||
custom_mfg_data = (uint8_t *)MEM_ALLOC_EXTRAM(mfg_data_len);
|
||||
if (custom_mfg_data == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory to custom mfg data");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(custom_mfg_data, mfg_data, sizeof(mfg_data));
|
||||
custom_mfg_data[8] = 0xff & (device_type >> 8);
|
||||
custom_mfg_data[9] = 0xff & device_type;
|
||||
custom_mfg_data[10] = device_subtype;
|
||||
custom_mfg_data[11] = 0;
|
||||
custom_mfg_data_len = mfg_data_len;
|
||||
ESP_LOG_BUFFER_HEXDUMP("tag", custom_mfg_data, mfg_data_len, 3);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void app_network_print_qr(const char *name, const char *pop, const char *transport)
|
||||
{
|
||||
if (!name || !transport) {
|
||||
ESP_LOGW(TAG, "Cannot generate QR code payload. Data missing.");
|
||||
return;
|
||||
}
|
||||
char payload[150];
|
||||
if (pop) {
|
||||
snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \
|
||||
",\"pop\":\"%s\",\"transport\":\"%s\"}",
|
||||
PROV_QR_VERSION, name, pop, transport);
|
||||
} else {
|
||||
snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \
|
||||
",\"transport\":\"%s\"}",
|
||||
PROV_QR_VERSION, name, transport);
|
||||
}
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_SHOW_QR
|
||||
ESP_LOGI(TAG, "Scan this QR code from the ESP RainMaker phone app for Provisioning.");
|
||||
qrcode_display(payload);
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_SHOW_QR */
|
||||
ESP_LOGI(TAG, "If QR code is not visible, copy paste the below URL in a browser.\n%s?data=%s", QRCODE_BASE_URL, payload);
|
||||
esp_event_post(APP_NETWORK_EVENT, APP_NETWORK_EVENT_QR_DISPLAY, payload, strlen(payload) + 1, portMAX_DELAY);
|
||||
}
|
||||
|
||||
/* Free random_bytes after use only if function returns ESP_OK */
|
||||
static esp_err_t read_random_bytes_from_nvs(uint8_t **random_bytes, size_t *len)
|
||||
{
|
||||
nvs_handle handle;
|
||||
esp_err_t err;
|
||||
*len = 0;
|
||||
|
||||
if ((err = nvs_open_from_partition(CONFIG_ESP_RMAKER_FACTORY_PARTITION_NAME, CREDENTIALS_NAMESPACE,
|
||||
NVS_READONLY, &handle)) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "NVS open for %s %s %s failed with error %d", CONFIG_ESP_RMAKER_FACTORY_PARTITION_NAME, CREDENTIALS_NAMESPACE, RANDOM_NVS_KEY, err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if ((err = nvs_get_blob(handle, RANDOM_NVS_KEY, NULL, len)) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Error %d. Failed to read key %s.", err, RANDOM_NVS_KEY);
|
||||
nvs_close(handle);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
*random_bytes = calloc(*len, 1);
|
||||
if (*random_bytes) {
|
||||
nvs_get_blob(handle, RANDOM_NVS_KEY, *random_bytes, len);
|
||||
nvs_close(handle);
|
||||
return ESP_OK;
|
||||
}
|
||||
nvs_close(handle);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
static char *custom_pop;
|
||||
esp_err_t app_network_set_custom_pop(const char *pop)
|
||||
{
|
||||
/* NULL PoP is not allowed here. Use POP_TYPE_NONE instead. */
|
||||
if (!pop) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Freeing up the PoP in case it is already allocated */
|
||||
if (custom_pop) {
|
||||
free(custom_pop);
|
||||
custom_pop = NULL;
|
||||
}
|
||||
|
||||
custom_pop = strdup(pop);
|
||||
if (!custom_pop) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t get_device_service_name(char *service_name, size_t max)
|
||||
{
|
||||
uint8_t *nvs_random = NULL;
|
||||
const char *ssid_prefix = CONFIG_APP_NETWORK_PROV_NAME_PREFIX;
|
||||
size_t nvs_random_size = 0;
|
||||
if ((read_random_bytes_from_nvs(&nvs_random, &nvs_random_size) != ESP_OK) || nvs_random_size < 3) {
|
||||
uint8_t mac_addr[6];
|
||||
esp_read_mac(mac_addr, ESP_MAC_BASE);
|
||||
snprintf(service_name, max, "%s_%02x%02x%02x", ssid_prefix, mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
} else {
|
||||
snprintf(service_name, max, "%s_%02x%02x%02x", ssid_prefix, nvs_random[nvs_random_size - 3],
|
||||
nvs_random[nvs_random_size - 2], nvs_random[nvs_random_size - 1]);
|
||||
}
|
||||
if (nvs_random) {
|
||||
free(nvs_random);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *get_device_pop(app_network_pop_type_t pop_type)
|
||||
{
|
||||
if (pop_type == POP_TYPE_NONE) {
|
||||
return NULL;
|
||||
} else if (pop_type == POP_TYPE_CUSTOM) {
|
||||
if (!custom_pop) {
|
||||
ESP_LOGE(TAG, "Custom PoP not set. Please use app_wifi_set_custom_pop().");
|
||||
return NULL;
|
||||
}
|
||||
return strdup(custom_pop);
|
||||
}
|
||||
char *pop = calloc(1, POP_STR_SIZE);
|
||||
if (!pop) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for PoP.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pop_type == POP_TYPE_MAC) {
|
||||
uint8_t mac_addr[6];
|
||||
esp_err_t err = esp_read_mac(mac_addr, ESP_MAC_BASE);
|
||||
if (err == ESP_OK) {
|
||||
snprintf(pop, POP_STR_SIZE, "%02x%02x%02x%02x", mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
return pop;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to get MAC address to generate PoP.");
|
||||
goto pop_err;
|
||||
}
|
||||
} else if (pop_type == POP_TYPE_RANDOM) {
|
||||
uint8_t *nvs_random = NULL;
|
||||
size_t nvs_random_size = 0;
|
||||
if ((read_random_bytes_from_nvs(&nvs_random, &nvs_random_size) != ESP_OK) || nvs_random_size < 4) {
|
||||
ESP_LOGE(TAG, "Failed to read random bytes from NVS to generate PoP.");
|
||||
if (nvs_random) {
|
||||
free(nvs_random);
|
||||
}
|
||||
goto pop_err;
|
||||
} else {
|
||||
snprintf(pop, POP_STR_SIZE, "%02x%02x%02x%02x", nvs_random[0], nvs_random[1], nvs_random[2], nvs_random[3]);
|
||||
free(nvs_random);
|
||||
return pop;
|
||||
}
|
||||
}
|
||||
pop_err:
|
||||
free(pop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void network_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
|
||||
{
|
||||
|
||||
#ifdef APP_PROV_STOP_ON_CREDS_MISMATCH
|
||||
static int failed_cnt = 0;
|
||||
#endif
|
||||
#ifdef APP_PROV_STOP_ON_CREDS_MISMATCH
|
||||
if (event_base == PROTOCOMM_SECURITY_SESSION_EVENT) {
|
||||
switch (event_id) {
|
||||
case PROTOCOMM_SECURITY_SESSION_SETUP_OK:
|
||||
ESP_LOGI(TAG, "Secured session established!");
|
||||
break;
|
||||
case PROTOCOMM_SECURITY_SESSION_INVALID_SECURITY_PARAMS:
|
||||
/* fall-through */
|
||||
case PROTOCOMM_SECURITY_SESSION_CREDENTIALS_MISMATCH:
|
||||
ESP_LOGE(TAG, "Received incorrect PoP or invalid security params! event: %d", (int) event_id);
|
||||
if (CONFIG_APP_NETWORK_PROV_MAX_POP_MISMATCH &&
|
||||
(++failed_cnt >= CONFIG_APP_NETWORK_PROV_MAX_POP_MISMATCH)) {
|
||||
/* stop provisioning for security reasons */
|
||||
network_prov_mgr_stop_provisioning();
|
||||
ESP_LOGW(TAG, "Max PoP attempts reached! Provisioning disabled for security reasons. Please reboot device to restart provisioning");
|
||||
esp_event_post(APP_NETWORK_EVENT, APP_NETWORK_EVENT_PROV_CRED_MISMATCH, NULL, 0, portMAX_DELAY);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* APP_PROV_STOP_ON_CREDS_MISMATCH */
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
|
||||
ESP_LOGI(TAG, "Connected with IP Address:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
/* Signal main application to continue execution */
|
||||
xEventGroupSetBits(network_event_group, NETWORK_CONNECTED_EVENT);
|
||||
}
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
if (event_base == NETWORK_PROV_EVENT && event_id == NETWORK_PROV_END) {
|
||||
if (prov_stop_timer) {
|
||||
esp_timer_stop(prov_stop_timer);
|
||||
esp_timer_delete(prov_stop_timer);
|
||||
prov_stop_timer = NULL;
|
||||
}
|
||||
network_prov_mgr_deinit();
|
||||
}
|
||||
}
|
||||
|
||||
void app_network_init()
|
||||
{
|
||||
/* Initialize the event loop, if not done already. */
|
||||
esp_err_t err = esp_event_loop_create_default();
|
||||
/* If the default event loop is already initialized, we get ESP_ERR_INVALID_STATE */
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGW(TAG, "Event loop creation failed with ESP_ERR_INVALID_STATE. Proceeding since it must have been created elsewhere.");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to create default event loop, err = %x", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
ESP_ERROR_CHECK(wifi_init());
|
||||
#endif
|
||||
network_event_group = xEventGroupCreate();
|
||||
#ifdef APP_PROV_STOP_ON_CREDS_MISMATCH
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(PROTOCOMM_SECURITY_SESSION_EVENT, ESP_EVENT_ANY_ID, &network_event_handler, NULL));
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &network_event_handler, NULL));
|
||||
#endif
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_PROV_EVENT, NETWORK_PROV_END, &network_event_handler, NULL));
|
||||
}
|
||||
|
||||
static void app_network_prov_stop(void *priv)
|
||||
{
|
||||
ESP_LOGW(TAG, "Provisioning timed out. Please reboot device to restart provisioning.");
|
||||
network_prov_mgr_stop_provisioning();
|
||||
esp_event_post(APP_NETWORK_EVENT, APP_NETWORK_EVENT_PROV_TIMEOUT, NULL, 0, portMAX_DELAY);
|
||||
}
|
||||
|
||||
esp_err_t app_network_start_timer(void)
|
||||
{
|
||||
if (prov_timeout_period == 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_timer_create_args_t prov_stop_timer_conf = {
|
||||
.callback = app_network_prov_stop,
|
||||
.arg = NULL,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "app_wifi_prov_stop_tm"
|
||||
};
|
||||
if (esp_timer_create(&prov_stop_timer_conf, &prov_stop_timer) == ESP_OK) {
|
||||
esp_timer_start_once(prov_stop_timer, prov_timeout_period);
|
||||
ESP_LOGI(TAG, "Provisioning will auto stop after %d minute(s).",
|
||||
APP_NETWORK_PROV_TIMEOUT_PERIOD);
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to create Provisioning auto stop timer.");
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t app_network_start(app_network_pop_type_t pop_type)
|
||||
{
|
||||
/* Do we want a proof-of-possession (ignored if Security 0 is selected):
|
||||
* - this should be a string with length > 0
|
||||
* - NULL if not used
|
||||
*/
|
||||
char *pop = get_device_pop(pop_type);
|
||||
if ((pop_type != POP_TYPE_NONE) && (pop == NULL)) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
/* What is the Device Service Name that we want
|
||||
* This translates to :
|
||||
* - device name when scheme is network_prov_scheme_ble/wifi_prov_scheme_ble
|
||||
*/
|
||||
char service_name[12];
|
||||
get_device_service_name(service_name, sizeof(service_name));
|
||||
/* What is the service key (Wi-Fi password)
|
||||
* NULL = Open network
|
||||
* This is ignored when scheme is network_prov_scheme_ble/wifi_prov_scheme_ble
|
||||
*/
|
||||
const char *service_key = NULL;
|
||||
esp_err_t err = ESP_OK;
|
||||
bool provisioned = false;
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
err = wifi_start(pop, service_name, service_key, custom_mfg_data, custom_mfg_data_len, &provisioned);
|
||||
#endif
|
||||
if (err != ESP_OK) {
|
||||
free(pop);
|
||||
return err;
|
||||
}
|
||||
if (!provisioned) {
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
app_network_print_qr(service_name, pop, PROV_TRANSPORT_BLE);
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
app_network_print_qr(service_name, pop, PROV_TRANSPORT_SOFTAP);
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
app_network_start_timer();
|
||||
}
|
||||
free(pop);
|
||||
intro_print(provisioned);
|
||||
if (custom_mfg_data) {
|
||||
free(custom_mfg_data);
|
||||
custom_mfg_data = NULL;
|
||||
custom_mfg_data_len = 0;
|
||||
}
|
||||
/* Wait for Network connection */
|
||||
xEventGroupWaitBits(network_event_group, NETWORK_CONNECTED_EVENT, false, true, portMAX_DELAY);
|
||||
return err;
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
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_event.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MFG_DATA_HEADER 0xe5, 0x02
|
||||
#define MGF_DATA_APP_ID 'N', 'o', 'v'
|
||||
#define MFG_DATA_VERSION 'a'
|
||||
#define MFG_DATA_CUSTOMER_ID 0x00, 0x01
|
||||
|
||||
#define MGF_DATA_DEVICE_TYPE_LIGHT 0x0005
|
||||
#define MGF_DATA_DEVICE_TYPE_SWITCH 0x0080
|
||||
#define MGF_DATA_DEVICE_TYPE_MATTER_CONTROLLER 0xFFF1
|
||||
|
||||
#define MFG_DATA_DEVICE_SUBTYPE_SWITCH 0x01
|
||||
#define MFG_DATA_DEVICE_SUBTYPE_LIGHT 0x01
|
||||
#define MFG_DATA_DEVICE_SUBTYPE_MATTER_CONTROLLER 0x01
|
||||
|
||||
#define MFG_DATA_DEVICE_EXTRA_CODE 0x00
|
||||
|
||||
/** ESP RainMaker Event Base */
|
||||
ESP_EVENT_DECLARE_BASE(APP_NETWORK_EVENT);
|
||||
|
||||
/** App Network Events */
|
||||
typedef enum {
|
||||
/** QR code available for display. Associated data is the NULL terminated QR payload. */
|
||||
APP_NETWORK_EVENT_QR_DISPLAY = 1,
|
||||
/** Provisioning timed out */
|
||||
APP_NETWORK_EVENT_PROV_TIMEOUT,
|
||||
/** Provisioning has restarted due to failures (Invalid SSID/Passphrase) */
|
||||
APP_NETWORK_EVENT_PROV_RESTART,
|
||||
/** Provisioning closed due to invalid credentials */
|
||||
APP_NETWORK_EVENT_PROV_CRED_MISMATCH,
|
||||
} app_network_event_t;
|
||||
|
||||
/** Types of Proof of Possession */
|
||||
typedef enum {
|
||||
/** Use MAC address to generate PoP */
|
||||
POP_TYPE_MAC,
|
||||
/** Use random stream generated and stored in fctry partition during claiming process as PoP */
|
||||
POP_TYPE_RANDOM,
|
||||
/** Do not use any PoP.
|
||||
* Use this option with caution. Consider using `CONFIG_APP_NETWORK_PROV_TIMEOUT_PERIOD` with this.
|
||||
*/
|
||||
POP_TYPE_NONE,
|
||||
/** Use a custom PoP.
|
||||
* Set a custom PoP using app_network_set_custom_pop() first.
|
||||
*/
|
||||
POP_TYPE_CUSTOM
|
||||
} app_network_pop_type_t;
|
||||
|
||||
/** Initialize Wi-Fi/Thread
|
||||
*
|
||||
* This initializes Wi-Fi/Thread stack and the network provisioning manager
|
||||
*/
|
||||
void app_network_init();
|
||||
|
||||
/** Start Wi-Fi/Thread
|
||||
*
|
||||
* This will start provisioning if the node is not provisioned and will connect to any network
|
||||
* if node is provisioned. Function will return successfully only after network is connected
|
||||
*
|
||||
* @param[in] pop_type The type for Proof of Possession (PoP) pin
|
||||
*
|
||||
* @return ESP_OK on success (Network connected).
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t app_network_start(app_network_pop_type_t pop_type);
|
||||
|
||||
/** Set custom manufacturing data
|
||||
*
|
||||
* This can be used to add some custom manufacturing data in BLE advertisements during
|
||||
* provisioning. This can be used by apps to filter the scanned BLE devices and show
|
||||
* only the relevant one. Supported by Nova Home app for light and switch
|
||||
*
|
||||
* @param[in] device_type Type of the device, like light or switch
|
||||
* @param[in] device_subtype Sub Type of the device (application specific)
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t app_network_set_custom_mfg_data(uint16_t device_type, uint8_t device_subtype);
|
||||
|
||||
/** Set custom PoP
|
||||
*
|
||||
* This can be used to set a custom Proof of Possession (PoP) pin for provisioning.
|
||||
* Applicable only if POP_TYPE_CUSTOM is used for app_network_start().
|
||||
*
|
||||
* @param[in] pop A NULL terminated PoP string (typically 8 characters alphanumeric)
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t app_network_set_custom_pop(const char *pop);
|
||||
|
||||
#if CONFIG_APP_WIFI_PROV_COMPAT
|
||||
#define APP_WIFI_EVENT APP_NETWORK_EVENT
|
||||
typedef app_network_event_t app_wifi_event_t;
|
||||
#define APP_WIFI_EVENT_QR_DISPLAY APP_NETWORK_EVENT_QR_DISPLAY
|
||||
#define APP_WIFI_EVENT_PROV_TIMEOUT APP_NETWORK_EVENT_PROV_TIMEOUT
|
||||
#define APP_WIFI_EVENT_PROV_RESTART APP_NETWORK_EVENT_PROV_RESTART
|
||||
#define APP_WIFI_EVENT_PROV_CRED_MISMATCH APP_NETWORK_EVENT_PROV_CRED_MISMATCH
|
||||
typedef app_network_pop_type_t app_wifi_pop_type_t;
|
||||
#define app_wifi_init() app_network_init()
|
||||
#define app_wifi_start(pop_type) app_network_start(pop_type)
|
||||
#define app_wifi_set_custom_mfg_data(device_type, device_subtype) app_network_set_custom_mfg_data(device_type, device_subtype)
|
||||
#define app_wifi_set_custom_pop(pop) app_network_set_custom_pop(pop)
|
||||
#endif /* !CONFIG_APP_WIFI_PROV_COMPAT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
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 <sdkconfig.h>
|
||||
#include <app_network.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_APP_WIFI_PROV_COMPAT
|
||||
#define APP_WIFI_EVENT APP_NETWORK_EVENT
|
||||
typedef app_network_event_t app_wifi_event_t;
|
||||
#define APP_WIFI_EVENT_QR_DISPLAY APP_NETWORK_EVENT_QR_DISPLAY
|
||||
#define APP_WIFI_EVENT_PROV_TIMEOUT APP_NETWORK_EVENT_PROV_TIMEOUT
|
||||
#define APP_WIFI_EVENT_PROV_RESTART APP_NETWORK_EVENT_PROV_RESTART
|
||||
#define APP_WIFI_EVENT_PROV_CRED_MISMATCH APP_NETWORK_EVENT_PROV_CRED_MISMATCH
|
||||
typedef app_network_pop_type_t app_wifi_pop_type_t;
|
||||
#define app_wifi_init() app_network_init()
|
||||
#define app_wifi_start(pop_type) app_network_start(pop_type)
|
||||
#define app_wifi_set_custom_mfg_data(device_type, device_subtype) app_network_set_custom_mfg_data(device_type, device_subtype)
|
||||
#define app_wifi_set_custom_pop(pop) app_network_set_custom_pop(pop)
|
||||
#endif /* !CONFIG_APP_WIFI_PROV_COMPAT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
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 <sdkconfig.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include <esp_rmaker_utils.h>
|
||||
#include <app_network.h>
|
||||
#include <app_wifi_internal.h>
|
||||
#include <esp_netif.h>
|
||||
|
||||
#include <network_provisioning/manager.h>
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
#include <network_provisioning/scheme_ble.h>
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
#include <network_provisioning/scheme_softap.h>
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
|
||||
#include <app_wifi_internal.h>
|
||||
#include <app_network.h>
|
||||
|
||||
#define APP_PROV_STOP_ON_CREDS_MISMATCH
|
||||
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
static const char* TAG = "app_wifi";
|
||||
/* Event handler for catching system events */
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
#ifdef CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
static int retries = 0;
|
||||
#endif
|
||||
|
||||
if (event_base == NETWORK_PROV_EVENT) {
|
||||
switch (event_id) {
|
||||
case NETWORK_PROV_START:
|
||||
ESP_LOGI(TAG, "Provisioning started");
|
||||
break;
|
||||
case NETWORK_PROV_WIFI_CRED_RECV: {
|
||||
wifi_sta_config_t *wifi_sta_cfg = (wifi_sta_config_t *)event_data;
|
||||
ESP_LOGI(TAG, "Received Wi-Fi credentials"
|
||||
"\n\tSSID : %s\n\tPassword : %s",
|
||||
(const char *) wifi_sta_cfg->ssid,
|
||||
(const char *) wifi_sta_cfg->password);
|
||||
break;
|
||||
}
|
||||
case NETWORK_PROV_WIFI_CRED_FAIL: {
|
||||
network_prov_wifi_sta_fail_reason_t *reason = (network_prov_wifi_sta_fail_reason_t *)event_data;
|
||||
ESP_LOGE(TAG, "Provisioning failed!\n\tReason : %s"
|
||||
"\n\tPlease reset to factory and retry provisioning",
|
||||
(*reason == NETWORK_PROV_WIFI_STA_AUTH_ERROR) ?
|
||||
"Wi-Fi station authentication failed" : "Wi-Fi access-point not found");
|
||||
#ifdef CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
retries++;
|
||||
if (retries >= CONFIG_APP_NETWORK_PROV_MAX_RETRY_CNT) {
|
||||
ESP_LOGI(TAG, "Failed to connect with provisioned AP, resetting provisioned credentials");
|
||||
network_prov_mgr_reset_wifi_sm_state_on_failure();
|
||||
esp_event_post(APP_NETWORK_EVENT, APP_NETWORK_EVENT_PROV_RESTART, NULL, 0, portMAX_DELAY);
|
||||
retries = 0;
|
||||
}
|
||||
#endif // CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
break;
|
||||
}
|
||||
case NETWORK_PROV_WIFI_CRED_SUCCESS:
|
||||
ESP_LOGI(TAG, "Provisioning successful");
|
||||
#ifdef CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
retries = 0;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
ESP_LOGI(TAG, "Disconnected. Connecting to the AP again...");
|
||||
esp_wifi_connect();
|
||||
}
|
||||
}
|
||||
|
||||
static void wifi_init_sta()
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
}
|
||||
#endif // CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
|
||||
esp_err_t wifi_init(void)
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
/* Initialize TCP/IP */
|
||||
esp_netif_init();
|
||||
/* Register our event handler for Wi-Fi, IP and Provisioning related events */
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_PROV_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
|
||||
|
||||
/* Initialize Wi-Fi including netif with default config */
|
||||
esp_netif_create_default_wifi_sta();
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
return ESP_OK;
|
||||
#else
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
}
|
||||
|
||||
esp_err_t wifi_start(const char *pop, const char *service_name, const char *service_key, uint8_t *mfg_data,
|
||||
size_t mfg_data_len, bool *provisioned)
|
||||
{
|
||||
#ifdef CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI
|
||||
/* Configuration for the provisioning manager */
|
||||
network_prov_mgr_config_t config = {
|
||||
/* What is the Provisioning Scheme that we want ?
|
||||
* network_prov_scheme_softap or network_prov_scheme_ble */
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
.scheme = network_prov_scheme_ble,
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
.scheme = network_prov_scheme_softap,
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
|
||||
/* Any default scheme specific event handler that you would
|
||||
* like to choose. Since our example application requires
|
||||
* neither BT nor BLE, we can choose to release the associated
|
||||
* memory once provisioning is complete, or not needed
|
||||
* (in case when device is already provisioned). Choosing
|
||||
* appropriate scheme specific event handler allows the manager
|
||||
* to take care of this automatically. This can be set to
|
||||
* NETWORK_PROV_EVENT_HANDLER_NONE when using network_prov_scheme_softap*/
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
.scheme_event_handler = NETWORK_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM
|
||||
#else /* CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP */
|
||||
.scheme_event_handler = NETWORK_PROV_EVENT_HANDLER_NONE,
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
};
|
||||
|
||||
/* Initialize provisioning manager with the
|
||||
* configuration parameters set above */
|
||||
ESP_ERROR_CHECK(network_prov_mgr_init(config));
|
||||
/* Let's find out if the device is provisioned */
|
||||
network_prov_mgr_is_wifi_provisioned(provisioned);
|
||||
/* If device is not yet provisioned start provisioning service */
|
||||
if (!(*provisioned)) {
|
||||
ESP_LOGI(TAG, "Starting provisioning");
|
||||
#if CONFIG_ESP_WIFI_SOFTAP_SUPPORT
|
||||
esp_netif_create_default_wifi_ap();
|
||||
#endif
|
||||
/* What is the security level that we want (0 or 1):
|
||||
* - NETWORK_PROV_SECURITY_0/WIFI_PROV_SECURITY_0 is simply plain text communication.
|
||||
* - NETWORK_PROV_SECURITY_1/WIFI_PROV_SECURITY_1 is secure communication which consists of secure handshake
|
||||
* using X25519 key exchange and proof of possession (pop) and AES-CTR
|
||||
* for encryption/decryption of messages.
|
||||
*/
|
||||
network_prov_security_t security = NETWORK_PROV_SECURITY_1;
|
||||
|
||||
#ifdef CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
/* This step is only useful when scheme is wifi_prov_scheme_ble. This will
|
||||
* set a custom 128 bit UUID which will be included in the BLE advertisement
|
||||
* and will correspond to the primary GATT service that provides provisioning
|
||||
* endpoints as GATT characteristics. Each GATT characteristic will be
|
||||
* formed using the primary service UUID as base, with different auto assigned
|
||||
* 12th and 13th bytes (assume counting starts from 0th byte). The client side
|
||||
* applications must identify the endpoints by reading the User Characteristic
|
||||
* Description descriptor (0x2901) for each characteristic, which contains the
|
||||
* endpoint name of the characteristic */
|
||||
uint8_t custom_service_uuid[] = {
|
||||
/* This is a random uuid. This can be modified if you want to change the BLE uuid. */
|
||||
/* 12th and 13th bit will be replaced by internal bits. */
|
||||
0xb4, 0xdf, 0x5a, 0x1c, 0x3f, 0x6b, 0xf4, 0xbf,
|
||||
0xea, 0x4a, 0x82, 0x03, 0x04, 0x90, 0x1a, 0x02,
|
||||
};
|
||||
esp_err_t err = network_prov_scheme_ble_set_service_uuid(custom_service_uuid);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "wifi_prov_scheme_ble_set_service_uuid failed %d", err);
|
||||
return err;
|
||||
}
|
||||
if (mfg_data) {
|
||||
err = network_prov_scheme_ble_set_mfg_data(mfg_data, mfg_data_len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to set mfg data, err=0x%x", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE */
|
||||
|
||||
/* Start provisioning service */
|
||||
ESP_ERROR_CHECK(network_prov_mgr_start_provisioning(security, pop, service_name, service_key));
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Already provisioned, starting Wi-Fi STA");
|
||||
/* We don't need the manager as device is already provisioned,
|
||||
* so let's release it's resources */
|
||||
network_prov_mgr_deinit();
|
||||
|
||||
/* Start Wi-Fi station */
|
||||
wifi_init_sta();
|
||||
}
|
||||
return ESP_OK;
|
||||
#else
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif /* CONFIG_ESP_RMAKER_NETWORK_OVER_WIFI */
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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_event.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include "app_network.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Initialize Wi-Fi
|
||||
*
|
||||
* This initializes Wi-Fi and the network/wifi provisioning manager
|
||||
*
|
||||
* @return ESP_OK on success.
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t wifi_init();
|
||||
|
||||
/** Start Wi-Fi
|
||||
*
|
||||
* This will start provisioning if the node is not provisioned and will connect to Wi-Fi
|
||||
* if node is provisioned. Function will return successfully only after Wi-Fi is connect
|
||||
*
|
||||
* @param[in] pop The Proof of Possession (PoP) pin
|
||||
* @param[in] service_name The service name of network/wifi provisioning. This translates to
|
||||
* - Wi-Fi SSID when scheme is network_prov_scheme_softap/wifi_prov_scheme_softap
|
||||
* - device name when scheme is network_prov_scheme_ble/wifi_prov_scheme_ble
|
||||
* @param[in] service_key The service key of network/wifi provisioning. This translates to
|
||||
* - Wi-Fi password when scheme is network_prov_scheme_softap/wifi_prov_scheme_softap (NULL = Open network)
|
||||
* @param[in] mfg_data The manufacture specific data of network/wifi provisioning.
|
||||
* @param[in] mfg_data The manufacture specific data length of network/wifi provisioning.
|
||||
* @param[out] provisioned Whether the device is provisioned.
|
||||
*
|
||||
* @return ESP_OK on success (Wi-Fi connected).
|
||||
* @return error in case of failure.
|
||||
*/
|
||||
esp_err_t wifi_start(const char *pop, const char *service_name, const char *service_key, uint8_t *mfg_data,
|
||||
size_t mfg_data_len, bool *provisioned);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# sdkconfig replacement configurations for deprecated options formatted as
|
||||
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
|
||||
|
||||
|
||||
CONFIG_APP_WIFI_PROV_SHOW_QR CONFIG_APP_NETWORK_PROV_SHOW_QR
|
||||
CONFIG_APP_WIFI_PROV_MAX_POP_MISMATCH CONFIG_APP_NETWORK_PROV_MAX_POP_MISMATCH
|
||||
CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP CONFIG_APP_NETWORK_PROV_TRANSPORT_SOFTAP
|
||||
CONFIG_APP_WIFI_PROV_TRANSPORT_BLE CONFIG_APP_NETWORK_PROV_TRANSPORT_BLE
|
||||
CONFIG_APP_WIFI_PROV_TRANSPORT CONFIG_APP_NETWORK_PROV_TRANSPORT
|
||||
CONFIG_APP_WIFI_RESET_PROV_ON_FAILURE CONFIG_APP_NETWORK_RESET_PROV_ON_FAILURE
|
||||
CONFIG_APP_WIFI_SHOW_DEMO_INTRO_TEXT CONFIG_APP_NETWORK_SHOW_DEMO_INTRO_TEXT
|
||||
CONFIG_APP_WIFI_PROV_TIMEOUT_PERIOD CONFIG_APP_NETWORK_PROV_TIMEOUT_PERIOD
|
||||
CONFIG_APP_WIFI_PROV_NAME_PREFIX CONFIG_APP_NETWORK_PROV_NAME_PREFIX
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
set(requires esp_http_client json mbedtls esp_wifi wifi_provisioning nvs_flash protobuf-c esp_app_format pthread)
|
||||
|
||||
idf_component_register(SRCS "rainmaker_api.cpp"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES ${requires})
|
||||
Executable
+1299
File diff suppressed because it is too large
Load Diff
Executable
+394
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <esp_err.h>
|
||||
#include <esp_http_client.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_crt_bundle.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cJSON.h>
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Group operation types for node management
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_RAINMAKER_API_ADD_NODE_TO_GROUP = 0, /* Add node to group */
|
||||
ESP_RAINMAKER_API_REMOVE_NODE_FROM_GROUP, /* Remove node from group */
|
||||
} esp_rainmaker_api_group_operation_type_t;
|
||||
|
||||
/**
|
||||
* @brief Node mapping operation types
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_RAINMAKER_API_ADD_NODE_MAPPING = 0, /* Add node mapping */
|
||||
ESP_RAINMAKER_API_REMOVE_NODE_MAPPING, /* Remove node mapping */
|
||||
} esp_rainmaker_api_node_mapping_operation_type_t;
|
||||
|
||||
/**
|
||||
* @brief Node mapping status types
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_RAINMAKER_API_NODE_MAPPING_STATUS_REQUESTED = 0, /* Node mapping status requested */
|
||||
ESP_RAINMAKER_API_NODE_MAPPING_STATUS_CONFIRMED, /* Node mapping status confirmed */
|
||||
ESP_RAINMAKER_API_NODE_MAPPING_STATUS_TIMEOUT, /* Node mapping status timeout */
|
||||
ESP_RAINMAKER_API_NODE_MAPPING_STATUS_DISCARDED, /* Node mapping status discarded */
|
||||
ESP_RAINMAKER_API_NODE_MAPPING_STATUS_INTERNAL_ERROR, /* Node mapping status internal error */
|
||||
} esp_rainmaker_api_node_mapping_status_type_t;
|
||||
|
||||
/* Login to Rainmaker cloud using refresh token
|
||||
* This function attempts to login to the Rainmaker cloud using the stored refresh token.
|
||||
* If successful, it will store the access token for subsequent API calls.
|
||||
* Returns ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t esp_rainmaker_api_login(void);
|
||||
|
||||
/* Get user ID
|
||||
* This function retrieves the user ID associated with the Rainmaker account.
|
||||
* Returns user ID string
|
||||
*/
|
||||
char *esp_rainmaker_api_get_user_id(void);
|
||||
|
||||
/* Get all nodes associated with the account
|
||||
* This function retrieves all nodes (devices) associated with the Rainmaker account.
|
||||
* It will automatically handle pagination and create corresponding device objects.
|
||||
* Returns ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t esp_rainmaker_api_refresh_nodes(void);
|
||||
|
||||
/* Get nodes list associated with the account
|
||||
* This function retrieves all nodes (devices) associated with the Rainmaker account.
|
||||
* The caller is responsible for freeing the returned string.
|
||||
* Returns JSON string with nodes list (caller must free), nullptr on error
|
||||
*/
|
||||
char* esp_rainmaker_api_get_nodes_list(void);
|
||||
|
||||
/* Get node config
|
||||
* This function retrieves the config of a node (device) in the Rainmaker cloud.
|
||||
* The caller is responsible for freeing the returned string.
|
||||
* @param node_id Node ID
|
||||
* Returns JSON string with node config (caller must free), nullptr on error
|
||||
*/
|
||||
char* esp_rainmaker_api_get_node_config(const char* node_id);
|
||||
|
||||
/* Set node parameters
|
||||
* This function updates the parameters of a node (device) in the Rainmaker cloud.
|
||||
* The parameters should be provided as a JSON string.
|
||||
* @param payload JSON string containing parameter updates
|
||||
* Returns ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t esp_rainmaker_api_set_node_params(const char* payload);
|
||||
|
||||
/* Get node parameters
|
||||
* This function retrieves the current parameters of a specific node (device).
|
||||
* The caller is responsible for freeing the returned string.
|
||||
* @param node_id ID of the node to query
|
||||
* Returns JSON string with node parameters (caller must free), nullptr on error
|
||||
*/
|
||||
char* esp_rainmaker_api_get_node_params(const char* node_id);
|
||||
|
||||
/* Set refresh token for authentication
|
||||
* This function stores the refresh token that will be used for authentication.
|
||||
* The refresh token is used to obtain access tokens for API calls.
|
||||
* @param refresh_token The refresh token string
|
||||
* Returns ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t esp_rainmaker_api_set_refresh_token(const char* refresh_token);
|
||||
|
||||
/* Set base URL for Rainmaker API
|
||||
* This function sets the base URL for the Rainmaker API.
|
||||
* @param base_url The base URL string
|
||||
* Returns ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t esp_rainmaker_api_set_base_url(const char* base_url);
|
||||
|
||||
/* Delete stored refresh token
|
||||
* This function clears the stored refresh token and access token.
|
||||
* This should be called when logging out or when the tokens are no longer valid.
|
||||
* Returns ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t esp_rainmaker_api_delete_refresh_token(void);
|
||||
|
||||
/* Get all groups
|
||||
* This function retrieves all groups associated with the Rainmaker account.
|
||||
* The caller is responsible for freeing the returned string.
|
||||
* Returns JSON string with groups (caller must free), nullptr on error
|
||||
*/
|
||||
char* esp_rainmaker_api_get_group(void);
|
||||
|
||||
/* Create a new group
|
||||
* This function creates a new group in the Rainmaker cloud.
|
||||
* @param group_name Name of the group to create
|
||||
* Returns ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t esp_rainmaker_api_create_group(const char* group_name);
|
||||
|
||||
/* Delete a group
|
||||
* This function deletes an existing group from the Rainmaker cloud.
|
||||
* @param group_id ID of the group to delete
|
||||
* Returns ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t esp_rainmaker_api_delete_group(const char* group_id);
|
||||
|
||||
/* Add or remove node from group
|
||||
* This function adds or removes a node (device) from a group.
|
||||
* The operation type determines whether to add or remove the node.
|
||||
* @param node_id ID of the node to operate on
|
||||
* @param group_id ID of the target group
|
||||
* @param operation_type Operation type (add or remove)
|
||||
* Returns ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t esp_rainmaker_api_operate_node_to_group(const char* node_id, const char* group_id,
|
||||
esp_rainmaker_api_group_operation_type_t operation_type);
|
||||
|
||||
/* Set node mapping
|
||||
* This function sets the node mapping for a user.
|
||||
* @param user_id User ID
|
||||
* @param secret_key Secret key
|
||||
* @param node_id Node ID
|
||||
* @param operation_type Operation type (add or remove)
|
||||
* @param request_id Request ID to store the request ID
|
||||
* Returns ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t esp_rainmaker_api_set_node_mapping(const char* user_id, const char* secret_key, const char* node_id,
|
||||
esp_rainmaker_api_node_mapping_operation_type_t operation_type, char *request_id);
|
||||
|
||||
/* Get node mapping status
|
||||
* This function retrieves the status of a node mapping request.
|
||||
* @param request_id Request ID
|
||||
* Returns node mapping status
|
||||
*/
|
||||
esp_rainmaker_api_node_mapping_status_type_t esp_rainmaker_api_get_node_mapping_status(const char *request_id);
|
||||
|
||||
/* Get node connection status
|
||||
* This function retrieves the connection status of a node.
|
||||
* @param node_id Node ID
|
||||
* @param connection_status Pointer to store connection status
|
||||
* Returns ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t esp_rainmaker_api_get_node_connection_status(const char *node_id, bool *connection_status);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Rainmaker API client class (Singleton)
|
||||
*
|
||||
* This class provides a unified interface for interacting with ESP Rainmaker cloud services.
|
||||
* It handles authentication, node management, and group operations.
|
||||
*/
|
||||
class RainmakerApi
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Get singleton instance
|
||||
* @return Reference to the singleton instance
|
||||
*/
|
||||
static RainmakerApi& GetInstance() {
|
||||
static RainmakerApi instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/* Disable copy constructor and assignment operator */
|
||||
RainmakerApi(const RainmakerApi&) = delete;
|
||||
RainmakerApi& operator=(const RainmakerApi&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Login to Rainmaker cloud using refresh token
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t Login(void);
|
||||
|
||||
/**
|
||||
* @brief Get user info
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t GetUserInfo(void);
|
||||
|
||||
/**
|
||||
* @brief Get all nodes associated with the account
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t RefreshNodes(void);
|
||||
|
||||
/**
|
||||
* @brief Get nodes list associated with the account
|
||||
* @return JSON string with node list (caller must free), nullptr on error
|
||||
*/
|
||||
char* GetNodeList(void);
|
||||
|
||||
/**
|
||||
* @brief Get node config
|
||||
* @param node_id Node ID
|
||||
* @return JSON string with node config (caller must free), nullptr on error
|
||||
*/
|
||||
char* GetNodeConfig(const char* node_id);
|
||||
|
||||
/**
|
||||
* @brief Set node parameters
|
||||
* @param payload JSON payload containing parameter updates
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t SetNodeParams(const char* payload);
|
||||
|
||||
/**
|
||||
* @brief Get node parameters
|
||||
* @param node_id Node ID to query
|
||||
* @return JSON string with node parameters (caller must free), nullptr on error
|
||||
*/
|
||||
char* GetNodeParams(const char* node_id);
|
||||
|
||||
/**
|
||||
* @brief Set refresh token for authentication
|
||||
* @param refresh_token The refresh token string
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t SetRefreshToken(const char* refresh_token);
|
||||
|
||||
/**
|
||||
* @brief Set base URL for Rainmaker API
|
||||
* @param base_url The base URL string
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t SetBaseUrl(const char* base_url);
|
||||
|
||||
/**
|
||||
* @brief Delete stored refresh token
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t DeleteRefreshToken(void);
|
||||
|
||||
/**
|
||||
* @brief Get all groups
|
||||
* @return JSON string with groups (caller must free), nullptr on error
|
||||
*/
|
||||
char* GetGroup(void);
|
||||
|
||||
/**
|
||||
* @brief Create a new group
|
||||
* @param group_name Name of the group to create
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t CreateGroup(const char* group_name);
|
||||
|
||||
/**
|
||||
* @brief Delete a group
|
||||
* @param group_id ID of the group to delete
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t DeleteGroup(const char* group_id);
|
||||
|
||||
/**
|
||||
* @brief Add or remove node from group
|
||||
* @param node_id ID of the node
|
||||
* @param group_id ID of the group
|
||||
* @param operation_type Operation type (add or remove)
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t OperateNodeToGroup(const char* node_id, const char* group_id,
|
||||
esp_rainmaker_api_group_operation_type_t operation_type);
|
||||
|
||||
/**
|
||||
* @brief Set node mapping
|
||||
* @param user_id User ID
|
||||
* @param secret_key Secret key
|
||||
* @param node_id Node ID
|
||||
* @param operation_type Operation type (add or remove)
|
||||
* @param request_id Request ID to store the request ID
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t SetNodeMapping(const char* user_id, const char* secret_key, const char* node_id,
|
||||
esp_rainmaker_api_node_mapping_operation_type_t operation_type, char *request_id);
|
||||
|
||||
/**
|
||||
* @brief Get node mapping status
|
||||
* @param request_id Request ID
|
||||
* @return Node mapping status
|
||||
*/
|
||||
esp_rainmaker_api_node_mapping_status_type_t GetNodeMappingStatus(const char *request_id);
|
||||
|
||||
/**
|
||||
* @brief Get node connection status
|
||||
* @param node_id Node ID
|
||||
* @param connection_status Pointer to store connection status
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t GetNodeConnectionStatus(const char *node_id, bool *connection_status);
|
||||
|
||||
/**
|
||||
* @brief Get user id string (for C API)
|
||||
*/
|
||||
const std::string& GetUserId() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Private constructor for singleton pattern
|
||||
*/
|
||||
RainmakerApi();
|
||||
|
||||
/**
|
||||
* @brief Private destructor
|
||||
*/
|
||||
~RainmakerApi();
|
||||
|
||||
std::string access_token_; /* Current access token */
|
||||
std::string refresh_token_; /* Stored refresh token */
|
||||
std::string base_url_; /* Base URL for Rainmaker API */
|
||||
std::string user_id_; /* User ID */
|
||||
|
||||
/**
|
||||
* @brief Recursively get nodes with pagination
|
||||
* @param start_id Starting node ID for pagination
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t GetNodesRecursive(const char* start_id);
|
||||
|
||||
/**
|
||||
* @brief Cleanup HTTP client resources
|
||||
* @param client HTTP client handle
|
||||
* @param post_data Optional POST data to free
|
||||
*/
|
||||
static void CleanupHttpClient(void* client, char* post_data = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Make HTTP request
|
||||
* @param client HTTP client handle
|
||||
* @param post_data Optional POST data
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
static esp_err_t MakeHttpRequest(esp_http_client_handle_t client, const char* post_data = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Handle HTTP response and check for authentication errors
|
||||
* @param client HTTP client handle
|
||||
* @param response_data Pointer to store response data
|
||||
* @param retry_func Optional retry function to call on authentication error
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t HandleHttpResponse(esp_http_client_handle_t client, char** response_data,
|
||||
std::function<esp_err_t()> retry_func);
|
||||
|
||||
/**
|
||||
* @brief Read HTTP response data
|
||||
* @param client HTTP client handle
|
||||
* @param response_data Pointer to store response data (caller must free)
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t ReadHttpResponse(esp_http_client_handle_t client, char** response_data);
|
||||
};
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
PRIV_INCLUDE_DIRS "." "${ESP_MATTER_PATH}/examples/common/utils")
|
||||
|
||||
set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 17)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-DCHIP_HAVE_CONFIG_H")
|
||||
@@ -0,0 +1,53 @@
|
||||
menu "Rainmaker Bridge"
|
||||
|
||||
config RAINMAKER_PARAMS_GET_PERIOD_MS
|
||||
int
|
||||
default 10000
|
||||
range 1000 60000
|
||||
help
|
||||
Set the period to get rainmaker devices params in rainmaker bridge.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Thread BR Example"
|
||||
depends on OPENTHREAD_BORDER_ROUTER
|
||||
|
||||
choice THREAD_BR_BOARD_TYPE
|
||||
prompt "Thread border router board type"
|
||||
default ESP_THREAD_BR_BOARD_DEV_KIT
|
||||
help
|
||||
The board running the border router.
|
||||
|
||||
config ESP_THREAD_BR_BOARD_DEV_KIT
|
||||
bool "ESP Thread border router dev kit"
|
||||
help
|
||||
Integrated border router dev kit
|
||||
|
||||
config M5STACK_THREAD_BR_BOARD
|
||||
bool "M5Stack Thread border router board"
|
||||
help
|
||||
M5Stack CoreS3 with Module Gateway H2
|
||||
endchoice
|
||||
|
||||
menu "Board Configuration"
|
||||
config PIN_TO_RCP_RESET
|
||||
int "Pin to RCP reset"
|
||||
default "7"
|
||||
|
||||
config PIN_TO_RCP_BOOT
|
||||
int "Pin to RCP boot"
|
||||
default "18" if M5STACK_THREAD_BR_BOARD
|
||||
default "8"
|
||||
|
||||
config PIN_TO_RCP_TX
|
||||
int "Pin to RCP TX"
|
||||
default "10" if M5STACK_THREAD_BR_BOARD
|
||||
default "17"
|
||||
|
||||
config PIN_TO_RCP_RX
|
||||
int "Pin to RCP RX"
|
||||
default "17" if M5STACK_THREAD_BR_BOARD
|
||||
default "18"
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
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_matter.h>
|
||||
#include <esp_matter_console.h>
|
||||
#include <esp_matter_ota.h>
|
||||
|
||||
#include <common_macros.h>
|
||||
#include <app_bridged_device.h>
|
||||
#include <rainmaker_bridge.h>
|
||||
|
||||
static const char *TAG = "app_main";
|
||||
|
||||
using namespace esp_matter;
|
||||
using namespace esp_matter::attribute;
|
||||
using namespace esp_matter::endpoint;
|
||||
|
||||
uint16_t aggregator_endpoint_id = chip::kInvalidEndpointId;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// This callback is called for every attribute update. The callback implementation shall
|
||||
// handle the desired attributes and return an appropriate error code. If the attribute
|
||||
// is not of your interest, please do not return an error code and strictly return ESP_OK.
|
||||
static esp_err_t app_attribute_update_cb(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) {
|
||||
err = rainmaker_bridge_attribute_update(endpoint_id, cluster_id, attribute_id, val);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// This callback is invoked after the creation or resumption of a bridge endpoint.
|
||||
// It can be used to add data model elements (e.g., attributes, commands, etc.) to the bridge endpoint.
|
||||
esp_err_t create_bridge_devices(esp_matter::endpoint_t *ep, uint32_t device_type_id, void *priv_data)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
switch (device_type_id) {
|
||||
case ESP_MATTER_ON_OFF_LIGHT_DEVICE_TYPE_ID: {
|
||||
on_off_light::config_t on_off_light_conf;
|
||||
err = on_off_light::add(ep, &on_off_light_conf);
|
||||
break;
|
||||
}
|
||||
case ESP_MATTER_DIMMABLE_LIGHT_DEVICE_TYPE_ID: {
|
||||
dimmable_light::config_t dimmable_light_conf;
|
||||
err = dimmable_light::add(ep, &dimmable_light_conf);
|
||||
break;
|
||||
}
|
||||
case ESP_MATTER_COLOR_TEMPERATURE_LIGHT_DEVICE_TYPE_ID: {
|
||||
color_temperature_light::config_t color_temperature_light_conf;
|
||||
err = color_temperature_light::add(ep, &color_temperature_light_conf);
|
||||
break;
|
||||
}
|
||||
case ESP_MATTER_EXTENDED_COLOR_LIGHT_DEVICE_TYPE_ID: {
|
||||
extended_color_light::config_t extended_color_light_conf;
|
||||
err = extended_color_light::add(ep, &extended_color_light_conf);
|
||||
cluster_t *color_cluster = cluster::get(ep, chip::app::Clusters::ColorControl::Id);
|
||||
cluster::color_control::feature::hue_saturation::config_t hs_config;
|
||||
cluster::color_control::feature::hue_saturation::add(color_cluster, &hs_config);
|
||||
break;
|
||||
}
|
||||
case ESP_MATTER_ON_OFF_LIGHT_SWITCH_DEVICE_TYPE_ID: {
|
||||
on_off_light_switch::config_t switch_config;
|
||||
err = on_off_light_switch::add(ep, &switch_config);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ESP_LOGE(TAG, "Unsupported bridged matter device type");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
extern "C" void app_main()
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
/* Initialize the ESP NVS layer */
|
||||
nvs_flash_init();
|
||||
|
||||
/* 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, NULL);
|
||||
ABORT_APP_ON_FAILURE(node != nullptr, ESP_LOGE(TAG, "Failed to create Matter node"));
|
||||
|
||||
aggregator::config_t aggregator_config;
|
||||
endpoint_t *aggregator = endpoint::aggregator::create(node, &aggregator_config, ENDPOINT_FLAG_NONE, NULL);
|
||||
ABORT_APP_ON_FAILURE(aggregator != nullptr, ESP_LOGE(TAG, "Failed to create aggregator endpoint"));
|
||||
|
||||
aggregator_endpoint_id = endpoint::get_id(aggregator);
|
||||
|
||||
#if CONFIG_ENABLE_CHIP_SHELL
|
||||
esp_matter::console::diagnostics_register_commands();
|
||||
esp_matter::console::wifi_register_commands();
|
||||
esp_matter::console::factoryreset_register_commands();
|
||||
esp_matter::console::init();
|
||||
#endif
|
||||
rainmaker_init();
|
||||
|
||||
/* Matter start */
|
||||
err = esp_matter::start(app_event_cb);
|
||||
ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to start Matter, err:%d", err));
|
||||
|
||||
err = app_bridge_initialize(node, create_bridge_devices);
|
||||
ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to resume the bridged endpoints: %d", err));
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
dependencies:
|
||||
espressif/esp_rainmaker:
|
||||
version: "^1.4.2"
|
||||
espressif/qrcode:
|
||||
version: "*"
|
||||
espressif/button:
|
||||
version: "^4"
|
||||
@@ -0,0 +1,518 @@
|
||||
/*
|
||||
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_check.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_event.h>
|
||||
#include <esp_matter.h>
|
||||
#include <esp_matter_bridge.h>
|
||||
#include <esp_rmaker_standard_types.h>
|
||||
#include "esp_rmaker_standard_params.h"
|
||||
#include <json_parser.h>
|
||||
#include <json_generator.h>
|
||||
#include <app_bridged_device.h>
|
||||
#include <rainmaker_api.h>
|
||||
#include <rainmaker_controller_std.h>
|
||||
#include <app_network.h>
|
||||
#include <common_macros.h>
|
||||
#include <rainmaker_bridge.h>
|
||||
#include <app_thread_config.h>
|
||||
#include <esp_rmaker_thread_br.h>
|
||||
|
||||
static const char *TAG = "rainmaker_bridge";
|
||||
|
||||
using namespace chip::app::Clusters;
|
||||
using namespace esp_matter;
|
||||
using namespace esp_matter::cluster;
|
||||
|
||||
extern uint16_t aggregator_endpoint_id;
|
||||
|
||||
static esp_err_t attribute_update(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, int value)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
attribute_t *attribute = attribute::get(endpoint_id, cluster_id, attribute_id);
|
||||
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
|
||||
attribute::get_val(attribute, &val);
|
||||
|
||||
if (cluster_id == OnOff::Id) {
|
||||
if (attribute_id == OnOff::Attributes::OnOff::Id) {
|
||||
if (val.val.b != (bool)value) {
|
||||
val.val.b = (bool)value;
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} else if (cluster_id == LevelControl::Id) {
|
||||
if (attribute_id == LevelControl::Attributes::CurrentLevel::Id) {
|
||||
if (val.val.u8 != REMAP_TO_RANGE(value, RMAKER_LEVEL_MAX_VALUE, MATTER_LEVEL_MAX_VALUE)) {
|
||||
val.val.u8 = REMAP_TO_RANGE(value, RMAKER_LEVEL_MAX_VALUE, MATTER_LEVEL_MAX_VALUE);
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} else if (cluster_id == ColorControl::Id) {
|
||||
if (attribute_id == ColorControl::Attributes::CurrentHue::Id) {
|
||||
if (val.val.u8 != REMAP_TO_RANGE(value, RMAKER_HUE_MAX_VALUE, MATTER_HUE_MAX_VALUE)) {
|
||||
val.val.u8 = REMAP_TO_RANGE(value, RMAKER_HUE_MAX_VALUE, MATTER_HUE_MAX_VALUE);
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
} else if (attribute_id == ColorControl::Attributes::CurrentSaturation::Id) {
|
||||
if (val.val.u8 != REMAP_TO_RANGE(value, RMAKER_SATURATION_MAX_VALUE, MATTER_SATURATION_MAX_VALUE)) {
|
||||
val.val.u8 = REMAP_TO_RANGE(value, RMAKER_SATURATION_MAX_VALUE, MATTER_SATURATION_MAX_VALUE);
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
} else if (attribute_id == ColorControl::Attributes::ColorTemperatureMireds::Id) {
|
||||
if (val.val.u16 != REMAP_TO_RANGE_INVERSE(value, STANDARD_TEMPERATURE_FACTOR)) {
|
||||
val.val.u16 = REMAP_TO_RANGE_INVERSE(value, STANDARD_TEMPERATURE_FACTOR);
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} else if (cluster_id == WindowCovering::Id) {
|
||||
/* windowcovering was not supported yet */
|
||||
return err;
|
||||
}
|
||||
|
||||
attribute::report(endpoint_id, cluster_id, attribute_id, &val);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t get_attribute_value_from_rainmaker_device(uint16_t endpoint_id, const char *node_id, const char *node_name)
|
||||
{
|
||||
int attribute_value;
|
||||
jparse_ctx_t jctx;
|
||||
bool power;
|
||||
|
||||
char* receive_buffer = esp_rainmaker_api_get_node_params(node_id);
|
||||
|
||||
if (receive_buffer == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (json_parse_start(&jctx, receive_buffer, strlen(receive_buffer)) != 0) {
|
||||
free(receive_buffer);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (node_name[0] != 0) {
|
||||
if (json_obj_get_object(&jctx, node_name) == 0) {
|
||||
if (json_obj_get_int(&jctx, "Brightness", &attribute_value) == 0) {
|
||||
attribute_update(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, attribute_value);
|
||||
}
|
||||
|
||||
if (json_obj_get_int(&jctx, "Hue", &attribute_value) == 0) {
|
||||
attribute_update(endpoint_id, ColorControl::Id, ColorControl::Attributes::CurrentHue::Id, attribute_value);
|
||||
}
|
||||
|
||||
if (json_obj_get_bool(&jctx, "Power", &power) == 0) {
|
||||
attribute_value = power;
|
||||
attribute_update(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id, attribute_value);
|
||||
}
|
||||
|
||||
if (json_obj_get_int(&jctx, "Saturation", &attribute_value) == 0) {
|
||||
attribute_update(endpoint_id, ColorControl::Id, ColorControl::Attributes::CurrentSaturation::Id, attribute_value);
|
||||
}
|
||||
|
||||
if (json_obj_get_int(&jctx, "CCT", &attribute_value) == 0) {
|
||||
attribute_update(endpoint_id, ColorControl::Id, ColorControl::Attributes::ColorTemperatureMireds::Id, attribute_value);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No light param found in json ");
|
||||
}
|
||||
json_obj_leave_object(&jctx);
|
||||
} else {
|
||||
/* Todo: add other device types later */
|
||||
}
|
||||
|
||||
json_parse_end(&jctx);
|
||||
|
||||
free(receive_buffer);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static uint32_t matter_get_device_type_from_rainmaker_device(const char *input_buf, size_t buf_length, const char *node_name)
|
||||
{
|
||||
jparse_ctx_t jctx;
|
||||
int param_count;
|
||||
int device_count;
|
||||
char type[32];
|
||||
char param_type[32];
|
||||
uint32_t matter_device_type_id = 0xFFFF;
|
||||
|
||||
if (json_parse_start(&jctx, input_buf, buf_length) != 0) {
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
if (json_obj_get_array(&jctx, "devices", &device_count) == 0) {
|
||||
if (json_arr_get_object(&jctx, 0) == 0) {
|
||||
if (json_obj_get_string(&jctx, "type", type, sizeof(type)) == 0) {
|
||||
/* get device type */
|
||||
if ((strcmp(type, ESP_RMAKER_DEVICE_LIGHTBULB) == 0) || (strcmp(type, ESP_RMAKER_DEVICE_LIGHT) == 0)) {
|
||||
matter_device_type_id = ESP_MATTER_ON_OFF_LIGHT_DEVICE_TYPE_ID;
|
||||
if (json_obj_get_array(&jctx, "params", ¶m_count) == 0) {
|
||||
for (int i = 0; i < param_count; i++) {
|
||||
if (json_arr_get_object(&jctx, i) == 0) {
|
||||
if (json_obj_get_string(&jctx, "type", param_type, sizeof(param_type)) == 0) {
|
||||
/* get param type */
|
||||
if (((strcmp(param_type, ESP_RMAKER_PARAM_HUE) == 0) || (strcmp(param_type, ESP_RMAKER_PARAM_SATURATION) == 0)) &&
|
||||
(matter_device_type_id < ESP_MATTER_EXTENDED_COLOR_LIGHT_DEVICE_TYPE_ID)) {
|
||||
matter_device_type_id = ESP_MATTER_EXTENDED_COLOR_LIGHT_DEVICE_TYPE_ID;
|
||||
} else if ((strcmp(param_type, ESP_RMAKER_PARAM_CCT) == 0) &&
|
||||
(matter_device_type_id < ESP_MATTER_COLOR_TEMPERATURE_LIGHT_DEVICE_TYPE_ID)) {
|
||||
matter_device_type_id = ESP_MATTER_COLOR_TEMPERATURE_LIGHT_DEVICE_TYPE_ID;
|
||||
} else if (((strcmp(param_type, ESP_RMAKER_PARAM_BRIGHTNESS) == 0) || (strcmp(param_type, ESP_RMAKER_PARAM_INTENSITY) == 0)) &&
|
||||
(matter_device_type_id < ESP_MATTER_DIMMABLE_LIGHT_DEVICE_TYPE_ID)) {
|
||||
matter_device_type_id = ESP_MATTER_DIMMABLE_LIGHT_DEVICE_TYPE_ID;
|
||||
}
|
||||
}
|
||||
json_arr_leave_object(&jctx);
|
||||
}
|
||||
}
|
||||
json_obj_leave_array(&jctx);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No parameters found in json ");
|
||||
}
|
||||
} else {
|
||||
/* Todo: add other device types */
|
||||
ESP_LOGW(TAG, "unsupported device type\n");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No type found in json ");
|
||||
}
|
||||
|
||||
if (json_obj_get_string(&jctx, "name", node_name, 32) != 0) {
|
||||
ESP_LOGE(TAG, "Get node name failed");
|
||||
}
|
||||
|
||||
json_obj_leave_object(&jctx);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No devices found in array ");
|
||||
}
|
||||
json_obj_leave_array(&jctx);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No devices found in json ");
|
||||
}
|
||||
|
||||
json_parse_end(&jctx);
|
||||
|
||||
return matter_device_type_id;
|
||||
}
|
||||
|
||||
static esp_err_t rainmaker_bridge_match_device(const char *node_id, const char *node_name, uint32_t device_type_id)
|
||||
{
|
||||
node_t *node = node::get();
|
||||
|
||||
if (app_bridge_get_matter_endpointid_by_rainmaker_node_id(node_id) == chip::kInvalidEndpointId) {
|
||||
app_bridged_device_t *bridged_device = app_bridge_create_bridged_device(node, aggregator_endpoint_id, device_type_id,
|
||||
ESP_MATTER_BRIDGED_DEVICE_TYPE_RAINMAKER,
|
||||
app_bridge_rainmaker_address(node_id, node_name), NULL);
|
||||
ESP_RETURN_ON_FALSE(bridged_device, ESP_FAIL, TAG, "Failed to create bridged device (rainmaker device)");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Bridged node for %s rainmaker device has been created", node_id);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rainmaker_bridge_get_param_from_device(const char* node_id, uint16_t endpoint_id)
|
||||
{
|
||||
node_t *node = node::get();
|
||||
endpoint_t *dev_endpoint = endpoint::get(node, endpoint_id);
|
||||
uint8_t device_type_count = endpoint::get_device_type_count(dev_endpoint);
|
||||
uint32_t dev_type_id;
|
||||
uint8_t dev_type_ver;
|
||||
|
||||
for (uint8_t i = 0; i < device_type_count; ++i) {
|
||||
if ((ESP_OK == endpoint::get_device_type_at_index(dev_endpoint, i, dev_type_id, dev_type_ver))) {
|
||||
ESP_LOGI(TAG, "Endpoint Id: %d--Node Id: %s Device Type: %ld\n", endpoint_id, node_id, dev_type_id);
|
||||
if (dev_type_id != endpoint::bridged_node::get_device_type_id()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(dev_type_id) {
|
||||
case ESP_MATTER_EXTENDED_COLOR_LIGHT_DEVICE_TYPE_ID:
|
||||
case ESP_MATTER_COLOR_TEMPERATURE_LIGHT_DEVICE_TYPE_ID:
|
||||
case ESP_MATTER_DIMMABLE_LIGHT_DEVICE_TYPE_ID:
|
||||
case ESP_MATTER_ON_OFF_LIGHT_DEVICE_TYPE_ID:
|
||||
{
|
||||
get_attribute_value_from_rainmaker_device(endpoint_id ,node_id, app_bridge_get_rainmaker_node_name_by_matter_endpointid(endpoint_id));
|
||||
}
|
||||
break;
|
||||
/* Todo: add other device types */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rainmaker_bridge_update_online_state(const char* node_id, uint16_t endpoint_id)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
bool connection_status = false;
|
||||
attribute_t *attribute = attribute::get(endpoint_id, BridgedDeviceBasicInformation::Id, BridgedDeviceBasicInformation::Attributes::Reachable::Id);
|
||||
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
|
||||
attribute::get_val(attribute, &val);
|
||||
|
||||
err = esp_rainmaker_api_get_node_connection_status(node_id, &connection_status);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get node %s connection status", node_id);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "node %s connection status: %d", node_id, connection_status);
|
||||
if (val.val.b != connection_status) {
|
||||
val.val.b = connection_status;
|
||||
attribute::update(endpoint_id, BridgedDeviceBasicInformation::Id, BridgedDeviceBasicInformation::Attributes::Reachable::Id, &val);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t rainmaker_bridge_add_new_device(const char *node_id)
|
||||
{
|
||||
uint32_t matter_device_type = 0;
|
||||
char node_name[32] = {0};
|
||||
|
||||
if (node_id != NULL) {
|
||||
char* receive_buffer = esp_rainmaker_api_get_node_config(node_id);
|
||||
if (receive_buffer == NULL) {
|
||||
ESP_LOGE(TAG, "Get Node %s config failed\n", node_id);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
matter_device_type = matter_get_device_type_from_rainmaker_device(receive_buffer, strlen(receive_buffer), node_name);
|
||||
if ((matter_device_type != 0xFFFF) && (node_name[0] != 0)) {
|
||||
rainmaker_bridge_match_device(node_id, node_name, matter_device_type);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Node %s device type 0x%lx is invalid\n", node_id, matter_device_type);
|
||||
free(receive_buffer);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
free(receive_buffer);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rainmaker_bridge_delete_device(uint16_t endpoint_id)
|
||||
{
|
||||
const char* node_id = app_bridge_get_rainmaker_node_id_by_matter_endpointid(endpoint_id);
|
||||
|
||||
if (node_id == NULL) {
|
||||
ESP_LOGI(TAG, "Can't find rainmaker device from ep: %d", endpoint_id);
|
||||
}
|
||||
|
||||
app_bridged_device_t *bridged_device = app_bridge_get_device_by_rainmaker_node_id(node_id);
|
||||
|
||||
if (bridged_device) {
|
||||
app_bridge_remove_device(bridged_device);
|
||||
ESP_LOGI(TAG, "Bridged rainmaker device removed: %s", node_id);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Bridged rainmaker device not found: %s", node_id);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void matter_check_and_remove_not_exist_device()
|
||||
{
|
||||
uint16_t matter_endpoint_id_array[MAX_BRIDGED_DEVICE_COUNT];
|
||||
esp_matter_bridge::get_bridged_endpoint_ids(matter_endpoint_id_array);
|
||||
|
||||
for (int i = 0; i < MAX_BRIDGED_DEVICE_COUNT; i++) {
|
||||
if (matter_endpoint_id_array[i] != 0xFFFF) {
|
||||
const char *node_id = app_bridge_get_rainmaker_node_id_by_matter_endpointid(matter_endpoint_id_array[i]);
|
||||
if (node_id != NULL) {
|
||||
char *buffer = esp_rainmaker_api_get_node_params(node_id);
|
||||
if (buffer == NULL) {
|
||||
ESP_LOGI(TAG, "Remove not exist Rainmaker device Node: %s Endpoint: %d\n", node_id, matter_endpoint_id_array[i]);
|
||||
rainmaker_bridge_delete_device(matter_endpoint_id_array[i]);
|
||||
} else {
|
||||
free(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t rainmaker_sync_nodes(char *out_buf, size_t out_buf_len)
|
||||
{
|
||||
jparse_ctx_t jctx;
|
||||
int total_count;
|
||||
char node[32];
|
||||
static uint8_t check_count = 0;
|
||||
if (json_parse_start(&jctx, out_buf, out_buf_len) != 0) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (json_obj_get_array(&jctx, "nodes", &total_count) == 0) {
|
||||
for (int i = 0; i < total_count; i++) {
|
||||
if (json_arr_get_string(&jctx, i, node, sizeof(node)) == 0) {
|
||||
uint16_t endpoint_id = app_bridge_get_matter_endpointid_by_rainmaker_node_id(node);
|
||||
if (endpoint_id == chip::kInvalidEndpointId) {
|
||||
if (rainmaker_bridge_add_new_device(node) == ESP_OK) {
|
||||
endpoint_id = app_bridge_get_matter_endpointid_by_rainmaker_node_id(node);
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Exist node: %s--endpoint id: %d\n", node, endpoint_id);
|
||||
if (endpoint_id != chip::kInvalidEndpointId) {
|
||||
rainmaker_bridge_get_param_from_device(node, endpoint_id);
|
||||
rainmaker_bridge_update_online_state(node, endpoint_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
json_obj_leave_array(&jctx);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No node found in json ");
|
||||
json_obj_leave_array(&jctx);
|
||||
}
|
||||
|
||||
json_parse_end(&jctx);
|
||||
|
||||
check_count++;
|
||||
if (check_count >= 5) {
|
||||
matter_check_and_remove_not_exist_device();
|
||||
check_count = 0;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t rainmaker_bridge_attribute_update(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val)
|
||||
{
|
||||
char param_buffer[128] = {0};
|
||||
const char* node_id = app_bridge_get_rainmaker_node_id_by_matter_endpointid(endpoint_id);
|
||||
const char* node_name = app_bridge_get_rainmaker_node_name_by_matter_endpointid(endpoint_id);
|
||||
if (node_id == NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t err = ESP_OK;
|
||||
if (cluster_id == OnOff::Id) {
|
||||
if (attribute_id == OnOff::Attributes::OnOff::Id) {
|
||||
snprintf(param_buffer, sizeof(param_buffer), "[{\"node_id\":\"%s\",\"payload\":{\"%s\":{\"Power\":%s}}}]", node_id, node_name, val->val.b ? "true" : "false");
|
||||
}
|
||||
} else if (cluster_id == LevelControl::Id) {
|
||||
if (attribute_id == LevelControl::Attributes::CurrentLevel::Id) {
|
||||
attribute_t *attribute = attribute::get(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id);
|
||||
esp_matter_attr_val_t val_onoff = esp_matter_invalid(NULL);
|
||||
attribute::get_val(attribute, &val_onoff);
|
||||
if (val_onoff.val.b == false) {
|
||||
return ESP_OK;
|
||||
}
|
||||
snprintf(param_buffer, sizeof(param_buffer), "[{\"node_id\":\"%s\",\"payload\":{\"%s\":{\"Brightness\":%d}}}]", node_id, node_name,
|
||||
REMAP_TO_RANGE(val->val.u8, MATTER_LEVEL_MAX_VALUE, RMAKER_LEVEL_MAX_VALUE));
|
||||
}
|
||||
} else if (cluster_id == ColorControl::Id) {
|
||||
if (attribute_id == ColorControl::Attributes::CurrentHue::Id) {
|
||||
snprintf(param_buffer, sizeof(param_buffer), "[{\"node_id\":\"%s\",\"payload\":{\"%s\":{\"Hue\":%d}}}]", node_id, node_name,
|
||||
REMAP_TO_RANGE(val->val.u8, MATTER_HUE_MAX_VALUE, RMAKER_HUE_MAX_VALUE));
|
||||
} else if (attribute_id == ColorControl::Attributes::CurrentSaturation::Id) {
|
||||
snprintf(param_buffer, sizeof(param_buffer), "[{\"node_id\":\"%s\",\"payload\":{\"%s\":{\"Saturation\":%d}}}]", node_id, node_name,
|
||||
REMAP_TO_RANGE(val->val.u8, MATTER_SATURATION_MAX_VALUE, RMAKER_SATURATION_MAX_VALUE));
|
||||
} else if (attribute_id == ColorControl::Attributes::ColorTemperatureMireds::Id) {
|
||||
snprintf(param_buffer, sizeof(param_buffer), "[{\"node_id\":\"%s\",\"payload\":{\"%s\":{\"CCT\":%d}}}]", node_id, node_name,
|
||||
REMAP_TO_RANGE_INVERSE(val->val.u16, STANDARD_TEMPERATURE_FACTOR));
|
||||
}
|
||||
}
|
||||
|
||||
if (param_buffer[0] == 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_rainmaker_api_set_node_params(param_buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void rainmaker_bridge_task(void *pvParameters)
|
||||
{
|
||||
while(true) {
|
||||
vTaskDelay(pdMS_TO_TICKS(CONFIG_RAINMAKER_PARAMS_GET_PERIOD_MS));
|
||||
char* nodes_buffer = esp_rainmaker_api_get_nodes_list();
|
||||
if (nodes_buffer == NULL) {
|
||||
continue;
|
||||
}
|
||||
rainmaker_sync_nodes(nodes_buffer, strlen(nodes_buffer));
|
||||
free(nodes_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t write_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
|
||||
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
|
||||
{
|
||||
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_BASE_URL) == 0) {
|
||||
if (val.type != RMAKER_VAL_TYPE_STRING || !val.val.s) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
ESP_LOGI(TAG, "Set base url: %s\n", val.val.s);
|
||||
ESP_RETURN_ON_ERROR(esp_rainmaker_api_set_base_url(val.val.s), TAG, "Failed to set base_url");
|
||||
} else if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_USER_TOKEN) == 0) {
|
||||
if (val.type != RMAKER_VAL_TYPE_STRING || !val.val.s) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
ESP_LOGI(TAG, "Set user token: %s\n", val.val.s);
|
||||
ESP_RETURN_ON_ERROR(esp_rainmaker_api_set_refresh_token(val.val.s), TAG, "Failed to set user_token");
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void rainmaker_init()
|
||||
{
|
||||
/* Network Init */
|
||||
app_network_init();
|
||||
|
||||
/* Initialize the ESP RainMaker Agent.
|
||||
* Note that this should be called after app_network_init() but before app_network_start()
|
||||
* */
|
||||
esp_rmaker_config_t rainmaker_cfg = {
|
||||
.enable_time_sync = false,
|
||||
};
|
||||
esp_rmaker_node_t *node = esp_rmaker_node_init(&rainmaker_cfg, "ESP RainMaker Device", "RainmakerController");
|
||||
if (!node) {
|
||||
ESP_LOGE(TAG, "Could not initialise node. Aborting!!!");
|
||||
vTaskDelay(5000/portTICK_PERIOD_MS);
|
||||
abort();
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *device = esp_rmaker_device_create("Rainmaker Controller", ESP_RMAKER_DEVICE_CONTROLLER, NULL);
|
||||
|
||||
esp_rmaker_device_add_param(device, esp_rmaker_name_param_create(ESP_RMAKER_DEF_NAME_PARAM, "RainmakerController"));
|
||||
|
||||
esp_rmaker_node_add_device(node, device);
|
||||
|
||||
esp_rmaker_node_add_device(node, rainmaker_controller_service_create("RainmakerCTL", write_cb, NULL, NULL));
|
||||
|
||||
esp_rmaker_device_t *thread_br_device = esp_rmaker_device_create("ThreadBR", ESP_RMAKER_DEVICE_THREAD_BR, NULL);
|
||||
esp_rmaker_device_add_param(thread_br_device,
|
||||
esp_rmaker_name_param_create(ESP_RMAKER_DEF_NAME_PARAM, "ESP-ThreadBR"));
|
||||
esp_rmaker_node_add_device(node, thread_br_device);
|
||||
|
||||
esp_openthread_platform_config_t thread_cfg = {
|
||||
.radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
|
||||
.host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
|
||||
.port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG()
|
||||
};
|
||||
|
||||
#ifdef CONFIG_AUTO_UPDATE_RCP
|
||||
esp_rcp_update_config_t rcp_update_cfg = ESP_OPENTHREAD_RCP_UPDATE_CONFIG();
|
||||
esp_rmaker_thread_br_enable(&thread_cfg, &rcp_update_cfg);
|
||||
#else
|
||||
esp_rmaker_thread_br_enable(&thread_cfg, NULL);
|
||||
#endif
|
||||
|
||||
esp_rmaker_start();
|
||||
|
||||
app_network_start(POP_TYPE_RANDOM);
|
||||
|
||||
/* create task to get node and params from rainmaker side */
|
||||
xTaskCreate(rainmaker_bridge_task, "rainmaker_main", 10240, xTaskGetCurrentTaskHandle(), 5, NULL);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
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_matter_attribute_utils.h>
|
||||
#include <app_bridged_device.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define MATTER_HUE_MAX_VALUE 254
|
||||
#define MATTER_SATURATION_MAX_VALUE 254
|
||||
#define MATTER_LEVEL_MAX_VALUE 254
|
||||
|
||||
#define STANDARD_TEMPERATURE_FACTOR 1000000
|
||||
|
||||
#define RMAKER_HUE_MAX_VALUE 360
|
||||
#define RMAKER_SATURATION_MAX_VALUE 100
|
||||
#define RMAKER_LEVEL_MAX_VALUE 100
|
||||
|
||||
esp_err_t rainmaker_bridge_attribute_update(uint16_t endpoint_id, uint32_t cluster_id,
|
||||
uint32_t attribute_id, esp_matter_attr_val_t *val);
|
||||
|
||||
/* Init Rainmaker */
|
||||
void rainmaker_init();
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
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_rmaker_core.h>
|
||||
#include <rainmaker_controller_std.h>
|
||||
|
||||
esp_rmaker_param_t *rainmaker_controller_base_url_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_BASE_URL,
|
||||
esp_rmaker_str(""), PROP_FLAG_READ | PROP_FLAG_WRITE | PROP_FLAG_PERSIST);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_param_t *rainmaker_controller_user_token_param_create(const char *param_name)
|
||||
{
|
||||
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_USER_TOKEN,
|
||||
esp_rmaker_str(""), PROP_FLAG_WRITE | PROP_FLAG_PERSIST);
|
||||
return param;
|
||||
}
|
||||
|
||||
esp_rmaker_device_t *rainmaker_controller_service_create(const char *serv_name, esp_rmaker_device_write_cb_t write_cb,
|
||||
esp_rmaker_device_read_cb_t read_cb, void *priv_data)
|
||||
{
|
||||
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_CONTROLLER, priv_data);
|
||||
if (service) {
|
||||
esp_rmaker_device_add_cb(service, write_cb, read_cb);
|
||||
esp_rmaker_device_add_param(service, rainmaker_controller_base_url_param_create(ESP_RMAKER_DEF_BASE_URL_NAME));
|
||||
esp_rmaker_device_add_param(service, rainmaker_controller_user_token_param_create(ESP_RMAKER_DEF_USER_TOKEN_NAME));
|
||||
}
|
||||
return service;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
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_rmaker_core.h>
|
||||
|
||||
// Rainmaker Controller device
|
||||
#define ESP_RMAKER_DEVICE_CONTROLLER "esp.device.controller"
|
||||
|
||||
// Rainmaker Controller service
|
||||
#define ESP_RMAKER_SERVICE_CONTROLLER "esp.service.rmaker-user-auth"
|
||||
|
||||
// Rainmaker Controller parameters
|
||||
#define ESP_RMAKER_DEF_BASE_URL_NAME "BaseURL"
|
||||
#define ESP_RMAKER_PARAM_BASE_URL "esp.param.base-url"
|
||||
#define ESP_RMAKER_DEF_USER_TOKEN_NAME "UserToken"
|
||||
#define ESP_RMAKER_PARAM_USER_TOKEN "esp.param.user-token"
|
||||
|
||||
esp_rmaker_param_t *rainmaker_controller_base_url_param_create(const char *param_name);
|
||||
esp_rmaker_param_t *rainmaker_controller_user_token_param_create(const char *param_name);
|
||||
|
||||
esp_rmaker_device_t *rainmaker_controller_service_create(const char *serv_name, esp_rmaker_device_write_cb_t write_cb,
|
||||
esp_rmaker_device_read_cb_t read_cb, void *priv_data);
|
||||
@@ -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, encrypted
|
||||
nvs, data, nvs, 0x10000, 0xF000,
|
||||
nvs_keys, data, nvs_keys, 0x1F000, 0x1000, encrypted
|
||||
ota_0, app, ota_0, 0x20000, 0x250000,
|
||||
ota_1, app, ota_1, 0x270000, 0x250000,
|
||||
fctry, data, nvs, 0x4C0000, 0x6000,
|
||||
phy_init, data, phy, 0x4C6000, 0x1000,
|
||||
otadata, data, ota, 0x4C7000, 0x2000
|
||||
|
@@ -0,0 +1,47 @@
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
|
||||
|
||||
# Enable BT
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_TASK_STACK_SIZE=5120
|
||||
CONFIG_BT_NIMBLE_ACL_BUF_SIZE=255
|
||||
|
||||
# Enable FreeRTOS legacy API
|
||||
CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y
|
||||
|
||||
# Enable lwip ipv6 autoconfig
|
||||
CONFIG_LWIP_IPV6_AUTOCONFIG=y
|
||||
|
||||
# Use a custom partition table
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
|
||||
# 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
|
||||
|
||||
# Watchdog
|
||||
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=n
|
||||
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=n
|
||||
|
||||
# Enable HKDF in mbedtls
|
||||
CONFIG_MBEDTLS_HKDF_C=y
|
||||
|
||||
# Increase LwIP IPv6 address number to 6 (MAX_FABRIC + 1)
|
||||
# unique local addresses for fabrics(MAX_FABRIC), a link local address(1)
|
||||
CONFIG_LWIP_IPV6_NUM_ADDRESSES=6
|
||||
|
||||
# Increase matter console stack size
|
||||
CONFIG_ESP_MATTER_CONSOLE_TASK_STACK=3072
|
||||
|
||||
# mbedtls
|
||||
CONFIG_MBEDTLS_DYNAMIC_BUFFER=y
|
||||
CONFIG_MBEDTLS_DYNAMIC_FREE_PEER_CERT=y
|
||||
CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y
|
||||
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y
|
||||
|
||||
# Disable BLE for matter
|
||||
CONFIG_ENABLE_CHIPOBLE=n
|
||||
@@ -0,0 +1,7 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
|
||||
# System event stack size
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072
|
||||
|
||||
# Increase timer stack size
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
|
||||
@@ -165,6 +165,17 @@ app_bridged_device_address_t app_bridge_espnow_address(uint8_t espnow_macaddr[6]
|
||||
return bridged_address;
|
||||
}
|
||||
|
||||
app_bridged_device_address_t app_bridge_rainmaker_address(const char* rainmaker_node_id, const char* rainmaker_node_name)
|
||||
{
|
||||
app_bridged_device_address_t bridged_address = {
|
||||
.rainmaker_node_id = {0},
|
||||
.rainmaker_node_name = {0},
|
||||
};
|
||||
memcpy(bridged_address.rainmaker_node_id, rainmaker_node_id, 32);
|
||||
memcpy(bridged_address.rainmaker_node_name, rainmaker_node_name, 32);
|
||||
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,
|
||||
@@ -404,4 +415,57 @@ uint8_t *app_bridge_get_espnow_macaddr_by_matter_endpointid(uint16_t matter_endp
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Rainmaker Device APIs */
|
||||
app_bridged_device_t *app_bridge_get_device_by_rainmaker_node_id(char rainmaker_node_id[32])
|
||||
{
|
||||
app_bridged_device_t *current_dev = g_bridged_device_list;
|
||||
while (current_dev) {
|
||||
if ((current_dev->dev_type == ESP_MATTER_BRIDGED_DEVICE_TYPE_RAINMAKER) && current_dev->dev &&
|
||||
!memcmp(current_dev->dev_addr.rainmaker_node_id, rainmaker_node_id, 32)) {
|
||||
return current_dev;
|
||||
}
|
||||
current_dev = current_dev->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint16_t app_bridge_get_matter_endpointid_by_rainmaker_node_id(char rainmaker_node_id[32])
|
||||
{
|
||||
app_bridged_device_t *current_dev = g_bridged_device_list;
|
||||
while (current_dev) {
|
||||
if ((current_dev->dev_type == ESP_MATTER_BRIDGED_DEVICE_TYPE_RAINMAKER) && current_dev->dev &&
|
||||
!memcmp(current_dev->dev_addr.rainmaker_node_id, rainmaker_node_id, strlen(rainmaker_node_id))) {
|
||||
return esp_matter::endpoint::get_id(current_dev->dev->endpoint);
|
||||
}
|
||||
current_dev = current_dev->next;
|
||||
}
|
||||
return chip::kInvalidEndpointId;
|
||||
}
|
||||
|
||||
char* app_bridge_get_rainmaker_node_id_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_RAINMAKER) && current_dev->dev &&
|
||||
(esp_matter::endpoint::get_id(current_dev->dev->endpoint) == matter_endpointid)) {
|
||||
return current_dev->dev_addr.rainmaker_node_id;
|
||||
}
|
||||
current_dev = current_dev->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* app_bridge_get_rainmaker_node_name_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_RAINMAKER) && current_dev->dev &&
|
||||
(esp_matter::endpoint::get_id(current_dev->dev->endpoint) == matter_endpointid)) {
|
||||
return current_dev->dev_addr.rainmaker_node_name;
|
||||
}
|
||||
current_dev = current_dev->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -26,6 +26,8 @@ typedef enum {
|
||||
ESP_MATTER_BRIDGED_DEVICE_TYPE_BLEMESH,
|
||||
/** ESP-NOW */
|
||||
ESP_MATTER_BRIDGED_DEVICE_TYPE_ESPNOW,
|
||||
/** Rainmaker */
|
||||
ESP_MATTER_BRIDGED_DEVICE_TYPE_RAINMAKER,
|
||||
} app_bridged_device_type_t;
|
||||
|
||||
/* Bridged Device Address */
|
||||
@@ -43,6 +45,11 @@ typedef union {
|
||||
struct {
|
||||
uint8_t espnow_macaddr[6];
|
||||
};
|
||||
/** Rainmaker */
|
||||
struct {
|
||||
char rainmaker_node_id[32];
|
||||
char rainmaker_node_name[32];
|
||||
};
|
||||
} app_bridged_device_address_t;
|
||||
|
||||
/* Bridged Device */
|
||||
@@ -66,6 +73,8 @@ 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);
|
||||
|
||||
app_bridged_device_address_t app_bridge_rainmaker_address(const char* rainmaker_node_id, const char* rainmaker_node_name);
|
||||
|
||||
/** 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,
|
||||
@@ -97,3 +106,12 @@ app_bridged_device_t *app_bridge_get_device_by_espnow_macaddr(uint8_t espnow_mac
|
||||
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);
|
||||
|
||||
/** Rainmaker Device APIs */
|
||||
app_bridged_device_t *app_bridge_get_device_by_rainmaker_node_id(char rainmaker_node_id[32]);
|
||||
|
||||
uint16_t app_bridge_get_matter_endpointid_by_rainmaker_node_id(char rainmaker_node_id[32]);
|
||||
|
||||
char* app_bridge_get_rainmaker_node_id_by_matter_endpointid(uint16_t matter_endpointid);
|
||||
|
||||
char* app_bridge_get_rainmaker_node_name_by_matter_endpointid(uint16_t matter_endpointid);
|
||||
|
||||
Reference in New Issue
Block a user