blob: aa2b7d9d0b0a6cd35c548f23041a9647b0d82587 [file]
/*
*
* Copyright (c) 2025 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <app/DefaultSafeAttributePersistenceProvider.h>
#include <app/InteractionModelEngine.h>
#include <app/SafeAttributePersistenceProvider.h>
#include <app/persistence/DefaultAttributePersistenceProvider.h>
#include <app/server/Dnssd.h>
#include <app/server/Server.h>
#include <credentials/DeviceAttestationCredsProvider.h>
#include <credentials/examples/DeviceAttestationCredsExample.h>
#include <devices/device-factory/DeviceFactory.h>
#include <devices/root-node/WifiRootNodeDevice.h>
#include <esp_heap_caps.h>
#include <esp_log.h>
#include <esp_system.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <nvs_flash.h>
#include <platform/DiagnosticDataProvider.h>
#include <platform/ESP32/ESP32Config.h>
#include <platform/ESP32/ESP32Utils.h>
#include <platform/ESP32/NetworkCommissioningDriver.h>
#include <platform/PlatformManager.h>
#include <setup_payload/OnboardingCodesUtil.h>
#include <memory>
#include <string>
#if CONFIG_ENABLE_CHIP_SHELL
#include <DeviceShellCommands.h>
#include <lib/shell/commands/WiFi.h>
#include <shell_extension/launch.h>
#endif
#ifdef CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING
#include <platform/internal/BLEManager.h>
#endif
#if CONFIG_ENABLE_OTA_REQUESTOR
#include <ota/OTAHelper.h>
#endif
#if CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER
#include <platform/ESP32/ESP32FactoryDataProvider.h>
#endif // CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER
#if CONFIG_ENABLE_ESP32_DEVICE_INFO_PROVIDER
#include <platform/ESP32/ESP32DeviceInfoProvider.h>
#else
#include <DeviceInfoProviderImpl.h>
#endif // CONFIG_ENABLE_ESP32_DEVICE_INFO_PROVIDER
using namespace chip;
using namespace chip::app;
using namespace chip::DeviceLayer;
using namespace chip::Credentials;
using chip::DeviceLayer::Internal::ESP32Config;
static const char TAG[] = "all-devices-app";
// NVS key for storing the device type across reboots
static const ESP32Config::Key kConfigKey_DeviceType{ ESP32Config::kConfigNamespace_ChipConfig, "dev-type" };
static std::string gDeviceType = "contact-sensor";
static const size_t kMaxDeviceTypeLength = 64;
namespace {
// Use the singleton - platform event handlers report to GetInstance()
DeviceLayer::NetworkCommissioning::ESPWiFiDriver & sWiFiDriver = DeviceLayer::NetworkCommissioning::ESPWiFiDriver::GetInstance();
#if CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER
DeviceLayer::ESP32FactoryDataProvider sFactoryDataProvider;
#endif // CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER
#if CONFIG_ENABLE_ESP32_DEVICE_INFO_PROVIDER
DeviceLayer::ESP32DeviceInfoProvider gExampleDeviceInfoProvider;
#else
DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider;
#endif // CONFIG_ENABLE_ESP32_DEVICE_INFO_PROVIDER
chip::app::DefaultAttributePersistenceProvider gAttributePersistenceProvider;
chip::app::DefaultSafeAttributePersistenceProvider gSafeAttributePersistenceProvider;
Credentials::GroupDataProviderImpl gGroupDataProvider;
chip::app::CodeDrivenDataModelProvider * gDataModelProvider = nullptr;
std::unique_ptr<WifiRootNodeDevice> gRootNodeDevice;
std::unique_ptr<DeviceInterface> gConstructedDevice;
DefaultTimerDelegate gTimerDelegate;
void DeInitBLEIfCommissioned()
{
#ifdef CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING
static bool bleAlreadyShutdown = false;
if (chip::Server::GetInstance().GetFabricTable().FabricCount() > 0 && !bleAlreadyShutdown)
{
bleAlreadyShutdown = true;
chip::DeviceLayer::Internal::BLEMgr().Shutdown();
}
#endif
}
void DeviceEventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg)
{
switch (event->Type)
{
case DeviceLayer::DeviceEventType::kBLEDeinitialized:
ESP_LOGI(TAG, "BLE is deinitialized");
break;
case DeviceLayer::DeviceEventType::kInternetConnectivityChange:
if (event->InternetConnectivityChange.IPv4 == DeviceLayer::kConnectivity_Established)
{
ESP_LOGI(TAG, "IPv4 Server ready...");
chip::app::DnssdServer::Instance().StartServer();
}
else if (event->InternetConnectivityChange.IPv4 == DeviceLayer::kConnectivity_Lost)
{
ESP_LOGE(TAG, "Lost IPv4 connectivity...");
}
if (event->InternetConnectivityChange.IPv6 == DeviceLayer::kConnectivity_Established)
{
ESP_LOGI(TAG, "IPv6 Server ready...");
chip::app::DnssdServer::Instance().StartServer();
}
else if (event->InternetConnectivityChange.IPv6 == DeviceLayer::kConnectivity_Lost)
{
ESP_LOGE(TAG, "Lost IPv6 connectivity...");
}
break;
case DeviceLayer::DeviceEventType::kCHIPoBLEConnectionEstablished:
ESP_LOGI(TAG, "CHIPoBLE connection established");
break;
case DeviceLayer::DeviceEventType::kCHIPoBLEConnectionClosed:
ESP_LOGI(TAG, "CHIPoBLE disconnected");
break;
case DeviceLayer::DeviceEventType::kDnssdInitialized:
#if CONFIG_ENABLE_OTA_REQUESTOR
OTAHelpers::Instance().InitOTARequestor();
#endif
break;
case DeviceLayer::DeviceEventType::kCommissioningComplete:
ESP_LOGI(TAG, "Commissioning complete");
DeInitBLEIfCommissioned();
break;
case DeviceLayer::DeviceEventType::kInterfaceIpAddressChanged:
if ((event->InterfaceIpAddressChanged.Type == DeviceLayer::InterfaceIpChangeType::kIpV4_Assigned) ||
(event->InterfaceIpAddressChanged.Type == DeviceLayer::InterfaceIpChangeType::kIpV6_Assigned))
{
// MDNS server restart on any ip assignment: if link local ipv6 is configured, that
// will not trigger a 'internet connectivity change' as there is no internet
// connectivity. MDNS still wants to refresh its listening interfaces to include the
// newly selected address.
chip::app::DnssdServer::Instance().StartServer();
}
break;
default:
break;
}
ESP_LOGI(TAG, "Current free heap: Internal: %u/%u External: %u/%u",
static_cast<unsigned int>(heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL)),
static_cast<unsigned int>(heap_caps_get_total_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL)),
static_cast<unsigned int>(heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM)),
static_cast<unsigned int>(heap_caps_get_total_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM)));
}
chip::app::DataModel::Provider * PopulateCodeDrivenDataModelProvider(PersistentStorageDelegate * delegate,
TestEventTriggerDelegate * testEventTriggerDelegate)
{
// Initialize the attribute persistence provider with the storage delegate
CHIP_ERROR err = gAttributePersistenceProvider.Init(delegate);
if (err != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "Failed to init attribute persistence provider: %" CHIP_ERROR_FORMAT, err.Format());
return nullptr;
}
// Initialize the safe attribute persistence provider with the storage delegate
err = gSafeAttributePersistenceProvider.Init(delegate);
if (err != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "Failed to init safe attribute persistence provider: %" CHIP_ERROR_FORMAT, err.Format());
return nullptr;
}
SetSafeAttributePersistenceProvider(&gSafeAttributePersistenceProvider);
static chip::app::CodeDrivenDataModelProvider dataModelProvider =
chip::app::CodeDrivenDataModelProvider(*delegate, gAttributePersistenceProvider);
gDataModelProvider = &dataModelProvider;
DeviceLayer::DeviceInstanceInfoProvider * provider = DeviceLayer::GetDeviceInstanceInfoProvider();
if (provider == nullptr)
{
ESP_LOGE(TAG, "Failed to get DeviceInstanceInfoProvider.");
return nullptr;
}
gRootNodeDevice = std::make_unique<WifiRootNodeDevice>(
RootNodeDevice::Context {
.commissioningWindowManager = Server::GetInstance().GetCommissioningWindowManager(), //
.configurationManager = DeviceLayer::ConfigurationMgr(), //
.deviceControlServer = DeviceLayer::DeviceControlServer::DeviceControlSvr(), //
.fabricTable = Server::GetInstance().GetFabricTable(), //
.accessControl = Server::GetInstance().GetAccessControl(), //
.persistentStorage = Server::GetInstance().GetPersistentStorage(), //
.failSafeContext = Server::GetInstance().GetFailSafeContext(), //
.deviceInstanceInfoProvider = *provider, //
.platformManager = DeviceLayer::PlatformMgr(), //
.groupDataProvider = gGroupDataProvider, //
.sessionManager = Server::GetInstance().GetSecureSessionManager(), //
.dnssdServer = DnssdServer::Instance(), //
.deviceLoadStatusProvider = *InteractionModelEngine::GetInstance(), //
.diagnosticDataProvider = DeviceLayer::GetDiagnosticDataProvider(), //
.testEventTriggerDelegate = testEventTriggerDelegate, //
.dacProvider = *Credentials::GetDeviceAttestationCredentialsProvider(), //
.eventManagement = EventManagement::GetInstance(), //
.safeAttributePersistenceProvider = gSafeAttributePersistenceProvider, //
.timerDelegate = gTimerDelegate, //
#if CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED
.termsAndConditionsProvider = TermsAndConditionsManager::GetInstance(),
#endif // CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED
},
WifiRootNodeDevice::WifiContext{
.wifiDriver = sWiFiDriver,
});
err = gRootNodeDevice->Register(kRootEndpointId, dataModelProvider, kInvalidEndpointId);
if (err != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "Failed to register root node device: %" CHIP_ERROR_FORMAT, err.Format());
return nullptr;
}
// Default to contact-sensor device type
const char * deviceType = gDeviceType.c_str();
gConstructedDevice = DeviceFactory::GetInstance().Create(deviceType);
if (gConstructedDevice == nullptr)
{
ESP_LOGE(TAG, "Failed to create device of type: %s", deviceType);
return nullptr;
}
err = gConstructedDevice->Register(CONFIG_ALL_DEVICES_ENDPOINT, dataModelProvider, kInvalidEndpointId);
if (err != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "Failed to register device: %" CHIP_ERROR_FORMAT, err.Format());
return nullptr;
}
return &dataModelProvider;
}
void InitServer(intptr_t context)
{
DeviceFactory::GetInstance().Init(DeviceFactory::Context{
.groupDataProvider = gGroupDataProvider, //
.fabricTable = Server::GetInstance().GetFabricTable(), //
.timerDelegate = gTimerDelegate, //
});
static chip::CommonCaseDeviceServerInitParams initParams;
CHIP_ERROR err = initParams.InitializeStaticResourcesBeforeServerInit();
if (err != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "InitializeStaticResourcesBeforeServerInit() failed: %" CHIP_ERROR_FORMAT, err.Format());
return;
}
initParams.dataModelProvider =
PopulateCodeDrivenDataModelProvider(initParams.persistentStorageDelegate, initParams.testEventTriggerDelegate);
initParams.operationalServicePort = CHIP_PORT;
initParams.userDirectedCommissioningPort = CHIP_UDC_PORT;
if (initParams.dataModelProvider == nullptr)
{
ESP_LOGE(TAG, "Failed to populate data model provider");
return;
}
// Save device type to NVS only after successful device creation
err = ESP32Config::WriteConfigValueStr(kConfigKey_DeviceType, gDeviceType.c_str());
if (err != CHIP_NO_ERROR)
{
ESP_LOGW(TAG, "Failed to save device type to NVS: %" CHIP_ERROR_FORMAT, err.Format());
}
else
{
ESP_LOGI(TAG, "Device type '%s' saved to NVS", gDeviceType.c_str());
}
err = Server::GetInstance().Init(initParams);
if (err != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "Server init failed: %" CHIP_ERROR_FORMAT, err.Format());
return;
}
#if CHIP_DEVICE_CONFIG_ENABLE_WIFI && CONFIG_ENABLE_CHIP_SHELL
chip::Shell::SetWiFiDriver(&sWiFiDriver);
#endif
}
} // namespace
void InitServerWithDeviceType(std::string deviceType)
{
// Set the device type (store the actual string, not a pointer to temporary)
gDeviceType = std::move(deviceType);
// Init the server
SuccessOrDie(PlatformMgr().ScheduleWork(InitServer, reinterpret_cast<intptr_t>(nullptr)));
}
extern "C" void app_main()
{
// Initialize the ESP NVS layer.
esp_err_t err = nvs_flash_init();
if (err != ESP_OK)
{
ESP_LOGE(TAG, "nvs_flash_init() failed: %s", esp_err_to_name(err));
return;
}
err = esp_event_loop_create_default();
if (err != ESP_OK)
{
ESP_LOGE(TAG, "esp_event_loop_create_default() failed: %s", esp_err_to_name(err));
return;
}
ESP_LOGI(TAG, "==================================================");
ESP_LOGI(TAG, "chip-esp32-all-devices-app starting");
ESP_LOGI(TAG, "==================================================");
#if CHIP_DEVICE_CONFIG_ENABLE_WIFI
if (DeviceLayer::Internal::ESP32Utils::InitWiFiStack() != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "Failed to initialize WiFi stack");
return;
}
#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI
DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider);
CHIP_ERROR error = chip::Platform::MemoryInit();
if (error != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "Platform::MemoryInit() failed: %" CHIP_ERROR_FORMAT, error.Format());
return;
}
error = PlatformMgr().InitChipStack();
if (error != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "PlatformMgr().InitChipStack() failed: %" CHIP_ERROR_FORMAT, error.Format());
return;
}
// Register event handler to restart DNS-SD on connectivity change
SuccessOrDie(PlatformMgr().AddEventHandler(DeviceEventHandler, 0));
#if CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER
SetCommissionableDataProvider(&sFactoryDataProvider);
#if CONFIG_ENABLE_ESP32_DEVICE_INSTANCE_INFO_PROVIDER
SetDeviceInstanceInfoProvider(&sFactoryDataProvider);
#endif
SetDeviceAttestationCredentialsProvider(&sFactoryDataProvider);
#else
SetDeviceAttestationCredentialsProvider(Examples::GetExampleDACProvider());
#endif // CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER
error = PlatformMgr().StartEventLoopTask();
if (error != CHIP_NO_ERROR)
{
ESP_LOGE(TAG, "PlatformMgr().StartEventLoopTask() failed: %" CHIP_ERROR_FORMAT, error.Format());
return;
}
// Check if device type is stored in NVS from a previous boot
char storedDeviceType[kMaxDeviceTypeLength] = { 0 };
size_t storedLen = 0;
CHIP_ERROR nvsErr =
ESP32Config::ReadConfigValueStr(kConfigKey_DeviceType, storedDeviceType, sizeof(storedDeviceType), storedLen);
#if CONFIG_ENABLE_CHIP_SHELL
chip::LaunchShell();
chip::Shell::DeviceCommands::GetInstance().Register();
#endif // CONFIG_ENABLE_CHIP_SHELL
if (nvsErr == CHIP_NO_ERROR && storedLen > 0)
{
ESP_LOGI(TAG, "==================================================");
ESP_LOGI(TAG, "Found stored device type: %s", storedDeviceType);
ESP_LOGI(TAG, "Auto-initializing...");
ESP_LOGI(TAG, "==================================================");
InitServerWithDeviceType(std::string(storedDeviceType));
}
else
{
ESP_LOGI(TAG, "==================================================");
ESP_LOGI(TAG, "No stored device type found.");
ESP_LOGI(TAG, "Use command: devtype set <device-type>");
ESP_LOGI(TAG, "==================================================");
}
}