From 13824280fe349e2db4339474708e3ed5661631ca Mon Sep 17 00:00:00 2001 From: Rohit Jadhav <69809379+jadhavrohit924@users.noreply.github.com> Date: Wed, 24 Jan 2024 19:48:52 +0530 Subject: [PATCH] [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 --- .github/workflows/examples-esp32.yaml | 3 + .../src/EnergyEvseManager.cpp | 7 +- .../esp32/main/CMakeLists.txt | 2 + .../energy-management-app/esp32/main/main.cpp | 256 ++++++++++++++++-- 4 files changed, 240 insertions(+), 28 deletions(-) diff --git a/.github/workflows/examples-esp32.yaml b/.github/workflows/examples-esp32.yaml index 47908d654006e9..2e5432dd4ec03f 100644 --- a/.github/workflows/examples-esp32.yaml +++ b/.github/workflows/examples-esp32.yaml @@ -165,3 +165,6 @@ jobs: - 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 710b13f9d342b8..24643885d54368 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 @@ CHIP_ERROR EnergyEvseManager::LoadPersistentAttributes() { 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 1c7f2a5b782948..46f97d766dbfeb 100644 --- a/examples/energy-management-app/esp32/main/CMakeLists.txt +++ b/examples/energy-management-app/esp32/main/CMakeLists.txt @@ -50,6 +50,7 @@ set(SRC_DIRS_LIST "${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 @@ set(SRC_DIRS_LIST "${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 77899a45b58d03..fd79e606afb6aa 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 #include #include #include +#include +#include #include "esp_log.h" #include @@ -74,9 +77,11 @@ using namespace ::chip::Credentials; using namespace ::chip::DeviceManager; using namespace ::chip::DeviceLayer; -static EnergyEvseDelegate * gDelegate = nullptr; -static EnergyEvseManager * gInstance = nullptr; -static EVSEManufacturer * gEvseManufacturer = nullptr; +static std::unique_ptr gEvseDelegate; +static std::unique_ptr gEvseInstance; +static std::unique_ptr gDEMDelegate; +static std::unique_ptr gDEMInstance; +static std::unique_ptr 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 @@ chip::Credentials::DeviceAttestationCredentialsProvider * get_dac_provider(void) } // namespace -void ApplicationInit() +EVSEManufacturer * EnergyEvse::GetEvseManufacturer() +{ + 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) + { + ESP_LOGE(TAG, "DEM Instance or Delegate already exist."); + return CHIP_ERROR_INCORRECT_STATE; + } + + gDEMDelegate = std::make_unique(); + if (!gDEMDelegate) + { + ESP_LOGE(TAG, "Failed to allocate memory for DeviceEnergyManagementDelegate"); + return CHIP_ERROR_NO_MEMORY; + } + + /* Manufacturer may optionally not support all features, commands & attributes */ + gDEMInstance = std::make_unique( + EndpointId(ENERGY_EVSE_ENDPOINT), *gDEMDelegate, + BitMask( + DeviceEnergyManagement::Feature::kPowerAdjustment, DeviceEnergyManagement::Feature::kPowerForecastReporting, + DeviceEnergyManagement::Feature::kStateForecastReporting, DeviceEnergyManagement::Feature::kStartTimeAdjustment, + DeviceEnergyManagement::Feature::kPausable, DeviceEnergyManagement::Feature::kForecastAdjustment, + DeviceEnergyManagement::Feature::kConstraintBasedAdjustment)); + + if (!gDEMInstance) + { + 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() { - if ((gDelegate == nullptr) && (gInstance == nullptr) && (gEvseManufacturer == nullptr)) + /* Do this in the order Instance first, then delegate + * Ensure we call the Instance->Shutdown to free attribute & command handlers first + */ + if (gDEMInstance) { - gDelegate = new EnergyEvseDelegate(); - if (gDelegate != nullptr) - { - gInstance = new EnergyEvseManager( - EndpointId(ENERGY_EVSE_ENDPOINT), *gDelegate, - BitMask(EnergyEvse::Feature::kChargingPreferences, - EnergyEvse::Feature::kPlugAndCharge, EnergyEvse::Feature::kRfid, - EnergyEvse::Feature::kSoCReporting, EnergyEvse::Feature::kV2x), - BitMask(OptionalAttributes::kSupportsUserMaximumChargingCurrent, - OptionalAttributes::kSupportsRandomizationWindow, - OptionalAttributes::kSupportsApproximateEvEfficiency), - BitMask(OptionalCommands::kSupportsStartDiagnostics)); - gInstance->Init(); /* Register Attribute & Command handlers */ - } + /* deregister attribute & command handlers */ + gDEMInstance->Shutdown(); + gDEMInstance.reset(); } - else + if (gDEMDelegate) { - ChipLogError(AppServer, "EVSE Instance or Delegate already exist.") + 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 (gEvseManufacturer == nullptr) + if (gEvseDelegate || gEvseInstance) { - gEvseManufacturer = new EVSEManufacturer(); - gEvseManufacturer->Init(gInstance); + ESP_LOGE(TAG, "EVSE Instance or Delegate already exist."); + return CHIP_ERROR_INCORRECT_STATE; } - else + + gEvseDelegate = std::make_unique(); + 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( + EndpointId(ENERGY_EVSE_ENDPOINT), *gEvseDelegate, + BitMask(EnergyEvse::Feature::kChargingPreferences, EnergyEvse::Feature::kPlugAndCharge, + EnergyEvse::Feature::kRfid, EnergyEvse::Feature::kSoCReporting, + EnergyEvse::Feature::kV2x), + BitMask(EnergyEvse::OptionalAttributes::kSupportsUserMaximumChargingCurrent, + EnergyEvse::OptionalAttributes::kSupportsRandomizationWindow, + EnergyEvse::OptionalAttributes::kSupportsApproximateEvEfficiency), + BitMask(EnergyEvse::OptionalCommands::kSupportsStartDiagnostics)); + + if (!gEvseInstance) { - ChipLogError(AppServer, "EVSEManufacturer already exists.") + 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(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 void InitServer(intptr_t context) 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()