LED-Germany/01_Source/matter-light-c6-wifi/main/app_main.cpp

1306 lines
47 KiB
C++
Raw Normal View History

2026-02-26 09:59:27 +08:00
/*
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 <esp_event.h>
#include <esp_now.h>
#include <nvs_flash.h>
#include <esp_netif.h>
#include <esp_wifi.h>
#include <string.h>
#include <stdlib.h>
#include <esp_timer.h>
#include <nvs.h>
#include <esp_matter.h>
#include <esp_matter_console.h>
#include <esp_matter_ota.h>
#include <common_macros.h>
#include <log_heap_numbers.h>
#include <OtaManager.h>
#include <app_priv.h>
#include <app_reset.h>
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
#include <platform/ESP32/OpenthreadLauncher.h>
#endif
#include <setup_payload/OnboardingCodesUtil.h>
#include <app/server/CommissioningWindowManager.h>
#include <app/server/Server.h>
#include <platform/PlatformManager.h>
#include <app/clusters/scenes-server/scenes-server.h>
#include <lib/core/CHIPConfig.h>
#ifdef CONFIG_ENABLE_SET_CERT_DECLARATION_API
#include <esp_matter_providers.h>
#include <lib/support/Span.h>
#ifdef CONFIG_SEC_CERT_DAC_PROVIDER
#include <platform/ESP32/ESP32SecureCertDACProvider.h>
#elif defined(CONFIG_FACTORY_PARTITION_DAC_PROVIDER)
#include <platform/ESP32/ESP32FactoryDataProvider.h>
#endif
using namespace chip::DeviceLayer;
#endif
static const char *TAG = "app_main";
uint16_t rgb_light_endpoint_id = 0;
uint16_t cct_light_endpoint_id = 0;
using namespace esp_matter;
using namespace esp_matter::attribute;
using namespace esp_matter::endpoint;
using namespace chip::app::Clusters;
static void matter_set_onoff(uint16_t endpoint_id, bool on)
{
attribute_t *attribute = attribute::get(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id);
if (!attribute) {
return;
}
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);
val.val.b = on;
attribute::update(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id, &val);
}
static void matter_toggle_onoff(uint16_t endpoint_id)
{
attribute_t *attribute = attribute::get(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id);
if (!attribute) {
return;
}
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);
val.val.b = !val.val.b;
attribute::update(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id, &val);
}
static void matter_adjust_brightness(uint16_t endpoint_id, int delta)
{
attribute_t *attribute = attribute::get(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id);
if (!attribute) {
return;
}
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);
int level = static_cast<int>(val.val.u8);
level += delta;
if (level < 0) {
level = 0;
}
if (level > MATTER_BRIGHTNESS) {
level = MATTER_BRIGHTNESS;
}
val.val.u8 = static_cast<uint8_t>(level);
attribute::update(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, &val);
}
static void matter_adjust_color_temperature_mireds(uint16_t endpoint_id, int delta)
{
attribute_t *attribute =
attribute::get(endpoint_id, ColorControl::Id, ColorControl::Attributes::ColorTemperatureMireds::Id);
if (!attribute) {
return;
}
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);
int mireds = static_cast<int>(val.val.u16);
mireds += delta;
// Try to clamp using physical min/max if present; fall back to common CCT range.
int min_mireds = 153; // ~6500K
int max_mireds = 500; // ~2000K
attribute_t *min_attr =
attribute::get(endpoint_id, ColorControl::Id, ColorControl::Attributes::ColorTempPhysicalMinMireds::Id);
attribute_t *max_attr =
attribute::get(endpoint_id, ColorControl::Id, ColorControl::Attributes::ColorTempPhysicalMaxMireds::Id);
if (min_attr && max_attr) {
esp_matter_attr_val_t vmin = esp_matter_invalid(NULL);
esp_matter_attr_val_t vmax = esp_matter_invalid(NULL);
attribute::get_val(min_attr, &vmin);
attribute::get_val(max_attr, &vmax);
if (vmin.type == ESP_MATTER_VAL_TYPE_UINT16 && vmax.type == ESP_MATTER_VAL_TYPE_UINT16 &&
vmin.val.u16 > 0 && vmax.val.u16 > 0 && vmin.val.u16 < vmax.val.u16) {
min_mireds = static_cast<int>(vmin.val.u16);
max_mireds = static_cast<int>(vmax.val.u16);
}
}
if (mireds < min_mireds) {
mireds = min_mireds;
}
if (mireds > max_mireds) {
mireds = max_mireds;
}
val.val.u16 = static_cast<uint16_t>(mireds);
attribute::update(endpoint_id, ColorControl::Id, ColorControl::Attributes::ColorTemperatureMireds::Id, &val);
}
constexpr auto k_timeout_seconds = 300;
namespace {
enum EspNowCmd : uint8_t {
kEspNowCmdToggle = 0x01,
kEspNowCmdOn = 0x02,
kEspNowCmdBrightnessUp = 0x03,
kEspNowCmdBrightnessDown = 0x04,
kEspNowCmdOff = 0x05,
kEspNowCmdPairingConfirm = 0xA5,
};
constexpr uint8_t kBrightnessStep = 20;
static constexpr uint8_t kCctWarmCmd = kEspNowCmdBrightnessDown;
static constexpr uint8_t kCctCoolCmd = kEspNowCmdBrightnessUp;
// Short/long inference for CCT endpoint brightness keys.
// Requires the remote to send repeated brightness commands while holding the key.
static constexpr uint32_t kCctLongPressRepeatGapMs = 900;
static constexpr uint8_t kCctLongPressMinRepeats = 3;
static constexpr uint32_t kCctShortPressGapMs = 350;
static constexpr uint32_t kCctPressReleaseGapMs = 1500;
static constexpr uint32_t kCctColorModeGapMs = 800;
static constexpr int kCctTempStepMireds = 10;
static constexpr chip::GroupId kReadingSceneGroupId = 0x0000;
static constexpr chip::SceneId kReadingSceneId = 0x01;
static constexpr uint8_t kReadingRgbLevel = 180;
static constexpr uint8_t kReadingCctLevel = 180;
static constexpr uint16_t kReadingCctTempMireds = 370;
static uint8_t s_cct_last_cmd = 0;
static uint8_t s_cct_repeat_count = 0;
static uint32_t s_cct_last_cmd_ms = 0;
static bool s_cct_brightness_applied = false;
static int s_cct_brightness_applied_delta = 0;
static bool s_cct_color_mode_active = false;
static uint32_t s_cct_last_repeat_ms = 0;
static bool s_suppress_driver_updates = false;
static bool s_reading_scene_stored_for_fabric[CHIP_CONFIG_MAX_FABRICS + 1] = { 0 };
static void store_reading_scene_for_fabric(intptr_t arg);
static void store_reading_scenes_for_all_fabrics(intptr_t arg)
{
(void)arg;
const auto & fabric_table = chip::Server::GetInstance().GetFabricTable();
for (auto it = fabric_table.begin(); it != fabric_table.end(); ++it) {
const chip::FabricIndex fabric = it->GetFabricIndex();
store_reading_scene_for_fabric((intptr_t) fabric);
}
}
static void store_reading_scene_for_fabric(intptr_t arg)
{
const chip::FabricIndex fabric = static_cast<chip::FabricIndex>(arg);
if (fabric == chip::kUndefinedFabricIndex || fabric > CHIP_CONFIG_MAX_FABRICS) {
return;
}
if (s_reading_scene_stored_for_fabric[fabric]) {
return;
}
auto store_for_endpoint = [&](uint16_t endpoint_id, bool with_color_temp) {
if (!endpoint_id) {
return;
}
// Require ScenesManagement cluster present.
if (!cluster::get(endpoint_id, ScenesManagement::Id)) {
ESP_LOGW(TAG, "ScenesManagement cluster missing on endpoint %u, skipping reading scene", (unsigned)endpoint_id);
return;
}
attribute_t *onoff_attr = attribute::get(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id);
attribute_t *level_attr = attribute::get(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id);
if (!onoff_attr || !level_attr) {
ESP_LOGW(TAG, "Missing OnOff/LevelControl attributes on endpoint %u, skipping reading scene", (unsigned)endpoint_id);
return;
}
esp_matter_attr_val_t prev_onoff = esp_matter_invalid(NULL);
esp_matter_attr_val_t prev_level = esp_matter_invalid(NULL);
attribute::get_val(onoff_attr, &prev_onoff);
attribute::get_val(level_attr, &prev_level);
esp_matter_attr_val_t prev_temp = esp_matter_invalid(NULL);
attribute_t *temp_attr = nullptr;
if (with_color_temp) {
temp_attr = attribute::get(endpoint_id, ColorControl::Id, ColorControl::Attributes::ColorTemperatureMireds::Id);
if (temp_attr) {
attribute::get_val(temp_attr, &prev_temp);
}
}
s_suppress_driver_updates = true;
// Apply desired scene state to attributes (suppressed from affecting the physical driver).
esp_matter_attr_val_t v_onoff = prev_onoff;
v_onoff.val.b = true;
attribute::update(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id, &v_onoff);
esp_matter_attr_val_t v_level = prev_level;
v_level.val.u8 = with_color_temp ? kReadingCctLevel : kReadingRgbLevel;
attribute::update(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, &v_level);
if (with_color_temp && temp_attr) {
esp_matter_attr_val_t v_temp = prev_temp;
v_temp.val.u16 = kReadingCctTempMireds;
attribute::update(endpoint_id, ColorControl::Id, ColorControl::Attributes::ColorTemperatureMireds::Id, &v_temp);
}
chip::app::Clusters::ScenesManagement::ScenesServer::Instance().StoreCurrentScene(fabric, endpoint_id, kReadingSceneGroupId,
kReadingSceneId);
// Restore previous attribute values.
attribute::update(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id, &prev_onoff);
attribute::update(endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, &prev_level);
if (with_color_temp && temp_attr) {
attribute::update(endpoint_id, ColorControl::Id, ColorControl::Attributes::ColorTemperatureMireds::Id, &prev_temp);
}
s_suppress_driver_updates = false;
};
store_for_endpoint(rgb_light_endpoint_id, false);
store_for_endpoint(cct_light_endpoint_id, true);
s_reading_scene_stored_for_fabric[fabric] = true;
ESP_LOGI(TAG, "Reading scene stored: group=0 scene=1 for fabric %u", (unsigned)fabric);
}
static void handle_brightness_key(uint8_t cmd)
{
const uint32_t now_ms = esp_log_timestamp();
// If we are in color temperature mode, keep it active until we observe a real "release" gap.
// While in this mode, any incoming brightness key packets (up/down) are treated as temperature steps.
if (s_cct_color_mode_active) {
if ((now_ms - s_cct_last_repeat_ms) > kCctPressReleaseGapMs) {
s_cct_color_mode_active = false;
s_cct_repeat_count = 0;
s_cct_brightness_applied = false;
s_cct_brightness_applied_delta = 0;
} else {
s_cct_last_repeat_ms = now_ms;
if (cct_light_endpoint_id) {
if (cmd == kCctCoolCmd) {
matter_adjust_color_temperature_mireds(cct_light_endpoint_id, -kCctTempStepMireds);
} else if (cmd == kCctWarmCmd) {
matter_adjust_color_temperature_mireds(cct_light_endpoint_id, +kCctTempStepMireds);
}
}
return;
}
}
// For short presses we want fast response: allow a new short press after a small gap.
const bool new_sequence = (cmd != s_cct_last_cmd) || ((now_ms - s_cct_last_cmd_ms) > kCctShortPressGapMs);
if (new_sequence) {
// New press: allow one brightness step again.
s_cct_last_cmd = cmd;
s_cct_repeat_count = 1;
s_cct_brightness_applied = false;
s_cct_brightness_applied_delta = 0;
} else {
// Same press: only count repeat if it is within the repeat gap.
if ((now_ms - s_cct_last_cmd_ms) <= kCctLongPressRepeatGapMs) {
if (s_cct_repeat_count < 255) {
s_cct_repeat_count++;
}
}
}
s_cct_last_cmd_ms = now_ms;
if (!s_cct_color_mode_active && s_cct_repeat_count >= kCctLongPressMinRepeats) {
s_cct_color_mode_active = true;
s_cct_last_repeat_ms = now_ms;
if (s_cct_brightness_applied && s_cct_brightness_applied_delta != 0) {
if (rgb_light_endpoint_id) {
matter_adjust_brightness(rgb_light_endpoint_id, -s_cct_brightness_applied_delta);
}
if (cct_light_endpoint_id) {
matter_adjust_brightness(cct_light_endpoint_id, -s_cct_brightness_applied_delta);
}
s_cct_brightness_applied = false;
s_cct_brightness_applied_delta = 0;
}
ESP_LOGI(TAG, "CCT long press detected -> color temperature mode");
}
if (s_cct_color_mode_active) {
// Apply one temperature step immediately on the packet that triggers long-press.
s_cct_last_repeat_ms = now_ms;
if (cct_light_endpoint_id) {
if (cmd == kCctCoolCmd) {
matter_adjust_color_temperature_mireds(cct_light_endpoint_id, -kCctTempStepMireds);
} else if (cmd == kCctWarmCmd) {
matter_adjust_color_temperature_mireds(cct_light_endpoint_id, +kCctTempStepMireds);
}
}
return;
}
// Short press: apply only one brightness step for both endpoints.
if (!s_cct_brightness_applied) {
if (cmd == kEspNowCmdBrightnessUp) {
s_cct_brightness_applied_delta = kBrightnessStep;
s_cct_brightness_applied = true;
if (rgb_light_endpoint_id) {
matter_adjust_brightness(rgb_light_endpoint_id, kBrightnessStep);
}
if (cct_light_endpoint_id) {
matter_adjust_brightness(cct_light_endpoint_id, kBrightnessStep);
}
} else if (cmd == kEspNowCmdBrightnessDown) {
s_cct_brightness_applied_delta = -static_cast<int>(kBrightnessStep);
s_cct_brightness_applied = true;
if (rgb_light_endpoint_id) {
matter_adjust_brightness(rgb_light_endpoint_id, -static_cast<int>(kBrightnessStep));
}
if (cct_light_endpoint_id) {
matter_adjust_brightness(cct_light_endpoint_id, -static_cast<int>(kBrightnessStep));
}
}
}
}
static constexpr uint8_t kBootsToPair = 5;
static constexpr uint8_t kBootsToFactoryReset = 3;
static constexpr uint32_t kBootWindowMs = 60000; // 60 seconds window for 3 reboots
static constexpr uint32_t kPairingConfirmDelayMs = 5000;
static constexpr char kBootNvsNamespace[] = "boot_config";
static constexpr char kBootNvsKeyPair[] = "bootCount";
static constexpr char kBootNvsKeyFactory[] = "bootCntFactory";
static constexpr uint32_t kPairingBeaconMagic = 0xABCDEF01;
static bool s_espnow_inited = false;
static bool s_pairing_mode_active = false;
static bool s_boot_counter_reset_pending = false;
static bool s_factory_reset_pending = false;
static bool s_wifi_inited_for_espnow = false;
static bool s_matter_started = false;
static bool s_wifi_sta_connected = false;
static bool s_has_ipv4 = false;
static bool s_has_ipv6 = false;
static bool s_commissioned = false;
static bool s_commissioning_window_open = false;
static uint8_t g_espnow_channel = 1;
static esp_timer_handle_t s_boot_counter_reset_timer = nullptr;
static esp_timer_handle_t s_pairing_confirm_timer = nullptr;
static esp_timer_handle_t s_factory_reset_confirm_timer = nullptr;
static esp_timer_handle_t s_pairing_beacon_timer = nullptr;
static esp_timer_handle_t s_espnow_retry_timer = nullptr;
static void espnow_try_init();
static void factory_reset_work(intptr_t arg);
struct EspNowCmdPacket {
uint8_t cmd;
uint8_t src_addr[6];
};
static TaskHandle_t s_http_ota_task_handle = nullptr;
static void http_ota_progress_cb(int progress)
{
ESP_LOGI(TAG, "HTTP OTA progress: %d%%", progress);
}
static void http_ota_task(void *arg)
{
char *url = static_cast<char *>(arg);
OtaManager *ota = OtaManager::GetInstance();
ota->init();
ota->setProgressCallback(http_ota_progress_cb);
if (!url) {
ESP_LOGE(TAG, "HTTP OTA: url is null");
} else {
ESP_LOGW(TAG, "HTTP OTA starting: %s", url);
bool ok = ota->performOta(url, true);
if (!ok) {
ESP_LOGE(TAG, "HTTP OTA failed: %s", ota->getLastError().c_str());
}
}
if (url) {
free(url);
}
s_http_ota_task_handle = nullptr;
vTaskDelete(nullptr);
}
static esp_err_t http_ota_dispatch(int argc, char **argv)
{
if (argc < 1) {
ESP_LOGI(TAG, "Usage: matter esp ota <http://host/path/firmware.bin>");
return ESP_OK;
}
if (s_http_ota_task_handle) {
ESP_LOGE(TAG, "HTTP OTA already running");
return ESP_FAIL;
}
char *url_copy = strdup(argv[0]);
if (!url_copy) {
ESP_LOGE(TAG, "HTTP OTA: strdup failed");
return ESP_ERR_NO_MEM;
}
BaseType_t ret = xTaskCreate(&http_ota_task, "http_ota", 16384, url_copy, 2, &s_http_ota_task_handle);
if (ret != pdPASS) {
ESP_LOGE(TAG, "HTTP OTA: failed to create task");
free(url_copy);
s_http_ota_task_handle = nullptr;
return ESP_FAIL;
}
return ESP_OK;
}
static void log_app_state(const char *reason)
{
ESP_LOGW(TAG,
"[APP_STATE] %s matter=%d wifi_conn=%d ip4=%d ip6=%d commissioned=%d cw_open=%d espnow=%d ch=%u pairing=%d",
reason ? reason : "(null)", (int)s_matter_started, (int)s_wifi_sta_connected, (int)s_has_ipv4,
(int)s_has_ipv6, (int)s_commissioned, (int)s_commissioning_window_open, (int)s_espnow_inited,
(unsigned)g_espnow_channel, (int)s_pairing_mode_active);
}
static void handle_espnow_cmd(intptr_t arg)
{
EspNowCmdPacket *pkt = reinterpret_cast<EspNowCmdPacket *>(arg);
if (!pkt) {
return;
}
if (s_pairing_mode_active) {
if (pkt->cmd == kEspNowCmdPairingConfirm) {
ESP_LOGI(TAG, "ESP-NOW pairing confirmed (0xA5). Exiting pairing mode.");
s_pairing_mode_active = false;
if (s_pairing_beacon_timer) {
esp_timer_stop(s_pairing_beacon_timer);
}
}
free(pkt);
return;
}
switch (pkt->cmd) {
case kEspNowCmdToggle:
if (rgb_light_endpoint_id) {
matter_toggle_onoff(rgb_light_endpoint_id);
}
if (cct_light_endpoint_id) {
matter_toggle_onoff(cct_light_endpoint_id);
}
break;
case kEspNowCmdOn:
if (rgb_light_endpoint_id) {
matter_set_onoff(rgb_light_endpoint_id, true);
}
if (cct_light_endpoint_id) {
matter_set_onoff(cct_light_endpoint_id, true);
}
break;
case kEspNowCmdOff:
if (rgb_light_endpoint_id) {
matter_set_onoff(rgb_light_endpoint_id, false);
}
if (cct_light_endpoint_id) {
matter_set_onoff(cct_light_endpoint_id, false);
}
break;
case kEspNowCmdBrightnessUp:
handle_brightness_key(pkt->cmd);
break;
case kEspNowCmdBrightnessDown:
handle_brightness_key(pkt->cmd);
break;
default:
break;
}
free(pkt);
}
static void espnow_recv_cb(const esp_now_recv_info_t *recv_info, const uint8_t *data, int len)
{
if (!recv_info || !data || len < 1) {
return;
}
ESP_LOGI(TAG, "ESP-NOW RX: cmd=0x%02X len=%d from %02X:%02X:%02X:%02X:%02X:%02X", data[0], len, recv_info->src_addr[0],
recv_info->src_addr[1], recv_info->src_addr[2], recv_info->src_addr[3], recv_info->src_addr[4], recv_info->src_addr[5]);
EspNowCmdPacket *pkt = reinterpret_cast<EspNowCmdPacket *>(calloc(1, sizeof(EspNowCmdPacket)));
if (!pkt) {
return;
}
pkt->cmd = data[0];
memcpy(pkt->src_addr, recv_info->src_addr, sizeof(pkt->src_addr));
chip::DeviceLayer::PlatformMgr().ScheduleWork(handle_espnow_cmd, reinterpret_cast<intptr_t>(pkt));
}
static void pairing_beacon_timer_cb(void *arg)
{
(void)arg;
if (!s_pairing_mode_active || !s_espnow_inited) {
return;
}
uint8_t bcast[6];
memset(bcast, 0xFF, sizeof(bcast));
uint32_t beacon = kPairingBeaconMagic;
esp_now_send(bcast, reinterpret_cast<uint8_t *>(&beacon), sizeof(beacon));
}
static void start_pairing_beacon()
{
if (!s_espnow_inited) {
ESP_LOGW(TAG, "ESP-NOW not initialized yet, trying to init...");
espnow_try_init();
if (!s_espnow_inited) {
ESP_LOGE(TAG, "Failed to initialize ESP-NOW for pairing beacon");
return;
}
}
esp_now_peer_info_t broadcast_peer = {};
memset(broadcast_peer.peer_addr, 0xFF, sizeof(broadcast_peer.peer_addr));
broadcast_peer.channel = g_espnow_channel;
broadcast_peer.encrypt = false;
esp_now_add_peer(&broadcast_peer);
esp_timer_create_args_t targs = {
.callback = &pairing_beacon_timer_cb,
.arg = nullptr,
.dispatch_method = ESP_TIMER_TASK,
.name = "espnow_beacon",
.skip_unhandled_events = true,
};
if (!s_pairing_beacon_timer && esp_timer_create(&targs, &s_pairing_beacon_timer) == ESP_OK) {
esp_timer_start_periodic(s_pairing_beacon_timer, 200 * 1000);
} else if (s_pairing_beacon_timer) {
esp_timer_start_periodic(s_pairing_beacon_timer, 200 * 1000);
}
}
extern "C" void app_enter_espnow_pairing_mode()
{
if (s_pairing_mode_active) {
ESP_LOGW(TAG, "Already in ESP-NOW pairing mode");
return;
}
s_pairing_mode_active = true;
ESP_LOGW(TAG, "\n\n>>> ESP-NOW PAIRING MODE ACTIVE (button long press) <<<\n");
ESP_LOGW(TAG, "Broadcasting beacon 0x%08lX on channel %u", (unsigned long)kPairingBeaconMagic, (unsigned)g_espnow_channel);
start_pairing_beacon();
}
static void boot_counter_reset_timer_cb(void *arg)
{
(void)arg;
if (!s_boot_counter_reset_pending) {
return;
}
nvs_handle_t h;
if (nvs_open(kBootNvsNamespace, NVS_READWRITE, &h) == ESP_OK) {
nvs_set_u8(h, kBootNvsKeyPair, 0);
nvs_set_u8(h, kBootNvsKeyFactory, 0);
nvs_commit(h);
nvs_close(h);
}
s_boot_counter_reset_pending = false;
ESP_LOGW(TAG, ">>> bootCount RESET to 0 (30s window expired) <<<");
}
static void pairing_confirm_timer_cb(void *arg)
{
(void)arg;
if (s_factory_reset_pending) {
return;
}
if (s_pairing_mode_active) {
return;
}
s_pairing_mode_active = true;
ESP_LOGW(TAG, "ESP-NOW pairing mode ACTIVE (power-cycle trigger)");
ESP_LOGW(TAG, "Broadcasting beacon 0x%08lX on channel %u", (unsigned long)kPairingBeaconMagic, (unsigned)g_espnow_channel);
start_pairing_beacon();
}
static void factory_reset_confirm_timer_cb(void *arg)
{
(void)arg;
if (s_pairing_mode_active) {
return;
}
s_factory_reset_pending = true;
ESP_LOGW(TAG, "Factory reset CONFIRMED (power-cycle trigger)");
nvs_handle_t h;
if (nvs_open(kBootNvsNamespace, NVS_READWRITE, &h) == ESP_OK) {
nvs_set_u8(h, kBootNvsKeyPair, 0);
nvs_set_u8(h, kBootNvsKeyFactory, 0);
nvs_commit(h);
nvs_close(h);
}
if (s_matter_started) {
chip::DeviceLayer::PlatformMgr().ScheduleWork(factory_reset_work, 0);
}
}
static void pairing_boot_counter_init()
{
bool schedule_factory_reset = false;
bool schedule_pairing = false;
uint8_t boot_count = 0;
nvs_handle_t h;
const esp_err_t open_ret = nvs_open(kBootNvsNamespace, NVS_READWRITE, &h);
if (open_ret == ESP_OK) {
// Use a single boot counter to avoid pairing/factory reset conflicts.
esp_err_t get_ret = nvs_get_u8(h, kBootNvsKeyFactory, &boot_count);
esp_err_t get_pair_ret = ESP_OK;
if (get_ret != ESP_OK) {
get_pair_ret = nvs_get_u8(h, kBootNvsKeyPair, &boot_count);
if (get_pair_ret == ESP_OK) {
get_ret = get_pair_ret;
}
}
boot_count++;
if (boot_count == kBootsToFactoryReset) {
schedule_factory_reset = true;
} else if (boot_count == kBootsToPair) {
schedule_pairing = true;
boot_count = 0;
} else if (boot_count > kBootsToPair) {
boot_count = 0;
}
// Keep the legacy key in sync for compatibility with existing NVS content.
const esp_err_t set_pair_ret = nvs_set_u8(h, kBootNvsKeyPair, boot_count);
const esp_err_t set_factory_ret = nvs_set_u8(h, kBootNvsKeyFactory, boot_count);
const esp_err_t commit_ret = nvs_commit(h);
nvs_close(h);
ESP_LOGW(TAG,
"\n\n>>> bootCount: %u (pair=%u, factory=%u, within %lu ms, nvs_get=%d, nvs_get_pair=%d, nvs_set_pair=%d, nvs_set_factory=%d, nvs_commit=%d) <<<\n",
(unsigned)boot_count, (unsigned)kBootsToPair, (unsigned)kBootsToFactoryReset, (unsigned long)kBootWindowMs,
(int)get_ret, (int)get_pair_ret, (int)set_pair_ret, (int)set_factory_ret, (int)commit_ret);
} else {
ESP_LOGE(TAG, "bootCount: nvs_open(%s) failed: %d", kBootNvsNamespace, (int)open_ret);
}
s_boot_counter_reset_pending = (!schedule_factory_reset) && (boot_count > 0);
if (schedule_factory_reset) {
esp_timer_create_args_t targs = {
.callback = &factory_reset_confirm_timer_cb,
.arg = nullptr,
.dispatch_method = ESP_TIMER_TASK,
.name = "fac_confirm",
.skip_unhandled_events = true,
};
if (!s_factory_reset_confirm_timer && esp_timer_create(&targs, &s_factory_reset_confirm_timer) == ESP_OK) {
esp_timer_start_once(s_factory_reset_confirm_timer, (int64_t)kPairingConfirmDelayMs * 1000);
} else if (s_factory_reset_confirm_timer) {
esp_timer_stop(s_factory_reset_confirm_timer);
esp_timer_start_once(s_factory_reset_confirm_timer, (int64_t)kPairingConfirmDelayMs * 1000);
}
ESP_LOGW(TAG, "Factory reset will start in %lu ms if no further power-cycles occur", (unsigned long)kPairingConfirmDelayMs);
} else if (schedule_pairing) {
// Defer entering pairing mode to avoid conflict with an in-progress factory-reset power-cycle sequence.
esp_timer_create_args_t targs = {
.callback = &pairing_confirm_timer_cb,
.arg = nullptr,
.dispatch_method = ESP_TIMER_TASK,
.name = "pair_confirm",
.skip_unhandled_events = true,
};
if (!s_pairing_confirm_timer && esp_timer_create(&targs, &s_pairing_confirm_timer) == ESP_OK) {
esp_timer_start_once(s_pairing_confirm_timer, (int64_t)kPairingConfirmDelayMs * 1000);
} else if (s_pairing_confirm_timer) {
esp_timer_stop(s_pairing_confirm_timer);
esp_timer_start_once(s_pairing_confirm_timer, (int64_t)kPairingConfirmDelayMs * 1000);
}
ESP_LOGW(TAG, "Pairing will start in %lu ms if no further power-cycles occur", (unsigned long)kPairingConfirmDelayMs);
} else if (s_boot_counter_reset_pending) {
esp_timer_create_args_t targs = {
.callback = &boot_counter_reset_timer_cb,
.arg = nullptr,
.dispatch_method = ESP_TIMER_TASK,
.name = "bootcnt_reset",
.skip_unhandled_events = true,
};
if (!s_boot_counter_reset_timer && esp_timer_create(&targs, &s_boot_counter_reset_timer) == ESP_OK) {
esp_timer_start_once(s_boot_counter_reset_timer, (int64_t)kBootWindowMs * 1000);
}
}
}
static void factory_reset_work(intptr_t arg)
{
(void)arg;
ESP_LOGW(TAG, ">>> Performing factory reset (power-cycle trigger) <<<");
esp_matter::factory_reset();
}
static void wifi_init_for_espnow()
{
if (s_wifi_inited_for_espnow) {
return;
}
wifi_mode_t mode = WIFI_MODE_NULL;
esp_err_t err = esp_wifi_get_mode(&mode);
if (err == ESP_OK && mode != WIFI_MODE_NULL) {
uint8_t primary = 0;
wifi_second_chan_t second = WIFI_SECOND_CHAN_NONE;
esp_wifi_get_channel(&primary, &second);
if (primary > 0) {
g_espnow_channel = primary; // Use Wi-Fi's current channel for ESP-NOW
ESP_LOGI(TAG, "Wi-Fi managed by Matter (mode=%d, channel=%u), ESP-NOW will use channel %u", (int)mode, primary, g_espnow_channel);
if (primary != 1) {
ESP_LOGW(TAG, ">>> Router channel is %u, remote must also use channel %u <<<", primary, primary);
}
}
s_wifi_inited_for_espnow = true;
return;
}
ESP_LOGI(TAG, "Wi-Fi not started by Matter yet, initializing for ESP-NOW (channel %u)", (unsigned)g_espnow_channel);
err = esp_netif_init();
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "esp_netif_init: %d", err);
}
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
err = esp_wifi_init(&cfg);
if (err != ESP_OK && err != ESP_ERR_WIFI_INIT_STATE) {
ESP_LOGE(TAG, "esp_wifi_init failed: %d", err);
return;
}
err = esp_wifi_set_mode(WIFI_MODE_STA);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_wifi_set_mode failed: %d", err);
return;
}
err = esp_wifi_start();
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_wifi_start failed: %d", err);
return;
}
err = esp_wifi_set_channel(g_espnow_channel, WIFI_SECOND_CHAN_NONE);
if (err != ESP_OK) {
ESP_LOGW(TAG, "esp_wifi_set_channel failed: %d", err);
} else {
ESP_LOGI(TAG, "Wi-Fi channel fixed to %u for ESP-NOW", (unsigned)g_espnow_channel);
}
s_wifi_inited_for_espnow = true;
}
static void espnow_try_init()
{
if (s_espnow_inited) {
return;
}
if (!s_wifi_inited_for_espnow) {
wifi_init_for_espnow();
}
wifi_mode_t mode = WIFI_MODE_NULL;
if (esp_wifi_get_mode(&mode) != ESP_OK || mode == WIFI_MODE_NULL) {
ESP_LOGW(TAG, "WiFi not ready yet (mode=%d), skip ESP-NOW init", (int)mode);
return;
}
uint8_t primary = 0;
wifi_second_chan_t second = WIFI_SECOND_CHAN_NONE;
esp_err_t ch_err = esp_wifi_get_channel(&primary, &second);
if (ch_err == ESP_OK && primary > 0) {
g_espnow_channel = primary; // Always use current Wi-Fi channel
ESP_LOGI(TAG, "WiFi channel=%u, ESP-NOW will use this channel", primary);
} else {
ESP_LOGW(TAG, "esp_wifi_get_channel failed (err=%d), using default channel %u", (int)ch_err, g_espnow_channel);
}
esp_err_t err = esp_now_init();
if (err != ESP_OK && err != ESP_ERR_ESPNOW_EXIST) {
ESP_LOGE(TAG, "esp_now_init failed, err:%d", err);
return;
}
esp_now_register_recv_cb(espnow_recv_cb);
if (s_pairing_mode_active) {
esp_now_peer_info_t broadcast_peer = {};
memset(broadcast_peer.peer_addr, 0xFF, sizeof(broadcast_peer.peer_addr));
broadcast_peer.channel = g_espnow_channel;
broadcast_peer.encrypt = false;
esp_now_add_peer(&broadcast_peer);
esp_timer_create_args_t targs = {
.callback = &pairing_beacon_timer_cb,
.arg = nullptr,
.dispatch_method = ESP_TIMER_TASK,
.name = "espnow_beacon",
.skip_unhandled_events = true,
};
if (!s_pairing_beacon_timer && esp_timer_create(&targs, &s_pairing_beacon_timer) == ESP_OK) {
esp_timer_start_periodic(s_pairing_beacon_timer, 200 * 1000);
}
}
s_espnow_inited = true;
ESP_LOGI(TAG, "ESP-NOW RX initialized");
}
static void espnow_retry_timer_cb(void *arg)
{
(void)arg;
if (!s_espnow_inited) {
espnow_try_init();
}
if (s_espnow_inited && s_espnow_retry_timer) {
esp_timer_stop(s_espnow_retry_timer);
}
}
static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
(void)arg;
(void)event_data;
if (event_base != WIFI_EVENT) {
return;
}
ESP_LOGI(TAG, "WIFI_EVENT id=%ld", (long)event_id);
// For Matter over Wi-Fi: wait until connected to init ESP-NOW with correct channel
if (event_id == WIFI_EVENT_STA_CONNECTED) {
s_wifi_sta_connected = true;
log_app_state("wifi_sta_connected");
uint8_t primary = 0;
wifi_second_chan_t second = WIFI_SECOND_CHAN_NONE;
if (esp_wifi_get_channel(&primary, &second) == ESP_OK && primary > 0) {
g_espnow_channel = primary;
ESP_LOGI(TAG, "WiFi connected on channel %u, initializing ESP-NOW", primary);
if (primary != 1) {
ESP_LOGW(TAG, ">>> Remote must use channel %u (not 1) to work! <<<", primary);
}
}
// Initialize ESP-NOW after Wi-Fi is connected with correct channel
espnow_try_init();
// Start pairing beacon if in pairing mode
if (s_pairing_mode_active && s_espnow_inited) {
ESP_LOGW(TAG, "Starting ESP-NOW pairing beacon on channel %u", g_espnow_channel);
start_pairing_beacon();
}
}
if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
s_wifi_sta_connected = false;
s_has_ipv4 = false;
s_has_ipv6 = false;
log_app_state("wifi_sta_disconnected");
}
// For AP mode (fallback)
if (event_id == WIFI_EVENT_AP_START) {
espnow_try_init();
}
}
static void ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
(void)arg;
if (event_base != IP_EVENT) {
return;
}
if (event_id == IP_EVENT_STA_GOT_IP) {
s_has_ipv4 = true;
log_app_state("ip_sta_got_ipv4");
} else if (event_id == IP_EVENT_STA_LOST_IP) {
s_has_ipv4 = false;
log_app_state("ip_sta_lost_ipv4");
} else if (event_id == IP_EVENT_GOT_IP6) {
s_has_ipv6 = true;
log_app_state("ip_got_ipv6");
}
#ifdef IP_EVENT_LOST_IP6
else if (event_id == IP_EVENT_LOST_IP6) {
s_has_ipv6 = false;
log_app_state("ip_lost_ipv6");
} else {
(void)event_data;
}
#else
else {
(void)event_data;
}
#endif
}
} // namespace
#ifdef CONFIG_ENABLE_SET_CERT_DECLARATION_API
extern const uint8_t cd_start[] asm("_binary_certification_declaration_der_start");
extern const uint8_t cd_end[] asm("_binary_certification_declaration_der_end");
const chip::ByteSpan cdSpan(cd_start, static_cast<size_t>(cd_end - cd_start));
#endif // CONFIG_ENABLE_SET_CERT_DECLARATION_API
#if CONFIG_ENABLE_ENCRYPTED_OTA
extern const char decryption_key_start[] asm("_binary_esp_image_encryption_key_pem_start");
extern const char decryption_key_end[] asm("_binary_esp_image_encryption_key_pem_end");
static const char *s_decryption_key = decryption_key_start;
static const uint16_t s_decryption_key_len = decryption_key_end - decryption_key_start;
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
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");
log_app_state("chip_if_ip_changed");
break;
case chip::DeviceLayer::DeviceEventType::kServerReady:
ESP_LOGI(TAG, "Server initialization complete");
chip::DeviceLayer::PlatformMgr().ScheduleWork(store_reading_scenes_for_all_fabrics, 0);
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningComplete:
ESP_LOGI(TAG, "Commissioning complete");
s_commissioned = true;
log_app_state("commissioning_complete");
MEMORY_PROFILER_DUMP_HEAP_STAT("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");
log_app_state("commissioning_session_started");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStopped:
ESP_LOGI(TAG, "Commissioning session stopped");
log_app_state("commissioning_session_stopped");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowOpened:
ESP_LOGI(TAG, "Commissioning window opened");
s_commissioning_window_open = true;
s_commissioned = false;
log_app_state("commissioning_window_opened");
MEMORY_PROFILER_DUMP_HEAP_STAT("commissioning window opened");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowClosed:
ESP_LOGI(TAG, "Commissioning window closed");
s_commissioning_window_open = false;
log_app_state("commissioning_window_closed");
break;
case chip::DeviceLayer::DeviceEventType::kFabricRemoved:
{
ESP_LOGI(TAG, "Fabric removed successfully");
s_commissioned = false;
log_app_state("fabric_removed");
if (chip::Server::GetInstance().GetFabricTable().FabricCount() == 0)
{
chip::CommissioningWindowManager & commissionMgr = chip::Server::GetInstance().GetCommissioningWindowManager();
constexpr auto kTimeoutSeconds = chip::System::Clock::Seconds16(k_timeout_seconds);
if (!commissionMgr.IsCommissioningWindowOpen())
{
/* After removing last fabric, this example does not remove the Wi-Fi credentials
* and still has IP connectivity so, only advertising on DNS-SD.
*/
CHIP_ERROR err = commissionMgr.OpenBasicCommissioningWindow(kTimeoutSeconds,
chip::CommissioningWindowAdvertisement::kDnssdOnly);
if (err != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "Failed to open commissioning window, err:%" CHIP_ERROR_FORMAT, err.Format());
}
}
}
break;
}
case chip::DeviceLayer::DeviceEventType::kFabricWillBeRemoved:
ESP_LOGI(TAG, "Fabric will be removed");
log_app_state("fabric_will_be_removed");
break;
case chip::DeviceLayer::DeviceEventType::kFabricUpdated:
ESP_LOGI(TAG, "Fabric is updated");
log_app_state("fabric_updated");
break;
case chip::DeviceLayer::DeviceEventType::kFabricCommitted:
ESP_LOGI(TAG, "Fabric is committed");
s_commissioned = true;
log_app_state("fabric_committed");
chip::DeviceLayer::PlatformMgr().ScheduleWork(store_reading_scenes_for_all_fabrics, 0);
break;
case chip::DeviceLayer::DeviceEventType::kBLEDeinitialized:
ESP_LOGI(TAG, "BLE deinitialized and memory reclaimed");
log_app_state("ble_deinit");
MEMORY_PROFILER_DUMP_HEAP_STAT("BLE deinitialized");
break;
default:
break;
}
}
static esp_err_t app_identification_cb(identification::callback_type_t type, uint16_t endpoint_id, uint8_t effect_id,
uint8_t effect_variant, void *priv_data)
{
ESP_LOGI(TAG, "Identification callback: type: %u, effect: %u, variant: %u", type, effect_id, effect_variant);
return ESP_OK;
}
static esp_err_t app_attribute_update_cb(attribute::callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id,
uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data)
{
esp_err_t err = ESP_OK;
if (type == PRE_UPDATE) {
/* Driver update */
app_driver_handle_t driver_handle = (app_driver_handle_t)priv_data;
if (!s_suppress_driver_updates) {
err = app_driver_attribute_update(driver_handle, endpoint_id, cluster_id, attribute_id, val);
}
}
return err;
}
#if CONFIG_ENABLE_OTA_REQUESTOR
static void ota_requestor_start_work(intptr_t arg)
{
(void) arg;
esp_matter_ota_requestor_start();
}
#endif
extern "C" void app_main()
{
esp_err_t err = ESP_OK;
/* Initialize the ESP NVS layer */
err = nvs_flash_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "nvs_flash_init failed: %d", (int)err);
}
pairing_boot_counter_init();
MEMORY_PROFILER_DUMP_HEAP_STAT("Bootup");
/* Initialize light drivers (button init deferred until after Matter start) */
app_driver_handle_t rgb_light_handle = app_driver_rgb_light_init();
app_driver_handle_t cct_light_handle = app_driver_cct_light_init();
/* Create a Matter node and add the mandatory Root Node device type on endpoint 0 */
node::config_t node_config;
// node handle can be used to add/modify other endpoints.
node_t *node = node::create(&node_config, app_attribute_update_cb, app_identification_cb);
ABORT_APP_ON_FAILURE(node != nullptr, ESP_LOGE(TAG, "Failed to create Matter node"));
#if CONFIG_ENABLE_OTA_REQUESTOR
err = esp_matter_ota_requestor_init();
ABORT_APP_ON_FAILURE(err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED,
ESP_LOGE(TAG, "Failed to init OTA Requestor, err:%d", (int)err));
#endif
MEMORY_PROFILER_DUMP_HEAP_STAT("node created");
dimmable_light::config_t rgb_light_config;
rgb_light_config.on_off.on_off = DEFAULT_POWER;
rgb_light_config.on_off_lighting.start_up_on_off = nullptr;
rgb_light_config.level_control.current_level = DEFAULT_BRIGHTNESS;
rgb_light_config.level_control.on_level = DEFAULT_BRIGHTNESS;
rgb_light_config.level_control_lighting.start_up_current_level = DEFAULT_BRIGHTNESS;
// endpoint handles can be used to add/modify clusters.
endpoint_t *endpoint = dimmable_light::create(node, &rgb_light_config, ENDPOINT_FLAG_NONE, rgb_light_handle);
ABORT_APP_ON_FAILURE(endpoint != nullptr, ESP_LOGE(TAG, "Failed to create dimmable light endpoint"));
rgb_light_endpoint_id = endpoint::get_id(endpoint);
ESP_LOGI(TAG, "RGB light created with endpoint_id %d", rgb_light_endpoint_id);
color_temperature_light::config_t cct_light_config;
cct_light_config.on_off.on_off = DEFAULT_POWER;
cct_light_config.on_off_lighting.start_up_on_off = nullptr;
cct_light_config.level_control.current_level = DEFAULT_BRIGHTNESS;
cct_light_config.level_control.on_level = DEFAULT_BRIGHTNESS;
cct_light_config.level_control_lighting.start_up_current_level = DEFAULT_BRIGHTNESS;
cct_light_config.color_control.color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature;
cct_light_config.color_control.enhanced_color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature;
cct_light_config.color_control_color_temperature.start_up_color_temperature_mireds = nullptr;
endpoint = color_temperature_light::create(node, &cct_light_config, ENDPOINT_FLAG_NONE, cct_light_handle);
ABORT_APP_ON_FAILURE(endpoint != nullptr, ESP_LOGE(TAG, "Failed to create color temperature light endpoint"));
cct_light_endpoint_id = endpoint::get_id(endpoint);
ESP_LOGI(TAG, "CCT light created with endpoint_id %d", cct_light_endpoint_id);
/* Mark deferred persistence for some attributes that might be changed rapidly */
attribute_t *current_level_attribute = attribute::get(rgb_light_endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id);
attribute::set_deferred_persistence(current_level_attribute);
current_level_attribute = attribute::get(cct_light_endpoint_id, LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id);
attribute::set_deferred_persistence(current_level_attribute);
attribute_t *color_temp_attribute = attribute::get(cct_light_endpoint_id, ColorControl::Id, ColorControl::Attributes::ColorTemperatureMireds::Id);
attribute::set_deferred_persistence(color_temp_attribute);
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD && CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION
// Enable secondary network interface
secondary_network_interface::config_t secondary_network_interface_config;
endpoint = endpoint::secondary_network_interface::create(node, &secondary_network_interface_config, ENDPOINT_FLAG_NONE, nullptr);
ABORT_APP_ON_FAILURE(endpoint != nullptr, ESP_LOGE(TAG, "Failed to create secondary network interface endpoint"));
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
/* Set OpenThread platform config */
esp_openthread_platform_config_t config = {
.radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
.host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
.port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
};
set_openthread_platform_config(&config);
#endif
#ifdef CONFIG_ENABLE_SET_CERT_DECLARATION_API
auto * dac_provider = get_dac_provider();
#ifdef CONFIG_SEC_CERT_DAC_PROVIDER
static_cast<ESP32SecureCertDACProvider *>(dac_provider)->SetCertificationDeclaration(cdSpan);
#elif defined(CONFIG_FACTORY_PARTITION_DAC_PROVIDER)
static_cast<ESP32FactoryDataProvider *>(dac_provider)->SetCertificationDeclaration(cdSpan);
#endif
#endif // CONFIG_ENABLE_SET_CERT_DECLARATION_API
/* 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));
#if CONFIG_ENABLE_OTA_REQUESTOR
chip::DeviceLayer::PlatformMgr().ScheduleWork(ota_requestor_start_work, 0);
#endif
s_matter_started = true;
log_app_state("matter_started");
if (s_factory_reset_pending) {
s_factory_reset_pending = false;
chip::DeviceLayer::PlatformMgr().ScheduleWork(factory_reset_work, 0);
while (true) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
PrintOnboardingCodes(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE));
/* Initialize Wi-Fi for ESP-NOW (independent of Matter, fixed channel 1) */
wifi_init_for_espnow();
espnow_try_init();
log_app_state("after_espnow_init");
err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, nullptr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_event_handler_register(WIFI_EVENT) failed, err:%d", (int)err);
}
err = esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &ip_event_handler, nullptr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_event_handler_register(IP_EVENT) failed, err:%d", (int)err);
}
MEMORY_PROFILER_DUMP_HEAP_STAT("matter started");
/* Initialize button after Matter is ready to avoid callback crash */
app_driver_handle_t button_handle = app_driver_button_init();
app_reset_button_register(button_handle);
/* Starting driver with default values */
app_driver_light_set_defaults(rgb_light_endpoint_id);
app_driver_light_set_defaults(cct_light_endpoint_id);
#if CONFIG_ENABLE_ENCRYPTED_OTA
err = esp_matter_ota_requestor_encrypted_init(s_decryption_key, s_decryption_key_len);
ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to initialized the encrypted OTA, err: %d", err));
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
#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::attribute_register_commands();
#if CONFIG_OPENTHREAD_CLI
esp_matter::console::otcli_register_commands();
#endif
static const esp_matter::console::command_t ota_cmd = {
.name = "ota",
.description = "Start HTTP OTA. Usage: matter esp ota <http://url>",
.handler = http_ota_dispatch,
};
esp_matter::console::add_commands(&ota_cmd, 1);
esp_matter::console::init();
#endif
while (true) {
MEMORY_PROFILER_DUMP_HEAP_STAT("Idle");
vTaskDelay(10000 / portTICK_PERIOD_MS);
}
}