[ESP32] Fix build and crash and add DeviceManagementCluster, EVSE mode and DeviceManagementCluster mode. (#31591)
* [ESP32] Fix build failure and Add DeviceEnergyManagement clusters initialization
* Fix crash
* Support EVSE and Device Energy Management Mode
* Sync esp32 with linux app
diff --git a/.github/workflows/examples-esp32.yaml b/.github/workflows/examples-esp32.yaml
index 47908d6..2e5432d 100644
--- a/.github/workflows/examples-esp32.yaml
+++ b/.github/workflows/examples-esp32.yaml
@@ -165,3 +165,6 @@
- name: Build example Lighting App (external platform)
run: scripts/examples/esp_example.sh lighting-app sdkconfig.ext_plat.defaults
+
+ - name: Build example Energy Management App
+ run: scripts/examples/esp_example.sh energy-management-app sdkconfig.defaults
diff --git a/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp b/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp
index 710b13f..2464388 100644
--- a/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp
+++ b/examples/energy-management-app/energy-management-common/src/EnergyEvseManager.cpp
@@ -27,7 +27,12 @@
{
SafeAttributePersistenceProvider * aProvider = GetSafeAttributePersistenceProvider();
- EndpointId aEndpointId = mDelegate->GetEndpointId();
+ if (aProvider == nullptr)
+ {
+ ChipLogError(AppServer, "GetSafeAttributePersistenceProvider returned NULL");
+ return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
+ }
+ EndpointId aEndpointId = mDelegate->GetEndpointId();
CHIP_ERROR err;
// Restore ChargingEnabledUntil value
diff --git a/examples/energy-management-app/esp32/main/CMakeLists.txt b/examples/energy-management-app/esp32/main/CMakeLists.txt
index 1c7f2a5..46f97d7 100644
--- a/examples/energy-management-app/esp32/main/CMakeLists.txt
+++ b/examples/energy-management-app/esp32/main/CMakeLists.txt
@@ -50,6 +50,7 @@
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/time-format-localization-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/barrier-control-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/energy-evse-server"
+ "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/device-energy-management-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/occupancy-sensor-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server"
@@ -63,6 +64,7 @@
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/ota-requestor"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/groups-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/group-key-mgmt-server"
+ "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/mode-base-server"
)
set(PRIV_REQUIRES_LIST chip QRCode bt led_strip app_update openthread driver nvs_flash spi_flash)
diff --git a/examples/energy-management-app/esp32/main/main.cpp b/examples/energy-management-app/esp32/main/main.cpp
index 77899a4..fd79e60 100644
--- a/examples/energy-management-app/esp32/main/main.cpp
+++ b/examples/energy-management-app/esp32/main/main.cpp
@@ -16,9 +16,12 @@
*/
#include "DeviceCallbacks.h"
+#include <DeviceEnergyManagementManager.h>
#include <EVSEManufacturerImpl.h>
#include <EnergyEvseManager.h>
#include <EnergyManagementManager.h>
+#include <device-energy-management-modes.h>
+#include <energy-evse-modes.h>
#include "esp_log.h"
#include <common/CHIPDeviceManager.h>
@@ -74,9 +77,11 @@
using namespace ::chip::DeviceManager;
using namespace ::chip::DeviceLayer;
-static EnergyEvseDelegate * gDelegate = nullptr;
-static EnergyEvseManager * gInstance = nullptr;
-static EVSEManufacturer * gEvseManufacturer = nullptr;
+static std::unique_ptr<EnergyEvseDelegate> gEvseDelegate;
+static std::unique_ptr<EnergyEvseManager> gEvseInstance;
+static std::unique_ptr<DeviceEnergyManagementDelegate> gDEMDelegate;
+static std::unique_ptr<DeviceEnergyManagementManager> gDEMInstance;
+static std::unique_ptr<EVSEManufacturer> gEvseManufacturer;
#if CONFIG_ENABLE_ESP_INSIGHTS_TRACE
extern const char insights_auth_key_start[] asm("_binary_insights_auth_key_txt_start");
@@ -116,44 +121,238 @@
} // namespace
-void ApplicationInit()
+EVSEManufacturer * EnergyEvse::GetEvseManufacturer()
{
- if ((gDelegate == nullptr) && (gInstance == nullptr) && (gEvseManufacturer == nullptr))
+ return gEvseManufacturer.get();
+}
+
+/*
+ * @brief Creates a Delegate and Instance for DEM
+ *
+ * The Instance is a container around the Delegate, so
+ * create the Delegate first, then wrap it in the Instance
+ * Then call the Instance->Init() to register the attribute and command handlers
+ */
+CHIP_ERROR DeviceEnergyManagementInit()
+{
+ if (gDEMDelegate || gDEMInstance)
{
- gDelegate = new EnergyEvseDelegate();
- if (gDelegate != nullptr)
- {
- gInstance = new EnergyEvseManager(
- EndpointId(ENERGY_EVSE_ENDPOINT), *gDelegate,
- BitMask<EnergyEvse::Feature, uint32_t>(EnergyEvse::Feature::kChargingPreferences,
- EnergyEvse::Feature::kPlugAndCharge, EnergyEvse::Feature::kRfid,
- EnergyEvse::Feature::kSoCReporting, EnergyEvse::Feature::kV2x),
- BitMask<OptionalAttributes, uint32_t>(OptionalAttributes::kSupportsUserMaximumChargingCurrent,
- OptionalAttributes::kSupportsRandomizationWindow,
- OptionalAttributes::kSupportsApproximateEvEfficiency),
- BitMask<OptionalCommands, uint32_t>(OptionalCommands::kSupportsStartDiagnostics));
- gInstance->Init(); /* Register Attribute & Command handlers */
- }
- }
- else
- {
- ChipLogError(AppServer, "EVSE Instance or Delegate already exist.")
+ ESP_LOGE(TAG, "DEM Instance or Delegate already exist.");
+ return CHIP_ERROR_INCORRECT_STATE;
}
- if (gEvseManufacturer == nullptr)
+ gDEMDelegate = std::make_unique<DeviceEnergyManagementDelegate>();
+ if (!gDEMDelegate)
{
- gEvseManufacturer = new EVSEManufacturer();
- gEvseManufacturer->Init(gInstance);
+ ESP_LOGE(TAG, "Failed to allocate memory for DeviceEnergyManagementDelegate");
+ return CHIP_ERROR_NO_MEMORY;
}
- else
+
+ /* Manufacturer may optionally not support all features, commands & attributes */
+ gDEMInstance = std::make_unique<DeviceEnergyManagementManager>(
+ EndpointId(ENERGY_EVSE_ENDPOINT), *gDEMDelegate,
+ BitMask<DeviceEnergyManagement::Feature, uint32_t>(
+ DeviceEnergyManagement::Feature::kPowerAdjustment, DeviceEnergyManagement::Feature::kPowerForecastReporting,
+ DeviceEnergyManagement::Feature::kStateForecastReporting, DeviceEnergyManagement::Feature::kStartTimeAdjustment,
+ DeviceEnergyManagement::Feature::kPausable, DeviceEnergyManagement::Feature::kForecastAdjustment,
+ DeviceEnergyManagement::Feature::kConstraintBasedAdjustment));
+
+ if (!gDEMInstance)
{
- ChipLogError(AppServer, "EVSEManufacturer already exists.")
+ ESP_LOGE(TAG, "Failed to allocate memory for DeviceEnergyManagementManager");
+ gDEMDelegate.reset();
+ return CHIP_ERROR_NO_MEMORY;
}
+
+ CHIP_ERROR err = gDEMInstance->Init(); /* Register Attribute & Command handlers */
+ if (err != CHIP_NO_ERROR)
+ {
+ ESP_LOGE(TAG, "Init failed on gDEMInstance, err:%" CHIP_ERROR_FORMAT, err.Format());
+ gDEMInstance.reset();
+ gDEMDelegate.reset();
+ return err;
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR DeviceEnergyManagementShutdown()
+{
+ /* Do this in the order Instance first, then delegate
+ * Ensure we call the Instance->Shutdown to free attribute & command handlers first
+ */
+ if (gDEMInstance)
+ {
+ /* deregister attribute & command handlers */
+ gDEMInstance->Shutdown();
+ gDEMInstance.reset();
+ }
+ if (gDEMDelegate)
+ {
+ gDEMDelegate.reset();
+ }
+ return CHIP_NO_ERROR;
+}
+
+/*
+ * @brief Creates a Delegate and Instance for EVSE cluster
+ *
+ * The Instance is a container around the Delegate, so
+ * create the Delegate first, then wrap it in the Instance
+ * Then call the Instance->Init() to register the attribute and command handlers
+ */
+CHIP_ERROR EnergyEvseInit()
+{
+ CHIP_ERROR err;
+
+ if (gEvseDelegate || gEvseInstance)
+ {
+ ESP_LOGE(TAG, "EVSE Instance or Delegate already exist.");
+ return CHIP_ERROR_INCORRECT_STATE;
+ }
+
+ gEvseDelegate = std::make_unique<EnergyEvseDelegate>();
+ if (!gEvseDelegate)
+ {
+ ESP_LOGE(TAG, "Failed to allocate memory for EnergyEvseDelegate");
+ return CHIP_ERROR_NO_MEMORY;
+ }
+
+ /* Manufacturer may optionally not support all features, commands & attributes */
+ gEvseInstance = std::make_unique<EnergyEvseManager>(
+ EndpointId(ENERGY_EVSE_ENDPOINT), *gEvseDelegate,
+ BitMask<EnergyEvse::Feature, uint32_t>(EnergyEvse::Feature::kChargingPreferences, EnergyEvse::Feature::kPlugAndCharge,
+ EnergyEvse::Feature::kRfid, EnergyEvse::Feature::kSoCReporting,
+ EnergyEvse::Feature::kV2x),
+ BitMask<EnergyEvse::OptionalAttributes, uint32_t>(EnergyEvse::OptionalAttributes::kSupportsUserMaximumChargingCurrent,
+ EnergyEvse::OptionalAttributes::kSupportsRandomizationWindow,
+ EnergyEvse::OptionalAttributes::kSupportsApproximateEvEfficiency),
+ BitMask<EnergyEvse::OptionalCommands, uint32_t>(EnergyEvse::OptionalCommands::kSupportsStartDiagnostics));
+
+ if (!gEvseInstance)
+ {
+ ESP_LOGE(TAG, "Failed to allocate memory for EnergyEvseManager");
+ gEvseDelegate.reset();
+ return CHIP_ERROR_NO_MEMORY;
+ }
+
+ err = gEvseInstance->Init(); /* Register Attribute & Command handlers */
+ if (err != CHIP_NO_ERROR)
+ {
+ ESP_LOGE(TAG, "Init failed on gEvseInstance, err:%" CHIP_ERROR_FORMAT, err.Format());
+ gEvseInstance.reset();
+ gEvseDelegate.reset();
+ return err;
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR EnergyEvseShutdown()
+{
+ /* Do this in the order Instance first, then delegate
+ * Ensure we call the Instance->Shutdown to free attribute & command handlers first
+ */
+ if (gEvseInstance)
+ {
+ /* deregister attribute & command handlers */
+ gEvseInstance->Shutdown();
+ gEvseInstance.reset();
+ }
+
+ if (gEvseDelegate)
+ {
+ gEvseDelegate.reset();
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+/*
+ * @brief Creates a EVSEManufacturer class to hold the EVSE & DEM clusters
+ *
+ * The Instance is a container around the Delegate, so
+ * create the Delegate first, then wrap it in the Instance
+ * Then call the Instance->Init() to register the attribute and command handlers
+ */
+CHIP_ERROR EVSEManufacturerInit()
+{
+ CHIP_ERROR err;
+
+ if (gEvseManufacturer)
+ {
+ ESP_LOGE(TAG, "EvseManufacturer already exist.");
+ return CHIP_ERROR_INCORRECT_STATE;
+ }
+
+ /* Now create EVSEManufacturer */
+ gEvseManufacturer = std::make_unique<EVSEManufacturer>(gEvseInstance.get());
+ if (!gEvseManufacturer)
+ {
+ ESP_LOGE(TAG, "Failed to allocate memory for EvseManufacturer");
+ return CHIP_ERROR_NO_MEMORY;
+ }
+
+ /* Call Manufacturer specific init */
+ err = gEvseManufacturer->Init();
+ if (err != CHIP_NO_ERROR)
+ {
+ ESP_LOGE(TAG, "Init failed on gEvseManufacturer, err:%" CHIP_ERROR_FORMAT, err.Format());
+ gEvseManufacturer.reset();
+ return err;
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR EVSEManufacturerShutdown()
+{
+ if (gEvseManufacturer)
+ {
+ /* Shutdown the EVSEManufacturer */
+ gEvseManufacturer->Shutdown();
+ gEvseManufacturer.reset();
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+void ApplicationInit()
+{
+ if (DeviceEnergyManagementInit() != CHIP_NO_ERROR)
+ {
+ return;
+ }
+
+ if (EnergyEvseInit() != CHIP_NO_ERROR)
+ {
+ DeviceEnergyManagementShutdown();
+ return;
+ }
+
+ if (EVSEManufacturerInit() != CHIP_NO_ERROR)
+ {
+ DeviceEnergyManagementShutdown();
+ EnergyEvseShutdown();
+ return;
+ }
+}
+
+void ApplicationShutdown()
+{
+ ESP_LOGD(TAG, "Energy Management App: ApplicationShutdown()");
+
+ /* Shutdown in reverse order that they were created */
+ EVSEManufacturerShutdown(); /* Free the EVSEManufacturer */
+ EnergyEvseShutdown(); /* Free the EnergyEvse */
+ DeviceEnergyManagementShutdown(); /* Free the DEM */
+
+ Clusters::DeviceEnergyManagementMode::Shutdown();
+ Clusters::EnergyEvseMode::Shutdown();
}
static void InitServer(intptr_t context)
{
- ApplicationInit();
// Print QR Code URL
PrintOnboardingCodes(chip::RendezvousInformationFlags(CONFIG_RENDEZVOUS_MODE));
@@ -175,6 +374,9 @@
static Tracing::Insights::ESP32Backend backend;
Tracing::Register(backend);
#endif
+
+ // Application code should always be initialised after the initialisation of server.
+ ApplicationInit();
}
extern "C" void app_main()