| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- /*
- *
- * 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.
- */
- #include "AppTask.h"
- #include "AppConfig.h"
- #include "AppEvent.h"
- #include "ButtonHandler.h"
- #include "LEDWidget.h"
- #include "Service.h"
- #include "lcd.h"
- #include "qrcodegen.h"
- #include <app/common/gen/attribute-id.h>
- #include <app/common/gen/attribute-type.h>
- #include <app/common/gen/cluster-id.h>
- #include <app/server/OnboardingCodesUtil.h>
- #include <app/server/Server.h>
- #include <app/util/attribute-storage.h>
- #include <assert.h>
- #include <lib/support/CodeUtils.h>
- #include <setup_payload/QRCodeSetupPayloadGenerator.h>
- #include <setup_payload/SetupPayload.h>
- #include <platform/CHIPDeviceLayer.h>
- #if CHIP_ENABLE_OPENTHREAD
- #include <platform/EFR32/ThreadStackManagerImpl.h>
- #include <platform/OpenThread/OpenThreadUtils.h>
- #include <platform/ThreadStackManager.h>
- #endif
- #define FACTORY_RESET_TRIGGER_TIMEOUT 3000
- #define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000
- #define APP_TASK_STACK_SIZE (1536)
- #define APP_TASK_PRIORITY 2
- #define APP_EVENT_QUEUE_SIZE 10
- namespace {
- TimerHandle_t sFunctionTimer; // FreeRTOS app sw timer.
- TaskHandle_t sAppTaskHandle;
- QueueHandle_t sAppEventQueue;
- LEDWidget sStatusLED;
- LEDWidget sLockLED;
- bool sIsThreadProvisioned = false;
- bool sIsThreadEnabled = false;
- bool sHaveBLEConnections = false;
- bool sHaveServiceConnectivity = false;
- StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)];
- StaticTask_t appTaskStruct;
- } // namespace
- using namespace chip::TLV;
- using namespace ::chip::DeviceLayer;
- AppTask AppTask::sAppTask;
- CHIP_ERROR AppTask::StartAppTask()
- {
- sAppEventQueue = xQueueCreate(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent));
- 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;
- // Init ZCL Data Model
- InitServer();
- // Initialise WSTK buttons PB0 and PB1 (including debounce).
- ButtonHandler::Init();
- // 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)
- {
- EFR32_LOG("funct timer create failed");
- appError(err);
- }
- EFR32_LOG("Current Firmware Version: %s", CHIP_DEVICE_CONFIG_DEVICE_FIRMWARE_REVISION_STRING);
- err = BoltLockMgr().Init();
- if (err != CHIP_NO_ERROR)
- {
- EFR32_LOG("BoltLockMgr().Init() failed");
- appError(err);
- }
- BoltLockMgr().SetCallbacks(ActionInitiated, ActionCompleted);
- // Initialize LEDs
- LEDWidget::InitGpio();
- sStatusLED.Init(SYSTEM_STATE_LED);
- sLockLED.Init(LOCK_STATE_LED);
- sLockLED.Set(!BoltLockMgr().IsUnlocked());
- UpdateClusterState();
- ConfigurationMgr().LogDeviceConfig();
- // Print setup info on LCD if available
- #ifdef DISPLAY_ENABLED
- std::string QRCode;
- if (GetQRCode(QRCode, chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)) == CHIP_NO_ERROR)
- {
- LCDWriteQRCode((uint8_t *) QRCode.c_str());
- }
- else
- {
- EFR32_LOG("Getting QR code failed!");
- }
- #else
- PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE));
- #endif
- return err;
- }
- void AppTask::AppTaskMain(void * pvParameter)
- {
- int err;
- AppEvent event;
- uint64_t mLastChangeTimeUS = 0;
- err = sAppTask.Init();
- if (err != CHIP_NO_ERROR)
- {
- EFR32_LOG("AppTask.Init() failed");
- appError(err);
- }
- EFR32_LOG("App Task started");
- SetDeviceName("EFR32LockDemo._matter._udp.local.");
- while (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())
- {
- sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned();
- sIsThreadEnabled = ConnectivityMgr().IsThreadEnabled();
- sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0);
- sHaveServiceConnectivity = ConnectivityMgr().HaveServiceConnectivity();
- 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)
- {
- // Consider the system to be "fully connected" if it has service
- // connectivity
- if (sHaveServiceConnectivity)
- {
- sStatusLED.Set(true);
- }
- else if (sIsThreadProvisioned && sIsThreadEnabled)
- {
- sStatusLED.Blink(950, 50);
- }
- else if (sHaveBLEConnections)
- {
- sStatusLED.Blink(100, 100);
- }
- else
- {
- sStatusLED.Blink(50, 950);
- }
- }
- sStatusLED.Animate();
- sLockLED.Animate();
- uint64_t nowUS = chip::System::Clock::GetMonotonicMicroseconds();
- uint64_t nextChangeTimeUS = mLastChangeTimeUS + 5 * 1000 * 1000UL;
- if (nowUS > nextChangeTimeUS)
- {
- PublishService();
- mLastChangeTimeUS = nowUS;
- }
- }
- }
- void AppTask::LockActionEventHandler(AppEvent * aEvent)
- {
- bool initiated = false;
- BoltLockManager::Action_t action;
- int32_t actor;
- int err = CHIP_NO_ERROR;
- if (aEvent->Type == AppEvent::kEventType_Lock)
- {
- action = static_cast<BoltLockManager::Action_t>(aEvent->LockEvent.Action);
- actor = aEvent->LockEvent.Actor;
- }
- else if (aEvent->Type == AppEvent::kEventType_Button)
- {
- if (BoltLockMgr().IsUnlocked())
- {
- action = BoltLockManager::LOCK_ACTION;
- }
- else
- {
- action = BoltLockManager::UNLOCK_ACTION;
- }
- actor = AppEvent::kEventType_Button;
- }
- else
- {
- err = APP_ERROR_UNHANDLED_EVENT;
- }
- if (err == CHIP_NO_ERROR)
- {
- initiated = BoltLockMgr().InitiateAction(actor, action);
- if (!initiated)
- {
- EFR32_LOG("Action is already in progress or active.");
- }
- }
- }
- void AppTask::ButtonEventHandler(uint8_t btnIdx, uint8_t btnAction)
- {
- if (btnIdx != APP_LOCK_BUTTON && btnIdx != APP_FUNCTION_BUTTON)
- {
- return;
- }
- AppEvent button_event = {};
- button_event.Type = AppEvent::kEventType_Button;
- button_event.ButtonEvent.ButtonIdx = btnIdx;
- button_event.ButtonEvent.Action = btnAction;
- if (btnIdx == APP_LOCK_BUTTON && btnAction == APP_BUTTON_PRESSED)
- {
- button_event.Handler = LockActionEventHandler;
- sAppTask.PostEvent(&button_event);
- }
- else if (btnIdx == APP_FUNCTION_BUTTON)
- {
- button_event.Handler = FunctionHandler;
- 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;
- ConfigurationMgr().InitiateFactoryReset();
- }
- }
- void AppTask::FunctionHandler(AppEvent * aEvent)
- {
- if (aEvent->ButtonEvent.ButtonIdx != APP_FUNCTION_BUTTON)
- {
- return;
- }
- // 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 == APP_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;
- if (!ConnectivityMgr().IsThreadProvisioned())
- {
- // 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(!BoltLockMgr().IsUnlocked());
- 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(BoltLockManager::Action_t aAction, int32_t aActor)
- {
- // If the action has been initiated by the lock, update the bolt lock trait
- // and start flashing the LEDs rapidly to indicate action initiation.
- if (aAction == BoltLockManager::LOCK_ACTION)
- {
- EFR32_LOG("Lock Action has been initiated")
- }
- else if (aAction == BoltLockManager::UNLOCK_ACTION)
- {
- EFR32_LOG("Unlock Action has been initiated")
- }
- if (aActor == AppEvent::kEventType_Button)
- {
- sAppTask.mSyncClusterToButtonAction = true;
- }
- sLockLED.Blink(50, 50);
- }
- void AppTask::ActionCompleted(BoltLockManager::Action_t aAction)
- {
- // if the action has been completed by the lock, update the bolt lock trait.
- // Turn on the lock LED if in a LOCKED state OR
- // Turn off the lock LED if in an UNLOCKED state.
- if (aAction == BoltLockManager::LOCK_ACTION)
- {
- EFR32_LOG("Lock Action has been completed")
- sLockLED.Set(true);
- }
- else if (aAction == BoltLockManager::UNLOCK_ACTION)
- {
- EFR32_LOG("Unlock Action has been completed")
- sLockLED.Set(false);
- }
- if (sAppTask.mSyncClusterToButtonAction)
- {
- UpdateClusterState();
- sAppTask.mSyncClusterToButtonAction = false;
- }
- }
- void AppTask::PostLockActionRequest(int32_t aActor, BoltLockManager::Action_t aAction)
- {
- AppEvent event;
- event.Type = AppEvent::kEventType_Lock;
- event.LockEvent.Actor = aActor;
- event.LockEvent.Action = aAction;
- event.Handler = LockActionEventHandler;
- PostEvent(&event);
- }
- void AppTask::PostEvent(const AppEvent * aEvent)
- {
- if (sAppEventQueue != NULL)
- {
- if (!xQueueSend(sAppEventQueue, aEvent, 1))
- {
- EFR32_LOG("Failed to post event to app task event queue");
- }
- }
- }
- void AppTask::DispatchEvent(AppEvent * aEvent)
- {
- if (aEvent->Handler)
- {
- aEvent->Handler(aEvent);
- }
- else
- {
- EFR32_LOG("Event received with no handler. Dropping event.");
- }
- }
- void AppTask::UpdateClusterState(void)
- {
- uint8_t newValue = !BoltLockMgr().IsUnlocked();
- // write the new on/off value
- EmberAfStatus status = emberAfWriteAttribute(1, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
- (uint8_t *) &newValue, ZCL_BOOLEAN_ATTRIBUTE_TYPE);
- if (status != EMBER_ZCL_STATUS_SUCCESS)
- {
- EFR32_LOG("ERR: updating on/off %x", status);
- }
- }
|