diff --git a/examples/light-switch-app/efr32/BUILD.gn b/examples/light-switch-app/efr32/BUILD.gn index 6e3d588c974ce7..cb9dacf9b4e6e7 100644 --- a/examples/light-switch-app/efr32/BUILD.gn +++ b/examples/light-switch-app/efr32/BUILD.gn @@ -169,6 +169,7 @@ efr32_executable("light_switch_app") { defines = [] sources = [ + "${examples_plat_dir}/BaseApplication.cpp", "${examples_plat_dir}/LEDWidget.cpp", "${examples_plat_dir}/efr32_utils.cpp", "${examples_plat_dir}/heap_4_silabs.c", diff --git a/examples/light-switch-app/efr32/include/AppTask.h b/examples/light-switch-app/efr32/include/AppTask.h index 8b500a94e305d4..a8e186083c23f6 100644 --- a/examples/light-switch-app/efr32/include/AppTask.h +++ b/examples/light-switch-app/efr32/include/AppTask.h @@ -27,6 +27,7 @@ #include #include "AppEvent.h" +#include "BaseApplication.h" #include "FreeRTOS.h" #include "sl_simple_button_instances.h" #include "timers.h" // provides FreeRTOS timer support @@ -51,21 +52,13 @@ * AppTask Declaration *********************************************************/ -class AppTask +class AppTask : public BaseApplication { public: - /********************************************************** - * Public Function Declaration - *********************************************************/ + AppTask() = default; - /** - * @brief Create AppTask task and Event Queue - * If an error occurs during creation, application will hang after printing out error code - * - * @return CHIP_ERROR CHIP_NO_ERROR if no errors - */ - CHIP_ERROR StartAppTask(); + static AppTask & GetAppTask() { return sAppTask; } /** * @brief AppTask task main loop function @@ -74,12 +67,7 @@ class AppTask */ static void AppTaskMain(void * pvParameter); - /** - * @brief PostEvent function that add event to AppTask queue for processing - * - * @param event AppEvent to post - */ - void PostEvent(const AppEvent * event); + CHIP_ERROR StartAppTask(); /** * @brief Event handler when a button is pressed @@ -89,7 +77,7 @@ class AppTask * @param btnAction button action - SL_SIMPLE_BUTTON_PRESSED, * SL_SIMPLE_BUTTON_RELEASED or SL_SIMPLE_BUTTON_DISABLED */ - void ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAction); + void ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAction) override; /** * @brief Callback called by the identify-server when an identify command is received @@ -105,23 +93,8 @@ class AppTask */ static void OnIdentifyStop(Identify * identify); - /** - * @brief Function called to start the LED light timer - */ - void StartLightTimer(void); - - /** - * @brief Function to stop LED light timer - * Turns off Status LED before stopping timer - */ - void CancelLightTimer(void); - private: - /********************************************************** - * Private Function Declaration - *********************************************************/ - - friend AppTask & GetAppTask(void); + static AppTask sAppTask; /** * @brief AppTask initialisation function @@ -130,41 +103,6 @@ class AppTask */ CHIP_ERROR Init(); - /** - * @brief Function called to start the function timer - * - * @param aTimeoutMs timer duration in ms - */ - void StartFunctionTimer(uint32_t aTimeoutMs); - - /** - * @brief Function to stop function timer - */ - void CancelFunctionTimer(void); - - /** - * @brief Function call event callback function for processing - * - * @param event triggered event to be processed - */ - void DispatchEvent(AppEvent * event); - - /** - * @brief Function Timer finished callback function - * Post an FunctionEventHandler event - * - * @param xTimer timer that finished - */ - static void FunctionTimerEventHandler(TimerHandle_t xTimer); - - /** - * @brief Timer Event processing function - * Trigger factory if Press and Hold duration is respected - * - * @param aEvent post event being processed - */ - static void FunctionEventHandler(AppEvent * aEvent); - /** * @brief PB0 Button event processing function * Press and hold will trigger a factory reset timer start @@ -181,42 +119,4 @@ class AppTask * @param aEvent button event being processed */ static void SwitchActionEventHandler(AppEvent * aEvent); - - /** - * @brief Light Timer finished callback function - * Calls LED processing function - * - * @param xTimer timer that finished - */ - static void LightTimerEventHandler(TimerHandle_t xTimer); - - /** - * @brief Updates device LEDs - */ - static void LightEventHandler(); - - /********************************************************** - * Private Attributes declaration - *********************************************************/ - - enum Function_t - { - kFunction_NoneSelected = 0, - kFunction_SoftwareUpdate = 0, - kFunction_StartBleAdv = 1, - kFunction_FactoryReset = 2, - - kFunction_Invalid - } Function; - - Function_t mFunction; - bool mFunctionTimerActive; - bool mSyncClusterToButtonAction; - - static AppTask sAppTask; }; - -inline AppTask & GetAppTask(void) -{ - return AppTask::sAppTask; -} diff --git a/examples/light-switch-app/efr32/src/AppTask.cpp b/examples/light-switch-app/efr32/src/AppTask.cpp index 10138fa34a71df..26848ff9a62772 100644 --- a/examples/light-switch-app/efr32/src/AppTask.cpp +++ b/examples/light-switch-app/efr32/src/AppTask.cpp @@ -48,29 +48,12 @@ #include #include -#if CHIP_ENABLE_OPENTHREAD -#include -#include -#include -#endif // CHIP_ENABLE_OPENTHREAD - -#ifdef SL_WIFI -#include "wfx_host_events.h" -#include -#include -#endif // SL_WIFI +#include /********************************************************** * Defines and Constants *********************************************************/ -#define FACTORY_RESET_TRIGGER_TIMEOUT 3000 -#define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000 -#define APP_TASK_STACK_SIZE (4096) -#define APP_TASK_PRIORITY 2 -#define APP_EVENT_QUEUE_SIZE 10 -#define EXAMPLE_VENDOR_ID 0xcafe - #define SYSTEM_STATE_LED &sl_led_led0 #define APP_FUNCTION_BUTTON &sl_button_btn0 @@ -85,36 +68,8 @@ namespace { * Variable declarations *********************************************************/ -TimerHandle_t sFunctionTimer; // FreeRTOS app sw timer. -TimerHandle_t sLightTimer; - -TaskHandle_t sAppTaskHandle; -QueueHandle_t sAppEventQueue; - -LEDWidget sStatusLED; - -#ifdef SL_WIFI -app::Clusters::NetworkCommissioning::Instance - sWiFiNetworkCommissioningInstance(0 /* Endpoint Id */, &(NetworkCommissioning::SlWiFiDriver::GetInstance())); -#endif /* SL_WIFI */ - -#if !(defined(CHIP_DEVICE_CONFIG_ENABLE_SED) && CHIP_DEVICE_CONFIG_ENABLE_SED) - -bool sIsProvisioned = false; -bool sIsEnabled = false; -bool sIsAttached = false; -bool sHaveBLEConnections = false; - -#endif // CHIP_DEVICE_CONFIG_ENABLE_SED - EmberAfIdentifyEffectIdentifier sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; -uint8_t sAppEventQueueBuffer[APP_EVENT_QUEUE_SIZE * sizeof(AppEvent)]; -StaticQueue_t sAppEventQueueStruct; - -StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)]; -StaticTask_t appTaskStruct; - /********************************************************** * Identify Callbacks *********************************************************/ @@ -126,7 +81,7 @@ void OnTriggerIdentifyEffectCompleted(chip::System::Layer * systemLayer, void * sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; #if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 - GetAppTask().CancelLightTimer(); + AppTask::GetAppTask().StopStatusLEDTimer(); #endif } } // namespace @@ -144,7 +99,7 @@ void OnTriggerIdentifyEffect(Identify * identify) } #if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 - GetAppTask().StartLightTimer(); + AppTask::GetAppTask().StartStatusLEDTimer(); #endif switch (sIdentifyEffect) @@ -170,8 +125,10 @@ void OnTriggerIdentifyEffect(Identify * identify) } Identify gIdentify = { - chip::EndpointId{ 1 }, GetAppTask().OnIdentifyStart, - GetAppTask().OnIdentifyStop, EMBER_ZCL_IDENTIFY_IDENTIFY_TYPE_VISIBLE_LED, + chip::EndpointId{ 1 }, + AppTask::GetAppTask().OnIdentifyStart, + AppTask::GetAppTask().OnIdentifyStop, + EMBER_ZCL_IDENTIFY_IDENTIFY_TYPE_VISIBLE_LED, OnTriggerIdentifyEffect, }; @@ -186,77 +143,17 @@ using namespace ::chip::DeviceLayer; AppTask AppTask::sAppTask; -CHIP_ERROR AppTask::StartAppTask() -{ - sAppEventQueue = xQueueCreateStatic(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent), sAppEventQueueBuffer, &sAppEventQueueStruct); - if (sAppEventQueue == NULL) - { - EFR32_LOG("Failed to allocate app event queue"); - appError(APP_ERROR_EVENT_QUEUE_FAILED); - } - - // Start App task. - sAppTaskHandle = xTaskCreateStatic(AppTaskMain, APP_TASK_NAME, ArraySize(appStack), NULL, 1, appStack, &appTaskStruct); - if (sAppTaskHandle == nullptr) - { - EFR32_LOG("Failed to create app task"); - appError(APP_ERROR_CREATE_TASK_FAILED); - } - return CHIP_NO_ERROR; -} - CHIP_ERROR AppTask::Init() { CHIP_ERROR err = CHIP_NO_ERROR; -#ifdef SL_WIFI - /* - * Wait for the WiFi to be initialized - */ - EFR32_LOG("APP: Wait WiFi Init"); - while (!wfx_hw_ready()) - { - vTaskDelay(10); - } - EFR32_LOG("APP: Done WiFi Init"); - /* We will init server when we get IP */ - - sWiFiNetworkCommissioningInstance.Init(); -#endif - - // Create FreeRTOS sw timer for Function Selection. - sFunctionTimer = xTimerCreate("FnTmr", // Just a text name, not used by the RTOS kernel - 1, // == default timer period (mS) - false, // no timer reload (==one-shot) - (void *) this, // init timer id = app task obj context - FunctionTimerEventHandler // timer callback handler - ); - if (sFunctionTimer == NULL) - { - EFR32_LOG("funct timer create failed"); - appError(APP_ERROR_CREATE_TIMER_FAILED); - } - - // Create FreeRTOS sw timer for LED Management. - sLightTimer = xTimerCreate("LightTmr", // Text Name - 10, // Default timer period (mS) - true, // reload timer - (void *) this, // Timer Id - LightTimerEventHandler // Timer callback handler - ); - if (sLightTimer == NULL) + err = BaseApplication::Init(&gIdentify); + if (err != CHIP_NO_ERROR) { - EFR32_LOG("Light Timer create failed"); - appError(APP_ERROR_CREATE_TIMER_FAILED); + EFR32_LOG("BaseApplication::Init() failed"); + appError(err); } - EFR32_LOG("Current Software Version: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); - - LEDWidget::InitGpio(); - sStatusLED.Init(SYSTEM_STATE_LED); - - ConfigurationMgr().LogDeviceConfig(); - // Configure Bindings - TODO ERROR PROCESSING err = InitBindingHandler(); if (err != CHIP_NO_ERROR) @@ -265,30 +162,18 @@ CHIP_ERROR AppTask::Init() appError(err); } -// Print setup info on LCD if available -#ifdef QR_CODE_ENABLED - // Create buffer for QR code that can fit max size and null terminator. - char qrCodeBuffer[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1]; - chip::MutableCharSpan QRCode(qrCodeBuffer); - - if (GetQRCode(QRCode, chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)) == CHIP_NO_ERROR) - { - LCDWriteQRCode((uint8_t *) QRCode.data()); - } - else - { - EFR32_LOG("Getting QR code failed!"); - } -#else - PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE)); -#endif // QR_CODE_ENABLED - return err; } +CHIP_ERROR AppTask::StartAppTask() +{ + return BaseApplication::StartAppTask(AppTaskMain); +} + void AppTask::AppTaskMain(void * pvParameter) { AppEvent event; + QueueHandle_t sAppEventQueue = *(static_cast(pvParameter)); CHIP_ERROR err = sAppTask.Init(); if (err != CHIP_NO_ERROR) @@ -298,7 +183,7 @@ void AppTask::AppTaskMain(void * pvParameter) } #if !(defined(CHIP_DEVICE_CONFIG_ENABLE_SED) && CHIP_DEVICE_CONFIG_ENABLE_SED) - sAppTask.StartLightTimer(); + sAppTask.StartStatusLEDTimer(); #endif EFR32_LOG("App Task started"); @@ -318,7 +203,7 @@ void AppTask::OnIdentifyStart(Identify * identify) ChipLogProgress(Zcl, "onIdentifyStart"); #if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 - sAppTask.StartLightTimer(); + sAppTask.StartStatusLEDTimer(); #endif // CHIP_DEVICE_CONFIG_ENABLE_SED } @@ -327,7 +212,7 @@ void AppTask::OnIdentifyStop(Identify * identify) ChipLogProgress(Zcl, "onIdentifyStop"); #if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 - sAppTask.CancelLightTimer(); + sAppTask.StopStatusLEDTimer(); #endif // CHIP_DEVICE_CONFIG_ENABLE_SED } @@ -361,295 +246,7 @@ void AppTask::ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAc } else if (buttonHandle == APP_FUNCTION_BUTTON) { - button_event.Handler = ButtonHandler; + button_event.Handler = BaseApplication::ButtonHandler; sAppTask.PostEvent(&button_event); } } - -void AppTask::FunctionTimerEventHandler(TimerHandle_t xTimer) -{ - AppEvent event; - event.Type = AppEvent::kEventType_Timer; - event.TimerEvent.Context = (void *) xTimer; - event.Handler = FunctionEventHandler; - sAppTask.PostEvent(&event); -} - -void AppTask::LightTimerEventHandler(TimerHandle_t xTimer) -{ - sAppTask.LightEventHandler(); -} - -void AppTask::FunctionEventHandler(AppEvent * aEvent) -{ - if (aEvent->Type != AppEvent::kEventType_Timer) - { - return; - } - - // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT, - // initiate factory reset - if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv) - { - EFR32_LOG("Factory Reset Triggered. Release button within %ums to cancel.", FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); - - // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to - // cancel, if required. - sAppTask.StartFunctionTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); - -#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 - sAppTask.StartLightTimer(); -#endif // CHIP_DEVICE_CONFIG_ENABLE_SED - - sAppTask.mFunction = kFunction_FactoryReset; - - // Turn off all LEDs before starting blink to make sure blink is - // co-ordinated. - sStatusLED.Set(false); - sStatusLED.Blink(500); - } - else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) - { - // Actually trigger Factory Reset - sAppTask.mFunction = kFunction_NoneSelected; - -#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 - sAppTask.CancelLightTimer(); -#endif // CHIP_DEVICE_CONFIG_ENABLE_SED - - chip::Server::GetInstance().ScheduleFactoryReset(); - } -} - -void AppTask::LightEventHandler() -{ - // Collect connectivity and configuration state from the CHIP stack. Because - // the CHIP event loop is being run in a separate task, the stack must be - // locked while these values are queried. However we use a non-blocking - // lock request (TryLockCHIPStack()) to avoid blocking other UI activities - // when the CHIP task is busy (e.g. with a long crypto operation). -#if !(defined(CHIP_DEVICE_CONFIG_ENABLE_SED) && CHIP_DEVICE_CONFIG_ENABLE_SED) - if (PlatformMgr().TryLockChipStack()) - { -#ifdef SL_WIFI - sIsProvisioned = ConnectivityMgr().IsWiFiStationProvisioned(); - sIsEnabled = ConnectivityMgr().IsWiFiStationEnabled(); - sIsAttached = ConnectivityMgr().IsWiFiStationConnected(); -#endif /* SL_WIFI */ -#if CHIP_ENABLE_OPENTHREAD - sIsProvisioned = ConnectivityMgr().IsThreadProvisioned(); - sIsEnabled = ConnectivityMgr().IsThreadEnabled(); - sIsAttached = ConnectivityMgr().IsThreadAttached(); -#endif /* CHIP_ENABLE_OPENTHREAD */ - sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0); - PlatformMgr().UnlockChipStack(); - } -#endif // CHIP_DEVICE_CONFIG_ENABLE_SED - - // Update the status LED if factory reset has not been initiated. - // - // If system has "full connectivity", keep the LED On constantly. - // - // If thread and service provisioned, but not attached to the thread network - // yet OR no connectivity to the service OR subscriptions are not fully - // established THEN blink the LED Off for a short period of time. - // - // If the system has ble connection(s) uptill the stage above, THEN blink - // the LEDs at an even rate of 100ms. - // - // Otherwise, blink the LED ON for a very short time. - if (sAppTask.mFunction != kFunction_FactoryReset) - { - if (gIdentify.mActive) - { - sStatusLED.Blink(250, 250); - } - else if (sIdentifyEffect != EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT) - { - if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BLINK) - { - sStatusLED.Blink(50, 50); - } - if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BREATHE) - { - sStatusLED.Blink(1000, 1000); - } - if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_OKAY) - { - sStatusLED.Blink(300, 700); - } - } -#if !(defined(CHIP_DEVICE_CONFIG_ENABLE_SED) && CHIP_DEVICE_CONFIG_ENABLE_SED) - else if (sIsProvisioned && sIsEnabled) - { - if (sIsAttached) - { - sStatusLED.Set(true); - } - else - { - sStatusLED.Blink(950, 50); - } - } - else if (sHaveBLEConnections) - { - sStatusLED.Blink(100, 100); - } - else - { - sStatusLED.Blink(50, 950); - } -#endif // CHIP_DEVICE_CONFIG_ENABLE_SED - } - - sStatusLED.Animate(); -} - -void AppTask::ButtonHandler(AppEvent * aEvent) -{ - // To trigger software update: press the APP_FUNCTION_BUTTON button briefly (< - // FACTORY_RESET_TRIGGER_TIMEOUT) To initiate factory reset: press the - // APP_FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT + - // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT All LEDs start blinking after - // FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated. - // To cancel factory reset: release the APP_FUNCTION_BUTTON once all LEDs - // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT - if (aEvent->ButtonEvent.Action == SL_SIMPLE_BUTTON_PRESSED) - { - if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected) - { - sAppTask.StartFunctionTimer(FACTORY_RESET_TRIGGER_TIMEOUT); - sAppTask.mFunction = kFunction_StartBleAdv; - } - } - else - { - // If the button was released before factory reset got initiated, start BLE advertissement in fast mode - if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv) - { - sAppTask.CancelFunctionTimer(); - sAppTask.mFunction = kFunction_NoneSelected; - -#ifdef SL_WIFI - if (!ConnectivityMgr().IsWiFiStationProvisioned()) -#else - if (!ConnectivityMgr().IsThreadProvisioned()) -#endif /* !SL_WIFI */ - { - // Enable BLE advertisements - ConnectivityMgr().SetBLEAdvertisingEnabled(true); - ConnectivityMgr().SetBLEAdvertisingMode(ConnectivityMgr().kFastAdvertising); - } - else { EFR32_LOG("Network is already provisioned, Ble advertissement not enabled"); } - } - else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) - { - sAppTask.CancelFunctionTimer(); - -#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 - sAppTask.CancelLightTimer(); -#endif - - // Change the function to none selected since factory reset has been - // canceled. - sAppTask.mFunction = kFunction_NoneSelected; - EFR32_LOG("Factory Reset has been Canceled"); - } - } -} - -void AppTask::CancelFunctionTimer() -{ - if (xTimerStop(sFunctionTimer, 0) == pdFAIL) - { - EFR32_LOG("app timer stop() failed"); - appError(APP_ERROR_STOP_TIMER_FAILED); - } - - mFunctionTimerActive = false; -} - -void AppTask::CancelLightTimer() -{ - sStatusLED.Set(false); - if (xTimerStop(sLightTimer, 100) != pdPASS) - { - EFR32_LOG("Light Time start failed"); - appError(APP_ERROR_START_TIMER_FAILED); - } -} - -void AppTask::StartFunctionTimer(uint32_t aTimeoutInMs) -{ - if (xTimerIsTimerActive(sFunctionTimer)) - { - EFR32_LOG("app timer already started!"); - CancelFunctionTimer(); - } - - // timer is not active, change its period to required value (== restart). - // FreeRTOS- Block for a maximum of 100 ticks if the change period command - // cannot immediately be sent to the timer command queue. - if (xTimerChangePeriod(sFunctionTimer, aTimeoutInMs / portTICK_PERIOD_MS, 100) != pdPASS) - { - EFR32_LOG("app timer start() failed"); - appError(APP_ERROR_START_TIMER_FAILED); - } - - mFunctionTimerActive = true; -} - -void AppTask::StartLightTimer() -{ - if (pdPASS != xTimerStart(sLightTimer, 0)) - { - EFR32_LOG("Light Time start failed"); - appError(APP_ERROR_START_TIMER_FAILED); - } -} - -void AppTask::PostEvent(const AppEvent * aEvent) -{ - if (sAppEventQueue != NULL) - { - BaseType_t status; - if (xPortIsInsideInterrupt()) - { - BaseType_t higherPrioTaskWoken = pdFALSE; - status = xQueueSendFromISR(sAppEventQueue, aEvent, &higherPrioTaskWoken); - -#ifdef portYIELD_FROM_ISR - portYIELD_FROM_ISR(higherPrioTaskWoken); -#elif portEND_SWITCHING_ISR // portYIELD_FROM_ISR or portEND_SWITCHING_ISR - portEND_SWITCHING_ISR(higherPrioTaskWoken); -#else // portYIELD_FROM_ISR or portEND_SWITCHING_ISR -#error "Must have portYIELD_FROM_ISR or portEND_SWITCHING_ISR" -#endif // portYIELD_FROM_ISR or portEND_SWITCHING_ISR - } - else - { - status = xQueueSend(sAppEventQueue, aEvent, 1); - } - - if (!status) - { - EFR32_LOG("Failed to post event to app task event queue"); - } - } - else - { - EFR32_LOG("Event Queue is NULL should never happen"); - } -} - -void AppTask::DispatchEvent(AppEvent * aEvent) -{ - if (aEvent->Handler) - { - aEvent->Handler(aEvent); - } - else - { - EFR32_LOG("Event received with no handler. Dropping event."); - } -} diff --git a/examples/light-switch-app/efr32/src/main.cpp b/examples/light-switch-app/efr32/src/main.cpp index 7aa5aef9c6305b..7c6cebba603c81 100644 --- a/examples/light-switch-app/efr32/src/main.cpp +++ b/examples/light-switch-app/efr32/src/main.cpp @@ -66,7 +66,7 @@ int main(void) chip::DeviceLayer::PlatformMgr().UnlockChipStack(); EFR32_LOG("Starting App Task"); - if (GetAppTask().StartAppTask() != CHIP_NO_ERROR) + if (AppTask::GetAppTask().StartAppTask() != CHIP_NO_ERROR) appError(CHIP_ERROR_INTERNAL); EFR32_LOG("Starting FreeRTOS scheduler"); @@ -80,5 +80,5 @@ int main(void) void sl_button_on_change(const sl_button_t * handle) { - GetAppTask().ButtonEventHandler(handle, sl_button_get_state(handle)); + AppTask::GetAppTask().ButtonEventHandler(handle, sl_button_get_state(handle)); } diff --git a/examples/lighting-app/efr32/BUILD.gn b/examples/lighting-app/efr32/BUILD.gn index f549bbd64e139e..80f3a9dc3fee30 100644 --- a/examples/lighting-app/efr32/BUILD.gn +++ b/examples/lighting-app/efr32/BUILD.gn @@ -174,6 +174,7 @@ efr32_executable("lighting_app") { defines = [] sources = [ + "${examples_plat_dir}/BaseApplication.cpp", "${examples_plat_dir}/LEDWidget.cpp", "${examples_plat_dir}/efr32_utils.cpp", "${examples_plat_dir}/heap_4_silabs.c", diff --git a/examples/lighting-app/efr32/include/AppTask.h b/examples/lighting-app/efr32/include/AppTask.h index e7d20ce0b8dda3..df80995592fd95 100644 --- a/examples/lighting-app/efr32/include/AppTask.h +++ b/examples/lighting-app/efr32/include/AppTask.h @@ -19,18 +19,28 @@ #pragma once +/********************************************************** + * Includes + *********************************************************/ + #include #include #include "AppEvent.h" +#include "BaseApplication.h" +#include "FreeRTOS.h" #include "LightingManager.h" #include "sl_simple_button_instances.h" - -#include "FreeRTOS.h" #include "timers.h" // provides FreeRTOS timer support +#include #include +#include #include +/********************************************************** + * Defines + *********************************************************/ + // Application-defined error codes in the CHIP_ERROR space. #define APP_ERROR_EVENT_QUEUE_FAILED CHIP_APPLICATION_ERROR(0x01) #define APP_ERROR_CREATE_TASK_FAILED CHIP_APPLICATION_ERROR(0x02) @@ -39,57 +49,83 @@ #define APP_ERROR_START_TIMER_FAILED CHIP_APPLICATION_ERROR(0x05) #define APP_ERROR_STOP_TIMER_FAILED CHIP_APPLICATION_ERROR(0x06) -class AppTask +/********************************************************** + * AppTask Declaration + *********************************************************/ + +class AppTask : public BaseApplication { public: - CHIP_ERROR StartAppTask(); + AppTask() = default; + + static AppTask & GetAppTask() { return sAppTask; } + + /** + * @brief AppTask task main loop function + * + * @param pvParameter FreeRTOS task parameter + */ static void AppTaskMain(void * pvParameter); - void PostLightActionRequest(int32_t aActor, LightingManager::Action_t aAction); - void PostEvent(const AppEvent * event); + CHIP_ERROR StartAppTask(); - void ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAction); + /** + * @brief Event handler when a button is pressed + * Function posts an event for button processing + * + * @param buttonHandle APP_LIGHT_SWITCH or APP_FUNCTION_BUTTON + * @param btnAction button action - SL_SIMPLE_BUTTON_PRESSED, + * SL_SIMPLE_BUTTON_RELEASED or SL_SIMPLE_BUTTON_DISABLED + */ + void ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAction) override; + + /** + * @brief Callback called by the identify-server when an identify command is received + * + * @param identify identify structure the command applies on + */ + static void OnIdentifyStart(Identify * identify); + + /** + * @brief Callback called by the identify-server when an identify command is stopped or finished + * + * @param identify identify structure the command applies on + */ + static void OnIdentifyStop(Identify * identify); -private: - friend AppTask & GetAppTask(void); + void PostLightActionRequest(int32_t aActor, LightingManager::Action_t aAction); - CHIP_ERROR Init(); +private: + static AppTask sAppTask; static void ActionInitiated(LightingManager::Action_t aAction, int32_t aActor); static void ActionCompleted(LightingManager::Action_t aAction); - - void CancelTimer(void); - - void DispatchEvent(AppEvent * event); - - static void FunctionTimerEventHandler(AppEvent * aEvent); - static void FunctionHandler(AppEvent * aEvent); static void LightActionEventHandler(AppEvent * aEvent); - static void TimerEventHandler(TimerHandle_t xTimer); static void UpdateClusterState(intptr_t context); - void StartTimer(uint32_t aTimeoutMs); - - enum Function_t - { - kFunction_NoneSelected = 0, - kFunction_SoftwareUpdate = 0, - kFunction_StartBleAdv = 1, - kFunction_FactoryReset = 2, - - kFunction_Invalid - } Function; - - Function_t mFunction; - bool mFunctionTimerActive; - bool mSyncClusterToButtonAction; + /** + * @brief AppTask initialisation function + * + * @return CHIP_ERROR + */ + CHIP_ERROR Init(); - static AppTask sAppTask; + /** + * @brief PB0 Button event processing function + * Press and hold will trigger a factory reset timer start + * Press and release will restart BLEAdvertising if not commisionned + * + * @param aEvent button event being processed + */ + static void ButtonHandler(AppEvent * aEvent); + + /** + * @brief PB1 Button event processing function + * Function triggers a switch action sent to the CHIP task + * + * @param aEvent button event being processed + */ + static void SwitchActionEventHandler(AppEvent * aEvent); }; - -inline AppTask & GetAppTask(void) -{ - return AppTask::sAppTask; -} diff --git a/examples/lighting-app/efr32/src/AppTask.cpp b/examples/lighting-app/efr32/src/AppTask.cpp index 3edc00dfa8855e..e0358b55546361 100644 --- a/examples/lighting-app/efr32/src/AppTask.cpp +++ b/examples/lighting-app/efr32/src/AppTask.cpp @@ -47,23 +47,6 @@ #include #include -#if CHIP_ENABLE_OPENTHREAD -#include -#include -#include -#endif -#ifdef SL_WIFI -#include "wfx_host_events.h" -#include -#include -#endif /* SL_WIFI */ - -#define FACTORY_RESET_TRIGGER_TIMEOUT 3000 -#define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000 -#define APP_TASK_STACK_SIZE (4096) -#define APP_TASK_PRIORITY 2 -#define APP_EVENT_QUEUE_SIZE 10 -#define EXAMPLE_VENDOR_ID 0xcafe #define SYSTEM_STATE_LED &sl_led_led0 #define LIGHT_LED &sl_led_led1 @@ -74,37 +57,10 @@ using namespace chip; using namespace ::chip::DeviceLayer; namespace { -TimerHandle_t sFunctionTimer; // FreeRTOS app sw timer. - -TaskHandle_t sAppTaskHandle; -QueueHandle_t sAppEventQueue; - -LEDWidget sStatusLED; LEDWidget sLightLED; -#ifdef SL_WIFI -bool sIsWiFiProvisioned = false; -bool sIsWiFiEnabled = false; -bool sIsWiFiAttached = false; - -app::Clusters::NetworkCommissioning::Instance - sWiFiNetworkCommissioningInstance(0 /* Endpoint Id */, &(NetworkCommissioning::SlWiFiDriver::GetInstance())); -#endif /* SL_WIFI */ - -#if CHIP_ENABLE_OPENTHREAD -bool sIsThreadProvisioned = false; -bool sIsThreadEnabled = false; -#endif /* CHIP_ENABLE_OPENTHREAD */ -bool sHaveBLEConnections = false; - EmberAfIdentifyEffectIdentifier sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; -uint8_t sAppEventQueueBuffer[APP_EVENT_QUEUE_SIZE * sizeof(AppEvent)]; -StaticQueue_t sAppEventQueueStruct; - -StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)]; -StaticTask_t appTaskStruct; - /********************************************************** * Identify Callbacks *********************************************************/ @@ -164,94 +120,41 @@ using namespace ::chip::DeviceLayer; AppTask AppTask::sAppTask; -CHIP_ERROR AppTask::StartAppTask() -{ - sAppEventQueue = xQueueCreateStatic(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent), sAppEventQueueBuffer, &sAppEventQueueStruct); - if (sAppEventQueue == NULL) - { - EFR32_LOG("Failed to allocate app event queue"); - appError(APP_ERROR_EVENT_QUEUE_FAILED); - } - - // Start App task. - sAppTaskHandle = xTaskCreateStatic(AppTaskMain, APP_TASK_NAME, ArraySize(appStack), NULL, 1, appStack, &appTaskStruct); - return (sAppTaskHandle == nullptr) ? APP_ERROR_CREATE_TASK_FAILED : CHIP_NO_ERROR; -} - CHIP_ERROR AppTask::Init() { CHIP_ERROR err = CHIP_NO_ERROR; -#ifdef SL_WIFI - /* - * Wait for the WiFi to be initialized - */ - EFR32_LOG("APP: Wait WiFi Init"); - while (!wfx_hw_ready()) - { - vTaskDelay(10); - } - EFR32_LOG("APP: Done WiFi Init"); - /* We will init server when we get IP */ - - sWiFiNetworkCommissioningInstance.Init(); -#endif - - // Create FreeRTOS sw timer for Function Selection. - sFunctionTimer = xTimerCreate("FnTmr", // Just a text name, not used by the RTOS kernel - 1, // == default timer period (mS) - false, // no timer reload (==one-shot) - (void *) this, // init timer id = app task obj context - TimerEventHandler // timer callback handler - ); - if (sFunctionTimer == NULL) + err = BaseApplication::Init(&gIdentify); + if (err != CHIP_NO_ERROR) { - EFR32_LOG("funct timer create failed"); - appError(APP_ERROR_CREATE_TIMER_FAILED); + EFR32_LOG("BaseApplication::Init() failed"); + appError(err); } - EFR32_LOG("Current Software Version: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); err = LightMgr().Init(); if (err != CHIP_NO_ERROR) { - EFR32_LOG("LightMgr().Init() failed"); + EFR32_LOG("LightMgr::Init() failed"); appError(err); } LightMgr().SetCallbacks(ActionInitiated, ActionCompleted); - // Initialize LEDs - LEDWidget::InitGpio(); - sStatusLED.Init(SYSTEM_STATE_LED); sLightLED.Init(LIGHT_LED); sLightLED.Set(LightMgr().IsLightOn()); - ConfigurationMgr().LogDeviceConfig(); - -// Print setup info on LCD if available -#if QR_CODE_ENABLED - // Create buffer for QR code that can fit max size and null terminator. - char qrCodeBuffer[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1]; - chip::MutableCharSpan QRCode(qrCodeBuffer); - - if (GetQRCode(QRCode, chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)) == CHIP_NO_ERROR) - { - LCDWriteQRCode((uint8_t *) QRCode.data()); - } - else - { - EFR32_LOG("Getting QR code failed!"); - } -#else - PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE)); -#endif // QR_CODE_ENABLED - return err; } +CHIP_ERROR AppTask::StartAppTask() +{ + return BaseApplication::StartAppTask(AppTaskMain); +} + void AppTask::AppTaskMain(void * pvParameter) { AppEvent event; + QueueHandle_t sAppEventQueue = *(static_cast(pvParameter)); CHIP_ERROR err = sAppTask.Init(); if (err != CHIP_NO_ERROR) @@ -270,74 +173,6 @@ void AppTask::AppTaskMain(void * pvParameter) sAppTask.DispatchEvent(&event); eventReceived = xQueueReceive(sAppEventQueue, &event, 0); } - - // Collect connectivity and configuration state from the CHIP stack. Because - // the CHIP event loop is being run in a separate task, the stack must be - // locked while these values are queried. However we use a non-blocking - // lock request (TryLockCHIPStack()) to avoid blocking other UI activities - // when the CHIP task is busy (e.g. with a long crypto operation). - if (PlatformMgr().TryLockChipStack()) - { -#ifdef SL_WIFI - sIsWiFiProvisioned = ConnectivityMgr().IsWiFiStationProvisioned(); - sIsWiFiEnabled = ConnectivityMgr().IsWiFiStationEnabled(); - sIsWiFiAttached = ConnectivityMgr().IsWiFiStationConnected(); -#endif /* SL_WIFI */ -#if CHIP_ENABLE_OPENTHREAD - sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned(); - sIsThreadEnabled = ConnectivityMgr().IsThreadEnabled(); -#endif /* CHIP_ENABLE_OPENTHREAD */ - sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0); - PlatformMgr().UnlockChipStack(); - } - - // Update the status LED if factory reset has not been initiated. - // - // If system has "full connectivity", keep the LED On constantly. - // - // If thread and service provisioned, but not attached to the thread network - // yet OR no connectivity to the service OR subscriptions are not fully - // established THEN blink the LED Off for a short period of time. - // - // If the system has ble connection(s) uptill the stage above, THEN blink - // the LEDs at an even rate of 100ms. - // - // Otherwise, blink the LED ON for a very short time. - if (sAppTask.mFunction != kFunction_FactoryReset) - { - if (gIdentify.mActive) - { - sStatusLED.Blink(250, 250); - } - if (sIdentifyEffect != EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT) - { - if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BLINK) - { - sStatusLED.Blink(50, 50); - } - if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BREATHE) - { - sStatusLED.Blink(1000, 1000); - } - if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_OKAY) - { - sStatusLED.Blink(300, 700); - } - } -#if CHIP_ENABLE_OPENTHREAD - if (sIsThreadProvisioned && sIsThreadEnabled) -#else - if (sIsWiFiProvisioned && sIsWiFiEnabled && !sIsWiFiAttached) -#endif - { - sStatusLED.Blink(950, 50); - } - else if (sHaveBLEConnections) { sStatusLED.Blink(100, 100); } - else { sStatusLED.Blink(50, 950); } - } - - sStatusLED.Animate(); - sLightLED.Animate(); } } @@ -355,15 +190,8 @@ void AppTask::LightActionEventHandler(AppEvent * aEvent) } else if (aEvent->Type == AppEvent::kEventType_Button) { - if (LightMgr().IsLightOn()) - { - action = LightingManager::OFF_ACTION; - } - else - { - action = LightingManager::ON_ACTION; - } - actor = AppEvent::kEventType_Button; + action = (LightMgr().IsLightOn()) ? LightingManager::OFF_ACTION : LightingManager::ON_ACTION; + actor = AppEvent::kEventType_Button; } else { @@ -399,139 +227,11 @@ void AppTask::ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAc } else if (buttonHandle == APP_FUNCTION_BUTTON) { - button_event.Handler = FunctionHandler; + button_event.Handler = BaseApplication::ButtonHandler; sAppTask.PostEvent(&button_event); } } -void AppTask::TimerEventHandler(TimerHandle_t xTimer) -{ - AppEvent event; - event.Type = AppEvent::kEventType_Timer; - event.TimerEvent.Context = (void *) xTimer; - event.Handler = FunctionTimerEventHandler; - sAppTask.PostEvent(&event); -} - -void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) -{ - if (aEvent->Type != AppEvent::kEventType_Timer) - { - return; - } - - // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT, - // initiate factory reset - if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv) - { - EFR32_LOG("Factory Reset Triggered. Release button within %ums to cancel.", FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); - - // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to - // cancel, if required. - sAppTask.StartTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); - - sAppTask.mFunction = kFunction_FactoryReset; - - // Turn off all LEDs before starting blink to make sure blink is - // co-ordinated. - sStatusLED.Set(false); - sLightLED.Set(false); - - sStatusLED.Blink(500); - sLightLED.Blink(500); - } - else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) - { - // Actually trigger Factory Reset - sAppTask.mFunction = kFunction_NoneSelected; - chip::Server::GetInstance().ScheduleFactoryReset(); - } -} - -void AppTask::FunctionHandler(AppEvent * aEvent) -{ - // To trigger software update: press the APP_FUNCTION_BUTTON button briefly (< - // FACTORY_RESET_TRIGGER_TIMEOUT) To initiate factory reset: press the - // APP_FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT + - // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT All LEDs start blinking after - // FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated. - // To cancel factory reset: release the APP_FUNCTION_BUTTON once all LEDs - // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT - if (aEvent->ButtonEvent.Action == SL_SIMPLE_BUTTON_PRESSED) - { - if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected) - { - sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT); - sAppTask.mFunction = kFunction_StartBleAdv; - } - } - else - { - // If the button was released before factory reset got initiated, start BLE advertissement in fast mode - if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv) - { - sAppTask.CancelTimer(); - sAppTask.mFunction = kFunction_NoneSelected; - -#ifdef SL_WIFI - if (!ConnectivityMgr().IsWiFiStationProvisioned()) -#else - if (!ConnectivityMgr().IsThreadProvisioned()) -#endif /* !SL_WIFI */ - { - // Enable BLE advertisements - ConnectivityMgr().SetBLEAdvertisingEnabled(true); - ConnectivityMgr().SetBLEAdvertisingMode(ConnectivityMgr().kFastAdvertising); - } - else { EFR32_LOG("Network is already provisioned, Ble advertissement not enabled"); } - } - else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) - { - // Set Light status LED back to show state of light. - sLightLED.Set(LightMgr().IsLightOn()); - - sAppTask.CancelTimer(); - - // Change the function to none selected since factory reset has been - // canceled. - sAppTask.mFunction = kFunction_NoneSelected; - - EFR32_LOG("Factory Reset has been Canceled"); - } - } -} - -void AppTask::CancelTimer() -{ - if (xTimerStop(sFunctionTimer, 0) == pdFAIL) - { - EFR32_LOG("app timer stop() failed"); - appError(APP_ERROR_STOP_TIMER_FAILED); - } - - mFunctionTimerActive = false; -} - -void AppTask::StartTimer(uint32_t aTimeoutInMs) -{ - if (xTimerIsTimerActive(sFunctionTimer)) - { - EFR32_LOG("app timer already started!"); - CancelTimer(); - } - - // timer is not active, change its period to required value (== restart). - // FreeRTOS- Block for a maximum of 100 ticks if the change period command - // cannot immediately be sent to the timer command queue. - if (xTimerChangePeriod(sFunctionTimer, aTimeoutInMs / portTICK_PERIOD_MS, 100) != pdPASS) - { - EFR32_LOG("app timer start() failed"); - appError(APP_ERROR_START_TIMER_FAILED); - } - - mFunctionTimerActive = true; -} - void AppTask::ActionInitiated(LightingManager::Action_t aAction, int32_t aActor) { // Action initiated, update the light led @@ -581,50 +281,6 @@ void AppTask::PostLightActionRequest(int32_t aActor, LightingManager::Action_t a PostEvent(&event); } -void AppTask::PostEvent(const AppEvent * aEvent) -{ - if (sAppEventQueue != NULL) - { - BaseType_t status; - if (xPortIsInsideInterrupt()) - { - BaseType_t higherPrioTaskWoken = pdFALSE; - status = xQueueSendFromISR(sAppEventQueue, aEvent, &higherPrioTaskWoken); - -#ifdef portYIELD_FROM_ISR - portYIELD_FROM_ISR(higherPrioTaskWoken); -#elif portEND_SWITCHING_ISR // portYIELD_FROM_ISR or portEND_SWITCHING_ISR - portEND_SWITCHING_ISR(higherPrioTaskWoken); -#else // portYIELD_FROM_ISR or portEND_SWITCHING_ISR -#error "Must have portYIELD_FROM_ISR or portEND_SWITCHING_ISR" -#endif // portYIELD_FROM_ISR or portEND_SWITCHING_ISR - } - else - { - status = xQueueSend(sAppEventQueue, aEvent, 1); - } - - if (!status) - EFR32_LOG("Failed to post event to app task event queue"); - } - else - { - EFR32_LOG("Event Queue is NULL should never happen"); - } -} - -void AppTask::DispatchEvent(AppEvent * aEvent) -{ - if (aEvent->Handler) - { - aEvent->Handler(aEvent); - } - else - { - EFR32_LOG("Event received with no handler. Dropping event."); - } -} - void AppTask::UpdateClusterState(intptr_t context) { uint8_t newValue = LightMgr().IsLightOn(); diff --git a/examples/lighting-app/efr32/src/LightingManager.cpp b/examples/lighting-app/efr32/src/LightingManager.cpp index 47791911ea7308..ac5f86a1fb0faf 100644 --- a/examples/lighting-app/efr32/src/LightingManager.cpp +++ b/examples/lighting-app/efr32/src/LightingManager.cpp @@ -202,7 +202,7 @@ void LightingManager::TimerEventHandler(TimerHandle_t xTimer) { event.Handler = ActuatorMovementTimerEventHandler; } - GetAppTask().PostEvent(&event); + AppTask::GetAppTask().PostEvent(&event); } void LightingManager::AutoTurnOffTimerEventHandler(AppEvent * aEvent) diff --git a/examples/lighting-app/efr32/src/main.cpp b/examples/lighting-app/efr32/src/main.cpp index 79ef68cc720c88..c9838ac87b9e5a 100644 --- a/examples/lighting-app/efr32/src/main.cpp +++ b/examples/lighting-app/efr32/src/main.cpp @@ -66,7 +66,7 @@ int main(void) chip::DeviceLayer::PlatformMgr().UnlockChipStack(); EFR32_LOG("Starting App Task"); - if (GetAppTask().StartAppTask() != CHIP_NO_ERROR) + if (AppTask::GetAppTask().StartAppTask() != CHIP_NO_ERROR) appError(CHIP_ERROR_INTERNAL); EFR32_LOG("Starting FreeRTOS scheduler"); @@ -80,5 +80,5 @@ int main(void) void sl_button_on_change(const sl_button_t * handle) { - GetAppTask().ButtonEventHandler(handle, sl_button_get_state(handle)); + AppTask::GetAppTask().ButtonEventHandler(handle, sl_button_get_state(handle)); } diff --git a/examples/lock-app/efr32/BUILD.gn b/examples/lock-app/efr32/BUILD.gn index 2dec5b59cbba71..a4b32d8f6c7c80 100644 --- a/examples/lock-app/efr32/BUILD.gn +++ b/examples/lock-app/efr32/BUILD.gn @@ -168,6 +168,7 @@ efr32_executable("lock_app") { defines = [] sources = [ + "${examples_plat_dir}/BaseApplication.cpp", "${examples_plat_dir}/LEDWidget.cpp", "${examples_plat_dir}/efr32_utils.cpp", "${examples_plat_dir}/heap_4_silabs.c", diff --git a/examples/lock-app/efr32/include/AppTask.h b/examples/lock-app/efr32/include/AppTask.h index 7dab19d18c0728..a28837e776b85a 100644 --- a/examples/lock-app/efr32/include/AppTask.h +++ b/examples/lock-app/efr32/include/AppTask.h @@ -19,18 +19,28 @@ #pragma once +/********************************************************** + * Includes + *********************************************************/ + #include #include #include "AppEvent.h" +#include "BaseApplication.h" +#include "FreeRTOS.h" #include "LockManager.h" #include "sl_simple_button_instances.h" - -#include "FreeRTOS.h" #include "timers.h" // provides FreeRTOS timer support +#include #include +#include #include +/********************************************************** + * Defines + *********************************************************/ + // Application-defined error codes in the CHIP_ERROR space. #define APP_ERROR_EVENT_QUEUE_FAILED CHIP_APPLICATION_ERROR(0x01) #define APP_ERROR_CREATE_TASK_FAILED CHIP_APPLICATION_ERROR(0x02) @@ -40,57 +50,93 @@ #define APP_ERROR_STOP_TIMER_FAILED CHIP_APPLICATION_ERROR(0x06) #define APP_ERROR_ALLOCATION_FAILED CHIP_APPLICATION_ERROR(0x07) -class AppTask +/********************************************************** + * AppTask Declaration + *********************************************************/ + +class AppTask : public BaseApplication { public: - CHIP_ERROR StartAppTask(); - static void AppTaskMain(void * pvParameter); - - void ActionRequest(int32_t aActor, LockManager::Action_t aAction); - void PostEvent(const AppEvent * event); + AppTask() = default; - void ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAction); + static AppTask & GetAppTask() { return sAppTask; } -private: - friend AppTask & GetAppTask(void); + /** + * @brief AppTask task main loop function + * + * @param pvParameter FreeRTOS task parameter + */ + static void AppTaskMain(void * pvParameter); - CHIP_ERROR Init(); + CHIP_ERROR StartAppTask(); + void ActionRequest(int32_t aActor, LockManager::Action_t aAction); static void ActionInitiated(LockManager::Action_t aAction, int32_t aActor); static void ActionCompleted(LockManager::Action_t aAction); - void CancelTimer(void); + /** + * @brief Event handler when a button is pressed + * Function posts an event for button processing + * + * @param buttonHandle APP_LIGHT_SWITCH or APP_FUNCTION_BUTTON + * @param btnAction button action - SL_SIMPLE_BUTTON_PRESSED, + * SL_SIMPLE_BUTTON_RELEASED or SL_SIMPLE_BUTTON_DISABLED + */ + void ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAction) override; + + /** + * @brief Callback called by the identify-server when an identify command is received + * + * @param identify identify structure the command applies on + */ + static void OnIdentifyStart(Identify * identify); + + /** + * @brief Callback called by the identify-server when an identify command is stopped or finished + * + * @param identify identify structure the command applies on + */ + static void OnIdentifyStop(Identify * identify); - void DispatchEvent(AppEvent * event); +private: + static AppTask sAppTask; - static void FunctionTimerEventHandler(AppEvent * aEvent); - static void FunctionHandler(AppEvent * aEvent); - static void LockActionEventHandler(AppEvent * aEvent); - static void TimerEventHandler(TimerHandle_t xTimer); + /** + * @brief AppTask initialisation function + * + * @return CHIP_ERROR + */ + CHIP_ERROR Init(); + /** + * @brief PB0 Button event processing function + * Press and hold will trigger a factory reset timer start + * Press and release will restart BLEAdvertising if not commisionned + * + * @param aEvent button event being processed + */ + static void ButtonHandler(AppEvent * aEvent); + + /** + * @brief PB1 Button event processing function + * Function triggers a switch action sent to the CHIP task + * + * @param aEvent button event being processed + */ + static void SwitchActionEventHandler(AppEvent * aEvent); + + /** + * @brief Update Cluster State + * + * @param context current context + */ static void UpdateClusterState(intptr_t context); - void StartTimer(uint32_t aTimeoutMs); - - enum Function_t - { - kFunction_NoneSelected = 0, - kFunction_SoftwareUpdate = 0, - kFunction_StartBleAdv = 1, - kFunction_FactoryReset = 2, - - kFunction_Invalid - } Function; - - Function_t mFunction; - bool mFunctionTimerActive; - bool mSyncClusterToButtonAction; - - static AppTask sAppTask; + /** + * @brief Handle lock update event + * + * @param aEvent event received + */ + static void LockActionEventHandler(AppEvent * aEvent); }; - -inline AppTask & GetAppTask(void) -{ - return AppTask::sAppTask; -} diff --git a/examples/lock-app/efr32/src/AppTask.cpp b/examples/lock-app/efr32/src/AppTask.cpp index ac3914476a3f1e..04def4fe698fd3 100644 --- a/examples/lock-app/efr32/src/AppTask.cpp +++ b/examples/lock-app/efr32/src/AppTask.cpp @@ -51,22 +51,6 @@ #include #include -#if CHIP_ENABLE_OPENTHREAD -#include -#include -#include -#endif -#ifdef SL_WIFI -#include "wfx_host_events.h" -#include -#include -#endif /* SL_WIFI */ - -#define FACTORY_RESET_TRIGGER_TIMEOUT 3000 -#define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000 -#define APP_TASK_STACK_SIZE (4096) -#define APP_TASK_PRIORITY 2 -#define APP_EVENT_QUEUE_SIZE 10 #define SYSTEM_STATE_LED &sl_led_led0 #define LOCK_STATE_LED &sl_led_led1 @@ -83,38 +67,10 @@ using namespace ::chip::DeviceLayer::Internal; using namespace EFR32DoorLock::LockInitParams; namespace { -TimerHandle_t sFunctionTimer; // FreeRTOS app sw timer. - -TaskHandle_t sAppTaskHandle; -QueueHandle_t sAppEventQueue; - -LEDWidget sStatusLED; LEDWidget sLockLED; -#ifdef SL_WIFI -bool sIsWiFiProvisioned = false; -bool sIsWiFiEnabled = false; -bool sIsWiFiAttached = false; - -app::Clusters::NetworkCommissioning::Instance - sWiFiNetworkCommissioningInstance(0 /* Endpoint Id */, &(NetworkCommissioning::SlWiFiDriver::GetInstance())); -#endif /* SL_WIFI */ - -#if CHIP_ENABLE_OPENTHREAD -bool sIsThreadProvisioned = false; -bool sIsThreadEnabled = false; -#endif /* CHIP_ENABLE_OPENTHREAD */ -bool sHaveBLEConnections = false; -bool configValueSet = false; - EmberAfIdentifyEffectIdentifier sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; - -uint8_t sAppEventQueueBuffer[APP_EVENT_QUEUE_SIZE * sizeof(AppEvent)]; -StaticQueue_t sAppEventQueueStruct; - -StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)]; -StaticTask_t appTaskStruct; - +} // namespace /********************************************************** * Identify Callbacks *********************************************************/ @@ -124,7 +80,6 @@ void OnTriggerIdentifyEffectCompleted(chip::System::Layer * systemLayer, void * { sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; } -} // namespace void OnTriggerIdentifyEffect(Identify * identify) { @@ -174,54 +129,17 @@ using namespace ::chip::DeviceLayer; AppTask AppTask::sAppTask; -CHIP_ERROR AppTask::StartAppTask() -{ - sAppEventQueue = xQueueCreateStatic(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent), sAppEventQueueBuffer, &sAppEventQueueStruct); - if (sAppEventQueue == NULL) - { - EFR32_LOG("Failed to allocate app event queue"); - appError(APP_ERROR_EVENT_QUEUE_FAILED); - } - - // Start App task. - sAppTaskHandle = xTaskCreateStatic(AppTaskMain, APP_TASK_NAME, ArraySize(appStack), NULL, 1, appStack, &appTaskStruct); - return (sAppTaskHandle == nullptr) ? APP_ERROR_CREATE_TASK_FAILED : CHIP_NO_ERROR; -} - CHIP_ERROR AppTask::Init() { CHIP_ERROR err = CHIP_NO_ERROR; -#ifdef SL_WIFI - /* - * Wait for the WiFi to be initialized - */ - EFR32_LOG("APP: Wait WiFi Init"); - while (!wfx_hw_ready()) - { - vTaskDelay(10); - } - EFR32_LOG("APP: Done WiFi Init"); - /* We will init server when we get IP */ - - sWiFiNetworkCommissioningInstance.Init(); -#endif - - // Create FreeRTOS sw timer for Function Selection. - sFunctionTimer = xTimerCreate("FnTmr", // Just a text name, not used by the RTOS kernel - 1, // == default timer period (mS) - false, // no timer reload (==one-shot) - (void *) this, // init timer id = app task obj context - TimerEventHandler // timer callback handler - ); - if (sFunctionTimer == NULL) + err = BaseApplication::Init(&gIdentify); + if (err != CHIP_NO_ERROR) { - EFR32_LOG("funct timer create failed"); - appError(APP_ERROR_CREATE_TIMER_FAILED); + EFR32_LOG("BaseApplication::Init() failed"); + appError(err); } - EFR32_LOG("Current Software Version: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); - // Initial lock state chip::app::DataModel::Nullable state; chip::EndpointId endpointId{ 1 }; @@ -297,47 +215,25 @@ CHIP_ERROR AppTask::Init() LockMgr().SetCallbacks(ActionInitiated, ActionCompleted); // Initialize LEDs - LEDWidget::InitGpio(); - sStatusLED.Init(SYSTEM_STATE_LED); sLockLED.Init(LOCK_STATE_LED); - - if (state.Value() == DlLockState::kUnlocked) - { - sLockLED.Set(true); - } - else - { - sLockLED.Set(false); - } + sLockLED.Set(state.Value() == DlLockState::kUnlocked); chip::DeviceLayer::PlatformMgr().ScheduleWork(UpdateClusterState, reinterpret_cast(nullptr)); ConfigurationMgr().LogDeviceConfig(); -// Print setup info on LCD if available -#ifdef QR_CODE_ENABLED - // Create buffer for QR code that can fit max size and null terminator. - char qrCodeBuffer[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1]; - chip::MutableCharSpan QRCode(qrCodeBuffer); - - if (GetQRCode(QRCode, chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)) == CHIP_NO_ERROR) - { - LCDWriteQRCode((uint8_t *) QRCode.data()); - } - else - { - EFR32_LOG("Getting QR code failed!"); - } -#else - PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE)); -#endif // QR_CODE_ENABLED - return err; } +CHIP_ERROR AppTask::StartAppTask() +{ + return BaseApplication::StartAppTask(AppTaskMain); +} + void AppTask::AppTaskMain(void * pvParameter) { AppEvent event; + QueueHandle_t sAppEventQueue = *(static_cast(pvParameter)); CHIP_ERROR err = sAppTask.Init(); if (err != CHIP_NO_ERROR) @@ -348,89 +244,17 @@ void AppTask::AppTaskMain(void * pvParameter) EFR32_LOG("App Task started"); + // Users and credentials should be checked once from nvm flash on boot + LockMgr().ReadConfigValues(); + while (true) { - // Users and credentials should be checked once from nvm flash on boot - if (!configValueSet) - { - LockMgr().ReadConfigValues(); - configValueSet = true; - } - BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, pdMS_TO_TICKS(10)); while (eventReceived == pdTRUE) { sAppTask.DispatchEvent(&event); eventReceived = xQueueReceive(sAppEventQueue, &event, 0); } - - // Collect connectivity and configuration state from the CHIP stack. Because - // the CHIP event loop is being run in a separate task, the stack must be - // locked while these values are queried. However we use a non-blocking - // lock request (TryLockCHIPStack()) to avoid blocking other UI activities - // when the CHIP task is busy (e.g. with a long crypto operation). - if (PlatformMgr().TryLockChipStack()) - { -#ifdef SL_WIFI - sIsWiFiProvisioned = ConnectivityMgr().IsWiFiStationProvisioned(); - sIsWiFiEnabled = ConnectivityMgr().IsWiFiStationEnabled(); - sIsWiFiAttached = ConnectivityMgr().IsWiFiStationConnected(); -#endif /* SL_WIFI */ -#if CHIP_ENABLE_OPENTHREAD - sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned(); - sIsThreadEnabled = ConnectivityMgr().IsThreadEnabled(); -#endif /* CHIP_ENABLE_OPENTHREAD */ - sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0); - PlatformMgr().UnlockChipStack(); - } - - // Update the status LED if factory reset has not been initiated. - // - // If system has "full connectivity", keep the LED On constantly. - // - // If thread and service provisioned, but not attached to the thread network - // yet OR no connectivity to the service OR subscriptions are not fully - // established THEN blink the LED Off for a short period of time. - // - // If the system has ble connection(s) uptill the stage above, THEN blink - // the LEDs at an even rate of 100ms. - // - // Otherwise, blink the LED ON for a very short time. - if (sAppTask.mFunction != kFunction_FactoryReset) - { - if (gIdentify.mActive) - { - sStatusLED.Blink(250, 250); - } - if (sIdentifyEffect != EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT) - { - if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BLINK) - { - sStatusLED.Blink(50, 50); - } - if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BREATHE) - { - sStatusLED.Blink(1000, 1000); - } - if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_OKAY) - { - sStatusLED.Blink(300, 700); - } - } -#if CHIP_ENABLE_OPENTHREAD - if (sIsThreadProvisioned && sIsThreadEnabled) -#else - if (sIsWiFiProvisioned && sIsWiFiEnabled && !sIsWiFiAttached) -#endif - { - sStatusLED.Blink(950, 50); - } - else if (sHaveBLEConnections) { sStatusLED.Blink(100, 100); } - else { sStatusLED.Blink(50, 950); } - } - - sStatusLED.Animate(); - sLockLED.Animate(); } } @@ -492,139 +316,11 @@ void AppTask::ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAc } else if (buttonHandle == APP_FUNCTION_BUTTON) { - button_event.Handler = FunctionHandler; + button_event.Handler = BaseApplication::ButtonHandler; sAppTask.PostEvent(&button_event); } } -void AppTask::TimerEventHandler(TimerHandle_t xTimer) -{ - AppEvent event; - event.Type = AppEvent::kEventType_Timer; - event.TimerEvent.Context = (void *) xTimer; - event.Handler = FunctionTimerEventHandler; - sAppTask.PostEvent(&event); -} - -void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) -{ - if (aEvent->Type != AppEvent::kEventType_Timer) - { - return; - } - - // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT, - // initiate factory reset - if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv) - { - EFR32_LOG("Factory Reset Triggered. Release button within %ums to cancel.", FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); - - // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to - // cancel, if required. - sAppTask.StartTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); - - sAppTask.mFunction = kFunction_FactoryReset; - - // Turn off all LEDs before starting blink to make sure blink is - // co-ordinated. - sStatusLED.Set(false); - sLockLED.Set(false); - - sStatusLED.Blink(500); - sLockLED.Blink(500); - } - else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) - { - // Actually trigger Factory Reset - sAppTask.mFunction = kFunction_NoneSelected; - chip::Server::GetInstance().ScheduleFactoryReset(); - } -} - -void AppTask::FunctionHandler(AppEvent * aEvent) -{ - // To trigger software update: press the APP_FUNCTION_BUTTON button briefly (< - // FACTORY_RESET_TRIGGER_TIMEOUT) To initiate factory reset: press the - // APP_FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT + - // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT All LEDs start blinking after - // FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated. - // To cancel factory reset: release the APP_FUNCTION_BUTTON once all LEDs - // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT - if (aEvent->ButtonEvent.Action == SL_SIMPLE_BUTTON_PRESSED) - { - if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected) - { - sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT); - sAppTask.mFunction = kFunction_StartBleAdv; - } - } - else - { - // If the button was released before factory reset got initiated, start BLE advertissement in fast mode - if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv) - { - sAppTask.CancelTimer(); - sAppTask.mFunction = kFunction_NoneSelected; - -#ifdef SL_WIFI - if (!ConnectivityMgr().IsWiFiStationProvisioned()) -#else - if (!ConnectivityMgr().IsThreadProvisioned()) -#endif /* !SL_WIFI */ - { - // Enable BLE advertisements - ConnectivityMgr().SetBLEAdvertisingEnabled(true); - ConnectivityMgr().SetBLEAdvertisingMode(ConnectivityMgr().kFastAdvertising); - } - else { EFR32_LOG("Network is already provisioned, Ble advertissement not enabled"); } - } - else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) - { - // Set lock status LED back to show state of lock. - sLockLED.Set(!LockMgr().NextState()); - - sAppTask.CancelTimer(); - - // Change the function to none selected since factory reset has been - // canceled. - sAppTask.mFunction = kFunction_NoneSelected; - - EFR32_LOG("Factory Reset has been Canceled"); - } - } -} - -void AppTask::CancelTimer() -{ - if (xTimerStop(sFunctionTimer, 0) == pdFAIL) - { - EFR32_LOG("app timer stop() failed"); - appError(APP_ERROR_STOP_TIMER_FAILED); - } - - mFunctionTimerActive = false; -} - -void AppTask::StartTimer(uint32_t aTimeoutInMs) -{ - if (xTimerIsTimerActive(sFunctionTimer)) - { - EFR32_LOG("app timer already started!"); - CancelTimer(); - } - - // timer is not active, change its period to required value (== restart). - // FreeRTOS- Block for a maximum of 100 ticks if the change period command - // cannot immediately be sent to the timer command queue. - if (xTimerChangePeriod(sFunctionTimer, aTimeoutInMs / portTICK_PERIOD_MS, 100) != pdPASS) - { - EFR32_LOG("app timer start() failed"); - appError(APP_ERROR_START_TIMER_FAILED); - } - - mFunctionTimerActive = true; -} - void AppTask::ActionInitiated(LockManager::Action_t aAction, int32_t aActor) { // Action initiated, update the light led @@ -676,50 +372,6 @@ void AppTask::ActionRequest(int32_t aActor, LockManager::Action_t aAction) PostEvent(&event); } -void AppTask::PostEvent(const AppEvent * aEvent) -{ - if (sAppEventQueue != NULL) - { - BaseType_t status; - if (xPortIsInsideInterrupt()) - { - BaseType_t higherPrioTaskWoken = pdFALSE; - status = xQueueSendFromISR(sAppEventQueue, aEvent, &higherPrioTaskWoken); - -#ifdef portYIELD_FROM_ISR - portYIELD_FROM_ISR(higherPrioTaskWoken); -#elif portEND_SWITCHING_ISR // portYIELD_FROM_ISR or portEND_SWITCHING_ISR - portEND_SWITCHING_ISR(higherPrioTaskWoken); -#else // portYIELD_FROM_ISR or portEND_SWITCHING_ISR -#error "Must have portYIELD_FROM_ISR or portEND_SWITCHING_ISR" -#endif // portYIELD_FROM_ISR or portEND_SWITCHING_ISR - } - else - { - status = xQueueSend(sAppEventQueue, aEvent, 1); - } - - if (!status) - EFR32_LOG("Failed to post event to app task event queue"); - } - else - { - EFR32_LOG("Event Queue is NULL should never happen"); - } -} - -void AppTask::DispatchEvent(AppEvent * aEvent) -{ - if (aEvent->Handler) - { - aEvent->Handler(aEvent); - } - else - { - EFR32_LOG("Event received with no handler. Dropping event."); - } -} - void AppTask::UpdateClusterState(intptr_t context) { bool unlocked = LockMgr().NextState(); diff --git a/examples/lock-app/efr32/src/LockManager.cpp b/examples/lock-app/efr32/src/LockManager.cpp index 57c1ee68584550..dba71262b9272e 100644 --- a/examples/lock-app/efr32/src/LockManager.cpp +++ b/examples/lock-app/efr32/src/LockManager.cpp @@ -254,7 +254,7 @@ void LockManager::TimerEventHandler(TimerHandle_t xTimer) event.Type = AppEvent::kEventType_Timer; event.TimerEvent.Context = lock; event.Handler = ActuatorMovementTimerEventHandler; - GetAppTask().PostEvent(&event); + AppTask::GetAppTask().PostEvent(&event); } void LockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent) diff --git a/examples/lock-app/efr32/src/main.cpp b/examples/lock-app/efr32/src/main.cpp index a8ba160e7e2915..0856b9f11baf83 100644 --- a/examples/lock-app/efr32/src/main.cpp +++ b/examples/lock-app/efr32/src/main.cpp @@ -66,7 +66,7 @@ int main(void) chip::DeviceLayer::PlatformMgr().UnlockChipStack(); EFR32_LOG("Starting App Task"); - if (GetAppTask().StartAppTask() != CHIP_NO_ERROR) + if (AppTask::GetAppTask().StartAppTask() != CHIP_NO_ERROR) appError(CHIP_ERROR_INTERNAL); EFR32_LOG("Starting FreeRTOS scheduler"); @@ -80,5 +80,5 @@ int main(void) void sl_button_on_change(const sl_button_t * handle) { - GetAppTask().ButtonEventHandler(handle, sl_button_get_state(handle)); + AppTask::GetAppTask().ButtonEventHandler(handle, sl_button_get_state(handle)); } diff --git a/examples/platform/efr32/BaseApplication.cpp b/examples/platform/efr32/BaseApplication.cpp new file mode 100644 index 00000000000000..47dbb9c001361d --- /dev/null +++ b/examples/platform/efr32/BaseApplication.cpp @@ -0,0 +1,516 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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. + */ + +/********************************************************** + * Includes + *********************************************************/ + +#include "AppConfig.h" +#include "AppEvent.h" +#include "AppTask.h" +#include "LEDWidget.h" +#include "sl_simple_led_instances.h" + +#ifdef DISPLAY_ENABLED +#include "lcd.h" +#ifdef QR_CODE_ENABLED +#include "qrcodegen.h" +#endif // QR_CODE_ENABLED +#endif // DISPLAY_ENABLED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if CHIP_ENABLE_OPENTHREAD +#include +#include +#include +#endif // CHIP_ENABLE_OPENTHREAD + +#ifdef SL_WIFI +#include "wfx_host_events.h" +#include +#include +#endif // SL_WIFI + +/********************************************************** + * Defines and Constants + *********************************************************/ + +#define FACTORY_RESET_TRIGGER_TIMEOUT 3000 +#define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000 +#ifndef APP_TASK_STACK_SIZE +#define APP_TASK_STACK_SIZE (4096) +#endif +#define APP_TASK_PRIORITY 2 +#define APP_EVENT_QUEUE_SIZE 10 +#define EXAMPLE_VENDOR_ID 0xcafe + +#define SYSTEM_STATE_LED &sl_led_led0 + +#define APP_FUNCTION_BUTTON &sl_button_btn0 + +using namespace chip; +using namespace ::chip::DeviceLayer; + +namespace { + +/********************************************************** + * Variable declarations + *********************************************************/ + +TimerHandle_t sFunctionTimer; // FreeRTOS app sw timer. +TimerHandle_t sLightTimer; + +TaskHandle_t sAppTaskHandle; +QueueHandle_t sAppEventQueue; + +LEDWidget sStatusLED; + +#ifdef SL_WIFI +app::Clusters::NetworkCommissioning::Instance + sWiFiNetworkCommissioningInstance(0 /* Endpoint Id */, &(NetworkCommissioning::SlWiFiDriver::GetInstance())); +#endif /* SL_WIFI */ + +#if !(defined(CHIP_DEVICE_CONFIG_ENABLE_SED) && CHIP_DEVICE_CONFIG_ENABLE_SED) + +bool sIsProvisioned = false; +bool sIsEnabled = false; +bool sIsAttached = false; +bool sHaveBLEConnections = false; + +#endif // CHIP_DEVICE_CONFIG_ENABLE_SED + +EmberAfIdentifyEffectIdentifier sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; + +uint8_t sAppEventQueueBuffer[APP_EVENT_QUEUE_SIZE * sizeof(AppEvent)]; +StaticQueue_t sAppEventQueueStruct; + +StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)]; +StaticTask_t appTaskStruct; + +BaseApplication::Function_t mFunction; +bool mFunctionTimerActive; + +Identify * gIdentifyptr = nullptr; + +} // namespace + +/********************************************************** + * AppTask Definitions + *********************************************************/ + +CHIP_ERROR BaseApplication::StartAppTask(TaskFunction_t taskFunction) +{ + sAppEventQueue = xQueueCreateStatic(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent), sAppEventQueueBuffer, &sAppEventQueueStruct); + if (sAppEventQueue == NULL) + { + EFR32_LOG("Failed to allocate app event queue"); + appError(APP_ERROR_EVENT_QUEUE_FAILED); + } + + // Start App task. + sAppTaskHandle = + xTaskCreateStatic(taskFunction, APP_TASK_NAME, ArraySize(appStack), &sAppEventQueue, 1, appStack, &appTaskStruct); + if (sAppTaskHandle == nullptr) + { + EFR32_LOG("Failed to create app task"); + appError(APP_ERROR_CREATE_TASK_FAILED); + } + return CHIP_NO_ERROR; +} + +CHIP_ERROR BaseApplication::Init(Identify * identifyObj) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + if (identifyObj == nullptr) + { + EFR32_LOG("funct timer create failed"); + appError(CHIP_ERROR_INVALID_ARGUMENT); + } + + gIdentifyptr = identifyObj; + +#ifdef SL_WIFI + /* + * Wait for the WiFi to be initialized + */ + EFR32_LOG("APP: Wait WiFi Init"); + while (!wfx_hw_ready()) + { + vTaskDelay(10); + } + EFR32_LOG("APP: Done WiFi Init"); + /* We will init server when we get IP */ + + sWiFiNetworkCommissioningInstance.Init(); +#endif + + // Create FreeRTOS sw timer for Function Selection. + sFunctionTimer = xTimerCreate("FnTmr", // Just a text name, not used by the RTOS kernel + 1, // == default timer period (mS) + false, // no timer reload (==one-shot) + (void *) this, // init timer id = app task obj context + FunctionTimerEventHandler // timer callback handler + ); + if (sFunctionTimer == NULL) + { + EFR32_LOG("funct timer create failed"); + appError(APP_ERROR_CREATE_TIMER_FAILED); + } + + // Create FreeRTOS sw timer for LED Management. + sLightTimer = xTimerCreate("LightTmr", // Text Name + 10, // Default timer period (mS) + true, // reload timer + (void *) this, // Timer Id + LightTimerEventHandler // Timer callback handler + ); + if (sLightTimer == NULL) + { + EFR32_LOG("Light Timer create failed"); + appError(APP_ERROR_CREATE_TIMER_FAILED); + } + + EFR32_LOG("Current Software Version: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); + + LEDWidget::InitGpio(); + sStatusLED.Init(SYSTEM_STATE_LED); + + ConfigurationMgr().LogDeviceConfig(); + +// Print setup info on LCD if available +#ifdef QR_CODE_ENABLED + // Create buffer for QR code that can fit max size and null terminator. + char qrCodeBuffer[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1]; + chip::MutableCharSpan QRCode(qrCodeBuffer); + + if (GetQRCode(QRCode, chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)) == CHIP_NO_ERROR) + { + LCDWriteQRCode((uint8_t *) QRCode.data()); + } + else + { + EFR32_LOG("Getting QR code failed!"); + } +#else + PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE)); +#endif // QR_CODE_ENABLED + + return err; +} + +void BaseApplication::FunctionTimerEventHandler(TimerHandle_t xTimer) +{ + AppEvent event; + event.Type = AppEvent::kEventType_Timer; + event.TimerEvent.Context = (void *) xTimer; + event.Handler = FunctionEventHandler; + PostEvent(&event); +} + +void BaseApplication::FunctionEventHandler(AppEvent * aEvent) +{ + if (aEvent->Type != AppEvent::kEventType_Timer) + { + return; + } + + // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT, + // initiate factory reset + if (mFunctionTimerActive && mFunction == kFunction_StartBleAdv) + { + EFR32_LOG("Factory Reset Triggered. Release button within %ums to cancel.", FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); + + // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to + // cancel, if required. + StartFunctionTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); + +#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 + StartStatusLEDTimer(); +#endif // CHIP_DEVICE_CONFIG_ENABLE_SED + + mFunction = kFunction_FactoryReset; + + // Turn off all LEDs before starting blink to make sure blink is + // co-ordinated. + sStatusLED.Set(false); + sStatusLED.Blink(500); + } + else if (mFunctionTimerActive && mFunction == kFunction_FactoryReset) + { + // Actually trigger Factory Reset + mFunction = kFunction_NoneSelected; + +#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 + StopStatusLEDTimer(); +#endif // CHIP_DEVICE_CONFIG_ENABLE_SED + + chip::Server::GetInstance().ScheduleFactoryReset(); + } +} + +void BaseApplication::LightEventHandler() +{ + // Collect connectivity and configuration state from the CHIP stack. Because + // the CHIP event loop is being run in a separate task, the stack must be + // locked while these values are queried. However we use a non-blocking + // lock request (TryLockCHIPStack()) to avoid blocking other UI activities + // when the CHIP task is busy (e.g. with a long crypto operation). +#if !(defined(CHIP_DEVICE_CONFIG_ENABLE_SED) && CHIP_DEVICE_CONFIG_ENABLE_SED) + if (PlatformMgr().TryLockChipStack()) + { +#ifdef SL_WIFI + sIsProvisioned = ConnectivityMgr().IsWiFiStationProvisioned(); + sIsEnabled = ConnectivityMgr().IsWiFiStationEnabled(); + sIsAttached = ConnectivityMgr().IsWiFiStationConnected(); +#endif /* SL_WIFI */ +#if CHIP_ENABLE_OPENTHREAD + sIsProvisioned = ConnectivityMgr().IsThreadProvisioned(); + sIsEnabled = ConnectivityMgr().IsThreadEnabled(); + sIsAttached = ConnectivityMgr().IsThreadAttached(); +#endif /* CHIP_ENABLE_OPENTHREAD */ + sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0); + PlatformMgr().UnlockChipStack(); + } +#endif // CHIP_DEVICE_CONFIG_ENABLE_SED + + // Update the status LED if factory reset has not been initiated. + // + // If system has "full connectivity", keep the LED On constantly. + // + // If thread and service provisioned, but not attached to the thread network + // yet OR no connectivity to the service OR subscriptions are not fully + // established THEN blink the LED Off for a short period of time. + // + // If the system has ble connection(s) uptill the stage above, THEN blink + // the LEDs at an even rate of 100ms. + // + // Otherwise, blink the LED ON for a very short time. + if (mFunction != kFunction_FactoryReset) + { + if ((gIdentifyptr != nullptr) && (gIdentifyptr->mActive)) + { + sStatusLED.Blink(250, 250); + } + else if (sIdentifyEffect != EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT) + { + if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BLINK) + { + sStatusLED.Blink(50, 50); + } + if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BREATHE) + { + sStatusLED.Blink(1000, 1000); + } + if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_OKAY) + { + sStatusLED.Blink(300, 700); + } + } +#if !(defined(CHIP_DEVICE_CONFIG_ENABLE_SED) && CHIP_DEVICE_CONFIG_ENABLE_SED) + else if (sIsProvisioned && sIsEnabled) + { + if (sIsAttached) + { + sStatusLED.Set(true); + } + else + { + sStatusLED.Blink(950, 50); + } + } + else if (sHaveBLEConnections) + { + sStatusLED.Blink(100, 100); + } + else + { + sStatusLED.Blink(50, 950); + } +#endif // CHIP_DEVICE_CONFIG_ENABLE_SED + } + + sStatusLED.Animate(); +} + +void BaseApplication::ButtonHandler(AppEvent * aEvent) +{ + // To trigger software update: press the APP_FUNCTION_BUTTON button briefly (< + // FACTORY_RESET_TRIGGER_TIMEOUT) To initiate factory reset: press the + // APP_FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT + + // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT All LEDs start blinking after + // FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated. + // To cancel factory reset: release the APP_FUNCTION_BUTTON once all LEDs + // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT + if (aEvent->ButtonEvent.Action == SL_SIMPLE_BUTTON_PRESSED) + { + if (!mFunctionTimerActive && mFunction == kFunction_NoneSelected) + { + StartFunctionTimer(FACTORY_RESET_TRIGGER_TIMEOUT); + mFunction = kFunction_StartBleAdv; + } + } + else + { + // If the button was released before factory reset got initiated, start BLE advertissement in fast mode + if (mFunctionTimerActive && mFunction == kFunction_StartBleAdv) + { + CancelFunctionTimer(); + mFunction = kFunction_NoneSelected; + +#ifdef SL_WIFI + if (!ConnectivityMgr().IsWiFiStationProvisioned()) +#else + if (!ConnectivityMgr().IsThreadProvisioned()) +#endif /* !SL_WIFI */ + { + // Enable BLE advertisements + ConnectivityMgr().SetBLEAdvertisingEnabled(true); + ConnectivityMgr().SetBLEAdvertisingMode(ConnectivityMgr().kFastAdvertising); + } + else { EFR32_LOG("Network is already provisioned, Ble advertissement not enabled"); } + } + else if (mFunctionTimerActive && mFunction == kFunction_FactoryReset) + { + CancelFunctionTimer(); + +#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 + StopStatusLEDTimer(); +#endif + + // Change the function to none selected since factory reset has been + // canceled. + mFunction = kFunction_NoneSelected; + EFR32_LOG("Factory Reset has been Canceled"); + } + } +} + +void BaseApplication::CancelFunctionTimer() +{ + if (xTimerStop(sFunctionTimer, 0) == pdFAIL) + { + EFR32_LOG("app timer stop() failed"); + appError(APP_ERROR_STOP_TIMER_FAILED); + } + + mFunctionTimerActive = false; +} + +void BaseApplication::StartFunctionTimer(uint32_t aTimeoutInMs) +{ + if (xTimerIsTimerActive(sFunctionTimer)) + { + EFR32_LOG("app timer already started!"); + CancelFunctionTimer(); + } + + // timer is not active, change its period to required value (== restart). + // FreeRTOS- Block for a maximum of 100 ticks if the change period command + // cannot immediately be sent to the timer command queue. + if (xTimerChangePeriod(sFunctionTimer, aTimeoutInMs / portTICK_PERIOD_MS, 100) != pdPASS) + { + EFR32_LOG("app timer start() failed"); + appError(APP_ERROR_START_TIMER_FAILED); + } + + mFunctionTimerActive = true; +} + +void BaseApplication::StartStatusLEDTimer() +{ + if (pdPASS != xTimerStart(sLightTimer, 0)) + { + EFR32_LOG("Light Time start failed"); + appError(APP_ERROR_START_TIMER_FAILED); + } +} + +void BaseApplication::StopStatusLEDTimer() +{ + sStatusLED.Set(false); + if (xTimerStop(sLightTimer, 100) != pdPASS) + { + EFR32_LOG("Light Time start failed"); + appError(APP_ERROR_START_TIMER_FAILED); + } +} + +void BaseApplication::LightTimerEventHandler(TimerHandle_t xTimer) +{ + LightEventHandler(); +} + +void BaseApplication::PostEvent(const AppEvent * aEvent) +{ + if (sAppEventQueue != NULL) + { + BaseType_t status; + if (xPortIsInsideInterrupt()) + { + BaseType_t higherPrioTaskWoken = pdFALSE; + status = xQueueSendFromISR(sAppEventQueue, aEvent, &higherPrioTaskWoken); + +#ifdef portYIELD_FROM_ISR + portYIELD_FROM_ISR(higherPrioTaskWoken); +#elif portEND_SWITCHING_ISR // portYIELD_FROM_ISR or portEND_SWITCHING_ISR + portEND_SWITCHING_ISR(higherPrioTaskWoken); +#else // portYIELD_FROM_ISR or portEND_SWITCHING_ISR +#error "Must have portYIELD_FROM_ISR or portEND_SWITCHING_ISR" +#endif // portYIELD_FROM_ISR or portEND_SWITCHING_ISR + } + else + { + status = xQueueSend(sAppEventQueue, aEvent, 1); + } + + if (!status) + { + EFR32_LOG("Failed to post event to app task event queue"); + } + } + else + { + EFR32_LOG("Event Queue is NULL should never happen"); + } +} + +void BaseApplication::DispatchEvent(AppEvent * aEvent) +{ + if (aEvent->Handler) + { + aEvent->Handler(aEvent); + } + else + { + EFR32_LOG("Event received with no handler. Dropping event."); + } +} diff --git a/examples/platform/efr32/BaseApplication.h b/examples/platform/efr32/BaseApplication.h new file mode 100644 index 00000000000000..41e2c1dbe64ffd --- /dev/null +++ b/examples/platform/efr32/BaseApplication.h @@ -0,0 +1,172 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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. + */ + +#pragma once + +/********************************************************** + * Includes + *********************************************************/ + +#include +#include + +#include "AppEvent.h" +#include "FreeRTOS.h" +#include "sl_simple_button_instances.h" +#include "timers.h" // provides FreeRTOS timer support +#include +#include +#include +#include + +/********************************************************** + * Defines + *********************************************************/ + +// Application-defined error codes in the CHIP_ERROR space. +#define APP_ERROR_EVENT_QUEUE_FAILED CHIP_APPLICATION_ERROR(0x01) +#define APP_ERROR_CREATE_TASK_FAILED CHIP_APPLICATION_ERROR(0x02) +#define APP_ERROR_UNHANDLED_EVENT CHIP_APPLICATION_ERROR(0x03) +#define APP_ERROR_CREATE_TIMER_FAILED CHIP_APPLICATION_ERROR(0x04) +#define APP_ERROR_START_TIMER_FAILED CHIP_APPLICATION_ERROR(0x05) +#define APP_ERROR_STOP_TIMER_FAILED CHIP_APPLICATION_ERROR(0x06) + +/********************************************************** + * BaseApplication Declaration + *********************************************************/ + +class BaseApplication +{ + +public: + BaseApplication() = default; + virtual ~BaseApplication(){}; + + /** + * @brief Create AppTask task and Event Queue + * If an error occurs during creation, application will hang after printing out error code + * + * @return CHIP_ERROR CHIP_NO_ERROR if no errors + */ + CHIP_ERROR StartAppTask(TaskFunction_t taskFunction); + + /** + * @brief PostEvent function that add event to AppTask queue for processing + * + * @param event AppEvent to post + */ + static void PostEvent(const AppEvent * event); + + /** + * @brief Event handler when a button is pressed + * Function posts an event for button processing + * + * @param buttonHandle APP_LIGHT_SWITCH or APP_FUNCTION_BUTTON + * @param btnAction button action - SL_SIMPLE_BUTTON_PRESSED, + * SL_SIMPLE_BUTTON_RELEASED or SL_SIMPLE_BUTTON_DISABLED + */ + virtual void ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAction) = 0; + + /** + * @brief Function called to start the LED light timer + */ + void StartStatusLEDTimer(void); + + /** + * @brief Function to stop LED light timer + * Turns off Status LED before stopping timer + */ + void StopStatusLEDTimer(void); + + enum Function_t + { + kFunction_NoneSelected = 0, + kFunction_SoftwareUpdate = 0, + kFunction_StartBleAdv = 1, + kFunction_FactoryReset = 2, + + kFunction_Invalid + } Function; + +protected: + CHIP_ERROR Init(Identify * identifyObj); + + /** + * @brief Function called to start the function timer + * + * @param aTimeoutMs timer duration in ms + */ + static void StartFunctionTimer(uint32_t aTimeoutMs); + + /** + * @brief Function to stop function timer + */ + static void CancelFunctionTimer(void); + + /** + * @brief Function call event callback function for processing + * + * @param event triggered event to be processed + */ + void DispatchEvent(AppEvent * event); + + /** + * @brief Function Timer finished callback function + * Post an FunctionEventHandler event + * + * @param xTimer timer that finished + */ + static void FunctionTimerEventHandler(TimerHandle_t xTimer); + + /** + * @brief Timer Event processing function + * Trigger factory if Press and Hold duration is respected + * + * @param aEvent post event being processed + */ + static void FunctionEventHandler(AppEvent * aEvent); + + /** + * @brief PB0 Button event processing function + * Press and hold will trigger a factory reset timer start + * Press and release will restart BLEAdvertising if not commisionned + * + * @param aEvent button event being processed + */ + static void ButtonHandler(AppEvent * aEvent); + + /** + * @brief Light Timer finished callback function + * Calls LED processing function + * + * @param xTimer timer that finished + */ + static void LightTimerEventHandler(TimerHandle_t xTimer); + + /** + * @brief Updates device LEDs + */ + static void LightEventHandler(); + + /********************************************************** + * Private Attributes declaration + *********************************************************/ + + bool mSyncClusterToButtonAction; +}; diff --git a/examples/platform/efr32/Rpc.cpp b/examples/platform/efr32/Rpc.cpp index 1ab65c22bb06ab..0973049db2521e 100644 --- a/examples/platform/efr32/Rpc.cpp +++ b/examples/platform/efr32/Rpc.cpp @@ -82,7 +82,7 @@ class Efr32Button final : public Button public: pw::Status Event(const chip_rpc_ButtonEvent & request, pw_protobuf_Empty & response) override { - GetAppTask().ButtonEventHandler(SL_SIMPLE_BUTTON_INSTANCE(request.idx) /* PB 0 or PB 1 */, request.pushed); + AppTask::GetAppTask().ButtonEventHandler(SL_SIMPLE_BUTTON_INSTANCE(request.idx) /* PB 0 or PB 1 */, request.pushed); return pw::OkStatus(); } };