AppTask.cpp 15 KB

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