AppTask.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. /*
  2. *
  3. * Copyright (c) 2020 Project CHIP Authors
  4. * Copyright (c) 2019 Google LLC.
  5. * All rights reserved.
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. */
  19. #include "AppTask.h"
  20. #include "AppConfig.h"
  21. #include "AppEvent.h"
  22. #include "ButtonHandler.h"
  23. #include "LEDWidget.h"
  24. #include "Service.h"
  25. #include "lcd.h"
  26. #include "qrcodegen.h"
  27. #include <app/common/gen/attribute-id.h>
  28. #include <app/common/gen/attribute-type.h>
  29. #include <app/common/gen/cluster-id.h>
  30. #include <app/server/OnboardingCodesUtil.h>
  31. #include <app/server/Server.h>
  32. #include <app/util/attribute-storage.h>
  33. #include <assert.h>
  34. #include <lib/support/CodeUtils.h>
  35. #include <setup_payload/QRCodeSetupPayloadGenerator.h>
  36. #include <setup_payload/SetupPayload.h>
  37. #include <platform/CHIPDeviceLayer.h>
  38. #if CHIP_ENABLE_OPENTHREAD
  39. #include <platform/EFR32/ThreadStackManagerImpl.h>
  40. #include <platform/OpenThread/OpenThreadUtils.h>
  41. #include <platform/ThreadStackManager.h>
  42. #endif
  43. #define FACTORY_RESET_TRIGGER_TIMEOUT 3000
  44. #define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000
  45. #define APP_TASK_STACK_SIZE (1536)
  46. #define APP_TASK_PRIORITY 2
  47. #define APP_EVENT_QUEUE_SIZE 10
  48. namespace {
  49. TimerHandle_t sFunctionTimer; // FreeRTOS app sw timer.
  50. TaskHandle_t sAppTaskHandle;
  51. QueueHandle_t sAppEventQueue;
  52. LEDWidget sStatusLED;
  53. LEDWidget sLockLED;
  54. bool sIsThreadProvisioned = false;
  55. bool sIsThreadEnabled = false;
  56. bool sHaveBLEConnections = false;
  57. bool sHaveServiceConnectivity = false;
  58. StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)];
  59. StaticTask_t appTaskStruct;
  60. } // namespace
  61. using namespace chip::TLV;
  62. using namespace ::chip::DeviceLayer;
  63. AppTask AppTask::sAppTask;
  64. CHIP_ERROR AppTask::StartAppTask()
  65. {
  66. sAppEventQueue = xQueueCreate(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent));
  67. if (sAppEventQueue == NULL)
  68. {
  69. EFR32_LOG("Failed to allocate app event queue");
  70. appError(APP_ERROR_EVENT_QUEUE_FAILED);
  71. }
  72. // Start App task.
  73. sAppTaskHandle = xTaskCreateStatic(AppTaskMain, APP_TASK_NAME, ArraySize(appStack), NULL, 1, appStack, &appTaskStruct);
  74. return (sAppTaskHandle == nullptr) ? APP_ERROR_CREATE_TASK_FAILED : CHIP_NO_ERROR;
  75. }
  76. CHIP_ERROR AppTask::Init()
  77. {
  78. CHIP_ERROR err = CHIP_NO_ERROR;
  79. // Init ZCL Data Model
  80. InitServer();
  81. // Initialise WSTK buttons PB0 and PB1 (including debounce).
  82. ButtonHandler::Init();
  83. // Create FreeRTOS sw timer for Function Selection.
  84. sFunctionTimer = xTimerCreate("FnTmr", // Just a text name, not used by the RTOS kernel
  85. 1, // == default timer period (mS)
  86. false, // no timer reload (==one-shot)
  87. (void *) this, // init timer id = app task obj context
  88. TimerEventHandler // timer callback handler
  89. );
  90. if (sFunctionTimer == NULL)
  91. {
  92. EFR32_LOG("funct timer create failed");
  93. appError(err);
  94. }
  95. EFR32_LOG("Current Firmware Version: %s", CHIP_DEVICE_CONFIG_DEVICE_FIRMWARE_REVISION_STRING);
  96. err = BoltLockMgr().Init();
  97. if (err != CHIP_NO_ERROR)
  98. {
  99. EFR32_LOG("BoltLockMgr().Init() failed");
  100. appError(err);
  101. }
  102. BoltLockMgr().SetCallbacks(ActionInitiated, ActionCompleted);
  103. // Initialize LEDs
  104. LEDWidget::InitGpio();
  105. sStatusLED.Init(SYSTEM_STATE_LED);
  106. sLockLED.Init(LOCK_STATE_LED);
  107. sLockLED.Set(!BoltLockMgr().IsUnlocked());
  108. UpdateClusterState();
  109. ConfigurationMgr().LogDeviceConfig();
  110. // Print setup info on LCD if available
  111. #ifdef DISPLAY_ENABLED
  112. std::string QRCode;
  113. if (GetQRCode(QRCode, chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)) == CHIP_NO_ERROR)
  114. {
  115. LCDWriteQRCode((uint8_t *) QRCode.c_str());
  116. }
  117. else
  118. {
  119. EFR32_LOG("Getting QR code failed!");
  120. }
  121. #else
  122. PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE));
  123. #endif
  124. return err;
  125. }
  126. void AppTask::AppTaskMain(void * pvParameter)
  127. {
  128. int err;
  129. AppEvent event;
  130. uint64_t mLastChangeTimeUS = 0;
  131. err = sAppTask.Init();
  132. if (err != CHIP_NO_ERROR)
  133. {
  134. EFR32_LOG("AppTask.Init() failed");
  135. appError(err);
  136. }
  137. EFR32_LOG("App Task started");
  138. SetDeviceName("EFR32LockDemo._matter._udp.local.");
  139. while (true)
  140. {
  141. BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, pdMS_TO_TICKS(10));
  142. while (eventReceived == pdTRUE)
  143. {
  144. sAppTask.DispatchEvent(&event);
  145. eventReceived = xQueueReceive(sAppEventQueue, &event, 0);
  146. }
  147. // Collect connectivity and configuration state from the CHIP stack. Because
  148. // the CHIP event loop is being run in a separate task, the stack must be
  149. // locked while these values are queried. However we use a non-blocking
  150. // lock request (TryLockCHIPStack()) to avoid blocking other UI activities
  151. // when the CHIP task is busy (e.g. with a long crypto operation).
  152. if (PlatformMgr().TryLockChipStack())
  153. {
  154. sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned();
  155. sIsThreadEnabled = ConnectivityMgr().IsThreadEnabled();
  156. sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0);
  157. sHaveServiceConnectivity = ConnectivityMgr().HaveServiceConnectivity();
  158. PlatformMgr().UnlockChipStack();
  159. }
  160. // Update the status LED if factory reset has not been initiated.
  161. //
  162. // If system has "full connectivity", keep the LED On constantly.
  163. //
  164. // If thread and service provisioned, but not attached to the thread network
  165. // yet OR no connectivity to the service OR subscriptions are not fully
  166. // established THEN blink the LED Off for a short period of time.
  167. //
  168. // If the system has ble connection(s) uptill the stage above, THEN blink
  169. // the LEDs at an even rate of 100ms.
  170. //
  171. // Otherwise, blink the LED ON for a very short time.
  172. if (sAppTask.mFunction != kFunction_FactoryReset)
  173. {
  174. // Consider the system to be "fully connected" if it has service
  175. // connectivity
  176. if (sHaveServiceConnectivity)
  177. {
  178. sStatusLED.Set(true);
  179. }
  180. else if (sIsThreadProvisioned && sIsThreadEnabled)
  181. {
  182. sStatusLED.Blink(950, 50);
  183. }
  184. else if (sHaveBLEConnections)
  185. {
  186. sStatusLED.Blink(100, 100);
  187. }
  188. else
  189. {
  190. sStatusLED.Blink(50, 950);
  191. }
  192. }
  193. sStatusLED.Animate();
  194. sLockLED.Animate();
  195. uint64_t nowUS = chip::System::Clock::GetMonotonicMicroseconds();
  196. uint64_t nextChangeTimeUS = mLastChangeTimeUS + 5 * 1000 * 1000UL;
  197. if (nowUS > nextChangeTimeUS)
  198. {
  199. PublishService();
  200. mLastChangeTimeUS = nowUS;
  201. }
  202. }
  203. }
  204. void AppTask::LockActionEventHandler(AppEvent * aEvent)
  205. {
  206. bool initiated = false;
  207. BoltLockManager::Action_t action;
  208. int32_t actor;
  209. int err = CHIP_NO_ERROR;
  210. if (aEvent->Type == AppEvent::kEventType_Lock)
  211. {
  212. action = static_cast<BoltLockManager::Action_t>(aEvent->LockEvent.Action);
  213. actor = aEvent->LockEvent.Actor;
  214. }
  215. else if (aEvent->Type == AppEvent::kEventType_Button)
  216. {
  217. if (BoltLockMgr().IsUnlocked())
  218. {
  219. action = BoltLockManager::LOCK_ACTION;
  220. }
  221. else
  222. {
  223. action = BoltLockManager::UNLOCK_ACTION;
  224. }
  225. actor = AppEvent::kEventType_Button;
  226. }
  227. else
  228. {
  229. err = APP_ERROR_UNHANDLED_EVENT;
  230. }
  231. if (err == CHIP_NO_ERROR)
  232. {
  233. initiated = BoltLockMgr().InitiateAction(actor, action);
  234. if (!initiated)
  235. {
  236. EFR32_LOG("Action is already in progress or active.");
  237. }
  238. }
  239. }
  240. void AppTask::ButtonEventHandler(uint8_t btnIdx, uint8_t btnAction)
  241. {
  242. if (btnIdx != APP_LOCK_BUTTON && btnIdx != APP_FUNCTION_BUTTON)
  243. {
  244. return;
  245. }
  246. AppEvent button_event = {};
  247. button_event.Type = AppEvent::kEventType_Button;
  248. button_event.ButtonEvent.ButtonIdx = btnIdx;
  249. button_event.ButtonEvent.Action = btnAction;
  250. if (btnIdx == APP_LOCK_BUTTON && btnAction == APP_BUTTON_PRESSED)
  251. {
  252. button_event.Handler = LockActionEventHandler;
  253. sAppTask.PostEvent(&button_event);
  254. }
  255. else if (btnIdx == APP_FUNCTION_BUTTON)
  256. {
  257. button_event.Handler = FunctionHandler;
  258. sAppTask.PostEvent(&button_event);
  259. }
  260. }
  261. void AppTask::TimerEventHandler(TimerHandle_t xTimer)
  262. {
  263. AppEvent event;
  264. event.Type = AppEvent::kEventType_Timer;
  265. event.TimerEvent.Context = (void *) xTimer;
  266. event.Handler = FunctionTimerEventHandler;
  267. sAppTask.PostEvent(&event);
  268. }
  269. void AppTask::FunctionTimerEventHandler(AppEvent * aEvent)
  270. {
  271. if (aEvent->Type != AppEvent::kEventType_Timer)
  272. {
  273. return;
  274. }
  275. // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT,
  276. // initiate factory reset
  277. if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv)
  278. {
  279. EFR32_LOG("Factory Reset Triggered. Release button within %ums to cancel.", FACTORY_RESET_CANCEL_WINDOW_TIMEOUT);
  280. // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to
  281. // cancel, if required.
  282. sAppTask.StartTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT);
  283. sAppTask.mFunction = kFunction_FactoryReset;
  284. // Turn off all LEDs before starting blink to make sure blink is
  285. // co-ordinated.
  286. sStatusLED.Set(false);
  287. sLockLED.Set(false);
  288. sStatusLED.Blink(500);
  289. sLockLED.Blink(500);
  290. }
  291. else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset)
  292. {
  293. // Actually trigger Factory Reset
  294. sAppTask.mFunction = kFunction_NoneSelected;
  295. ConfigurationMgr().InitiateFactoryReset();
  296. }
  297. }
  298. void AppTask::FunctionHandler(AppEvent * aEvent)
  299. {
  300. if (aEvent->ButtonEvent.ButtonIdx != APP_FUNCTION_BUTTON)
  301. {
  302. return;
  303. }
  304. // To trigger software update: press the APP_FUNCTION_BUTTON button briefly (<
  305. // FACTORY_RESET_TRIGGER_TIMEOUT) To initiate factory reset: press the
  306. // APP_FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT +
  307. // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT All LEDs start blinking after
  308. // FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated.
  309. // To cancel factory reset: release the APP_FUNCTION_BUTTON once all LEDs
  310. // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT
  311. if (aEvent->ButtonEvent.Action == APP_BUTTON_PRESSED)
  312. {
  313. if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected)
  314. {
  315. sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT);
  316. sAppTask.mFunction = kFunction_StartBleAdv;
  317. }
  318. }
  319. else
  320. {
  321. // If the button was released before factory reset got initiated, start BLE advertissement in fast mode
  322. if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv)
  323. {
  324. sAppTask.CancelTimer();
  325. sAppTask.mFunction = kFunction_NoneSelected;
  326. if (!ConnectivityMgr().IsThreadProvisioned())
  327. {
  328. // Enable BLE advertisements
  329. ConnectivityMgr().SetBLEAdvertisingEnabled(true);
  330. ConnectivityMgr().SetBLEAdvertisingMode(ConnectivityMgr().kFastAdvertising);
  331. }
  332. else
  333. {
  334. EFR32_LOG("Network is already provisioned, Ble advertissement not enabled");
  335. }
  336. }
  337. else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset)
  338. {
  339. // Set lock status LED back to show state of lock.
  340. sLockLED.Set(!BoltLockMgr().IsUnlocked());
  341. sAppTask.CancelTimer();
  342. // Change the function to none selected since factory reset has been
  343. // canceled.
  344. sAppTask.mFunction = kFunction_NoneSelected;
  345. EFR32_LOG("Factory Reset has been Canceled");
  346. }
  347. }
  348. }
  349. void AppTask::CancelTimer()
  350. {
  351. if (xTimerStop(sFunctionTimer, 0) == pdFAIL)
  352. {
  353. EFR32_LOG("app timer stop() failed");
  354. appError(APP_ERROR_STOP_TIMER_FAILED);
  355. }
  356. mFunctionTimerActive = false;
  357. }
  358. void AppTask::StartTimer(uint32_t aTimeoutInMs)
  359. {
  360. if (xTimerIsTimerActive(sFunctionTimer))
  361. {
  362. EFR32_LOG("app timer already started!");
  363. CancelTimer();
  364. }
  365. // timer is not active, change its period to required value (== restart).
  366. // FreeRTOS- Block for a maximum of 100 ticks if the change period command
  367. // cannot immediately be sent to the timer command queue.
  368. if (xTimerChangePeriod(sFunctionTimer, aTimeoutInMs / portTICK_PERIOD_MS, 100) != pdPASS)
  369. {
  370. EFR32_LOG("app timer start() failed");
  371. appError(APP_ERROR_START_TIMER_FAILED);
  372. }
  373. mFunctionTimerActive = true;
  374. }
  375. void AppTask::ActionInitiated(BoltLockManager::Action_t aAction, int32_t aActor)
  376. {
  377. // If the action has been initiated by the lock, update the bolt lock trait
  378. // and start flashing the LEDs rapidly to indicate action initiation.
  379. if (aAction == BoltLockManager::LOCK_ACTION)
  380. {
  381. EFR32_LOG("Lock Action has been initiated")
  382. }
  383. else if (aAction == BoltLockManager::UNLOCK_ACTION)
  384. {
  385. EFR32_LOG("Unlock Action has been initiated")
  386. }
  387. if (aActor == AppEvent::kEventType_Button)
  388. {
  389. sAppTask.mSyncClusterToButtonAction = true;
  390. }
  391. sLockLED.Blink(50, 50);
  392. }
  393. void AppTask::ActionCompleted(BoltLockManager::Action_t aAction)
  394. {
  395. // if the action has been completed by the lock, update the bolt lock trait.
  396. // Turn on the lock LED if in a LOCKED state OR
  397. // Turn off the lock LED if in an UNLOCKED state.
  398. if (aAction == BoltLockManager::LOCK_ACTION)
  399. {
  400. EFR32_LOG("Lock Action has been completed")
  401. sLockLED.Set(true);
  402. }
  403. else if (aAction == BoltLockManager::UNLOCK_ACTION)
  404. {
  405. EFR32_LOG("Unlock Action has been completed")
  406. sLockLED.Set(false);
  407. }
  408. if (sAppTask.mSyncClusterToButtonAction)
  409. {
  410. UpdateClusterState();
  411. sAppTask.mSyncClusterToButtonAction = false;
  412. }
  413. }
  414. void AppTask::PostLockActionRequest(int32_t aActor, BoltLockManager::Action_t aAction)
  415. {
  416. AppEvent event;
  417. event.Type = AppEvent::kEventType_Lock;
  418. event.LockEvent.Actor = aActor;
  419. event.LockEvent.Action = aAction;
  420. event.Handler = LockActionEventHandler;
  421. PostEvent(&event);
  422. }
  423. void AppTask::PostEvent(const AppEvent * aEvent)
  424. {
  425. if (sAppEventQueue != NULL)
  426. {
  427. if (!xQueueSend(sAppEventQueue, aEvent, 1))
  428. {
  429. EFR32_LOG("Failed to post event to app task event queue");
  430. }
  431. }
  432. }
  433. void AppTask::DispatchEvent(AppEvent * aEvent)
  434. {
  435. if (aEvent->Handler)
  436. {
  437. aEvent->Handler(aEvent);
  438. }
  439. else
  440. {
  441. EFR32_LOG("Event received with no handler. Dropping event.");
  442. }
  443. }
  444. void AppTask::UpdateClusterState(void)
  445. {
  446. uint8_t newValue = !BoltLockMgr().IsUnlocked();
  447. // write the new on/off value
  448. EmberAfStatus status = emberAfWriteAttribute(1, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
  449. (uint8_t *) &newValue, ZCL_BOOLEAN_ATTRIBUTE_TYPE);
  450. if (status != EMBER_ZCL_STATUS_SUCCESS)
  451. {
  452. EFR32_LOG("ERR: updating on/off %x", status);
  453. }
  454. }