3 * Copyright (c) 2020 Project CHIP Authors
4 * Copyright (c) 2020 Google LLC.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include "support/ErrorStr.h"
24 #include "OnboardingCodesUtil.h"
25 #include <platform/CHIPDeviceLayer.h>
26 #include <platform/internal/DeviceNetworkInfo.h>
28 #include "attribute-storage.h"
29 #include "gen/attribute-id.h"
30 #include "gen/attribute-type.h"
31 #include "gen/cluster-id.h"
35 #include "LEDWidget.h"
36 #include "TimersManager.h"
37 #include "app_config.h"
39 constexpr uint32_t kFactoryResetTriggerTimeout = 6000;
40 constexpr uint8_t kAppEventQueueSize = 10;
42 TimerHandle_t sFunctionTimer; // FreeRTOS app sw timer.
44 static SemaphoreHandle_t sCHIPEventLock;
45 static QueueHandle_t sAppEventQueue;
47 static LEDWidget sStatusLED;
48 static LEDWidget sLockLED;
50 static bool sIsThreadProvisioned = false;
51 static bool sIsThreadEnabled = false;
52 static bool sHaveBLEConnections = false;
53 static bool sHaveServiceConnectivity = false;
55 static uint32_t eventMask = 0;
57 using namespace ::chip::DeviceLayer;
59 AppTask AppTask::sAppTask;
61 int AppTask::StartAppTask()
63 int err = CHIP_NO_ERROR;
65 sAppEventQueue = xQueueCreate(kAppEventQueueSize, sizeof(AppEvent));
66 if (sAppEventQueue == NULL)
68 K32W_LOG("Failed to allocate app event queue");
77 CHIP_ERROR err = CHIP_NO_ERROR;
79 // Init ZCL Data Model and start server
82 // QR code will be used with CHIP Tool
83 PrintOnboardingCodes(chip::RendezvousInformationFlags::kBLE);
90 /* start with all LEDS turnedd off */
91 sStatusLED.Init(SYSTEM_STATE_LED);
93 sLockLED.Init(LOCK_STATE_LED);
94 sLockLED.Set(!BoltLockMgr().IsUnlocked());
96 /* intialize the Keyboard and button press calback */
97 KBD_Init(KBD_Callback);
99 // Create FreeRTOS sw timer for Function Selection.
100 sFunctionTimer = xTimerCreate("FnTmr", // Just a text name, not used by the RTOS kernel
101 1, // == default timer period (mS)
102 false, // no timer reload (==one-shot)
103 (void *) this, // init timer id = app task obj context
104 TimerEventHandler // timer callback handler
106 if (sFunctionTimer == NULL)
108 K32W_LOG("app_timer_create() failed");
109 assert(err == CHIP_NO_ERROR);
112 err = BoltLockMgr().Init();
113 if (err != CHIP_NO_ERROR)
115 K32W_LOG("BoltLockMgr().Init() failed");
116 assert(err == CHIP_NO_ERROR);
119 BoltLockMgr().SetCallbacks(ActionInitiated, ActionCompleted);
121 sCHIPEventLock = xSemaphoreCreateMutex();
122 if (sCHIPEventLock == NULL)
124 K32W_LOG("xSemaphoreCreateMutex() failed");
125 assert(err == CHIP_NO_ERROR);
128 // Print the current software version
129 char currentFirmwareRev[ConfigurationManager::kMaxFirmwareRevisionLength + 1] = { 0 };
130 size_t currentFirmwareRevLen;
131 err = ConfigurationMgr().GetFirmwareRevision(currentFirmwareRev, sizeof(currentFirmwareRev), currentFirmwareRevLen);
132 if (err != CHIP_NO_ERROR)
134 K32W_LOG("Get version error");
135 assert(err == CHIP_NO_ERROR);
138 K32W_LOG("Current Firmware Version: %s", currentFirmwareRev);
143 void AppTask::AppTaskMain(void * pvParameter)
148 err = sAppTask.Init();
149 if (err != CHIP_NO_ERROR)
151 K32W_LOG("AppTask.Init() failed");
152 assert(err == CHIP_NO_ERROR);
157 BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, pdMS_TO_TICKS(10));
158 while (eventReceived == pdTRUE)
160 sAppTask.DispatchEvent(&event);
161 eventReceived = xQueueReceive(sAppEventQueue, &event, 0);
164 // Collect connectivity and configuration state from the CHIP stack. Because the
165 // CHIP event loop is being run in a separate task, the stack must be locked
166 // while these values are queried. However we use a non-blocking lock request
167 // (TryLockChipStack()) to avoid blocking other UI activities when the CHIP
168 // task is busy (e.g. with a long crypto operation).
169 if (PlatformMgr().TryLockChipStack())
171 sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned();
172 sIsThreadEnabled = ConnectivityMgr().IsThreadEnabled();
173 sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0);
174 sHaveServiceConnectivity = ConnectivityMgr().HaveServiceConnectivity();
175 PlatformMgr().UnlockChipStack();
178 // Update the status LED if factory reset has not been initiated.
180 // If system has "full connectivity", keep the LED On constantly.
182 // If thread and service provisioned, but not attached to the thread network yet OR no
183 // connectivity to the service OR subscriptions are not fully established
184 // THEN blink the LED Off for a short period of time.
186 // If the system has ble connection(s) uptill the stage above, THEN blink the LEDs at an even
189 // Otherwise, blink the LED ON for a very short time.
190 if (sAppTask.mFunction != kFunction_FactoryReset)
192 if (sHaveServiceConnectivity)
194 sStatusLED.Set(true);
196 else if (sIsThreadProvisioned && sIsThreadEnabled)
198 sStatusLED.Blink(950, 50);
200 else if (sHaveBLEConnections)
202 sStatusLED.Blink(100, 100);
206 sStatusLED.Blink(50, 950);
210 sStatusLED.Animate();
217 void AppTask::ButtonEventHandler(uint8_t pin_no, uint8_t button_action)
219 if ((pin_no != RESET_BUTTON) && (pin_no != LOCK_BUTTON) && (pin_no != JOIN_BUTTON) && (pin_no != BLE_BUTTON))
224 AppEvent button_event;
225 button_event.Type = AppEvent::kEventType_Button;
226 button_event.ButtonEvent.PinNo = pin_no;
227 button_event.ButtonEvent.Action = button_action;
229 if (pin_no == RESET_BUTTON)
231 button_event.Handler = ResetActionEventHandler;
233 else if (pin_no == LOCK_BUTTON)
235 button_event.Handler = LockActionEventHandler;
237 else if (pin_no == JOIN_BUTTON)
239 button_event.Handler = JoinHandler;
241 else if (pin_no == BLE_BUTTON)
243 button_event.Handler = BleHandler;
246 sAppTask.PostEvent(&button_event);
249 void AppTask::KBD_Callback(uint8_t events)
251 eventMask = eventMask | (uint32_t)(1 << events);
254 void AppTask::HandleKeyboard(void)
256 uint8_t keyEvent = 0xFF;
261 for (pos = 0; pos < (8 * sizeof(eventMask)); pos++)
263 if (eventMask & (1 << pos))
266 eventMask = eventMask & ~(1 << pos);
273 case gKBD_EventPB1_c:
274 ButtonEventHandler(RESET_BUTTON, RESET_BUTTON_PUSH);
276 case gKBD_EventPB2_c:
277 ButtonEventHandler(LOCK_BUTTON, LOCK_BUTTON_PUSH);
279 case gKBD_EventPB3_c:
280 ButtonEventHandler(JOIN_BUTTON, JOIN_BUTTON_PUSH);
282 case gKBD_EventPB4_c:
283 ButtonEventHandler(BLE_BUTTON, BLE_BUTTON_PUSH);
291 void AppTask::TimerEventHandler(TimerHandle_t xTimer)
294 event.Type = AppEvent::kEventType_Timer;
295 event.TimerEvent.Context = (void *) xTimer;
296 event.Handler = FunctionTimerEventHandler;
297 sAppTask.PostEvent(&event);
300 void AppTask::FunctionTimerEventHandler(AppEvent * aEvent)
302 if (aEvent->Type != AppEvent::kEventType_Timer)
305 K32W_LOG("Device will factory reset...");
307 // Actually trigger Factory Reset
308 ConfigurationMgr().InitiateFactoryReset();
311 void AppTask::ResetActionEventHandler(AppEvent * aEvent)
313 if (aEvent->ButtonEvent.PinNo != RESET_BUTTON)
316 if (sAppTask.mResetTimerActive)
318 sAppTask.CancelTimer();
319 sAppTask.mFunction = kFunction_NoneSelected;
321 /* restore initial state for the LED indicating Lock state */
322 if (BoltLockMgr().IsUnlocked())
331 K32W_LOG("Factory Reset was cancelled!");
335 uint32_t resetTimeout = kFactoryResetTriggerTimeout;
337 if (sAppTask.mFunction != kFunction_NoneSelected)
339 K32W_LOG("Another function is scheduled. Could not initiate Factory Reset!");
343 K32W_LOG("Factory Reset Triggered. Push the RESET button within %u ms to cancel!", resetTimeout);
344 sAppTask.mFunction = kFunction_FactoryReset;
346 /* LEDs will start blinking to signal that a Factory Reset was scheduled */
347 sStatusLED.Set(false);
350 sStatusLED.Blink(500);
353 sAppTask.StartTimer(kFactoryResetTriggerTimeout);
357 void AppTask::LockActionEventHandler(AppEvent * aEvent)
359 BoltLockManager::Action_t action;
360 int err = CHIP_NO_ERROR;
362 bool initiated = false;
364 if (sAppTask.mFunction != kFunction_NoneSelected)
366 K32W_LOG("Another function is scheduled. Could not initiate Lock/Unlock!");
370 if (aEvent->Type == AppEvent::kEventType_Lock)
372 action = static_cast<BoltLockManager::Action_t>(aEvent->LockEvent.Action);
373 actor = aEvent->LockEvent.Actor;
375 else if (aEvent->Type == AppEvent::kEventType_Button)
377 if (BoltLockMgr().IsUnlocked())
379 action = BoltLockManager::LOCK_ACTION;
383 action = BoltLockManager::UNLOCK_ACTION;
388 err = CHIP_ERROR_MAX;
391 if (err == CHIP_NO_ERROR)
393 initiated = BoltLockMgr().InitiateAction(actor, action);
397 K32W_LOG("Action is already in progress or active.");
402 void AppTask::ThreadStart()
404 chip::DeviceLayer::Internal::DeviceNetworkInfo networkInfo;
406 memset(networkInfo.ThreadNetworkName, 0, chip::DeviceLayer::Internal::kMaxThreadNetworkNameLength + 1);
407 memcpy(networkInfo.ThreadNetworkName, "OpenThread", 10);
409 networkInfo.ThreadExtendedPANId[0] = 0xde;
410 networkInfo.ThreadExtendedPANId[1] = 0xad;
411 networkInfo.ThreadExtendedPANId[2] = 0x00;
412 networkInfo.ThreadExtendedPANId[3] = 0xbe;
413 networkInfo.ThreadExtendedPANId[4] = 0xef;
414 networkInfo.ThreadExtendedPANId[5] = 0x00;
415 networkInfo.ThreadExtendedPANId[6] = 0xca;
416 networkInfo.ThreadExtendedPANId[7] = 0xfe;
418 networkInfo.ThreadMasterKey[0] = 0x00;
419 networkInfo.ThreadMasterKey[1] = 0x11;
420 networkInfo.ThreadMasterKey[2] = 0x22;
421 networkInfo.ThreadMasterKey[3] = 0x33;
422 networkInfo.ThreadMasterKey[4] = 0x44;
423 networkInfo.ThreadMasterKey[5] = 0x55;
424 networkInfo.ThreadMasterKey[6] = 0x66;
425 networkInfo.ThreadMasterKey[7] = 0x77;
426 networkInfo.ThreadMasterKey[8] = 0x88;
427 networkInfo.ThreadMasterKey[9] = 0x99;
428 networkInfo.ThreadMasterKey[10] = 0xAA;
429 networkInfo.ThreadMasterKey[11] = 0xBB;
430 networkInfo.ThreadMasterKey[12] = 0xCC;
431 networkInfo.ThreadMasterKey[13] = 0xDD;
432 networkInfo.ThreadMasterKey[14] = 0xEE;
433 networkInfo.ThreadMasterKey[15] = 0xFF;
435 networkInfo.ThreadPANId = 0xabcd;
436 networkInfo.ThreadChannel = 15;
438 networkInfo.FieldPresent.ThreadExtendedPANId = true;
439 networkInfo.FieldPresent.ThreadMeshPrefix = false;
440 networkInfo.FieldPresent.ThreadPSKc = false;
441 networkInfo.NetworkId = 0;
442 networkInfo.FieldPresent.NetworkId = true;
444 ThreadStackMgr().SetThreadEnabled(false);
445 ThreadStackMgr().SetThreadProvision(networkInfo);
446 ThreadStackMgr().SetThreadEnabled(true);
449 void AppTask::JoinHandler(AppEvent * aEvent)
451 if (aEvent->ButtonEvent.PinNo != JOIN_BUTTON)
454 if (sAppTask.mFunction != kFunction_NoneSelected)
456 K32W_LOG("Another function is scheduled. Could not initiate Thread Join!");
460 /* hard-code Thread Commissioning Parameters for the moment.
461 * In a future PR, these parameters will be sent via BLE.
466 void AppTask::BleHandler(AppEvent * aEvent)
468 if (aEvent->ButtonEvent.PinNo != BLE_BUTTON)
471 if (sAppTask.mFunction != kFunction_NoneSelected)
473 K32W_LOG("Another function is scheduled. Could not toggle BLE state!");
477 if (ConnectivityMgr().IsBLEAdvertisingEnabled())
479 ConnectivityMgr().SetBLEAdvertisingEnabled(false);
480 K32W_LOG("Stopped BLE Advertising!");
484 ConnectivityMgr().SetBLEAdvertisingEnabled(true);
486 if (OpenDefaultPairingWindow(chip::ResetAdmins::kNo) == CHIP_NO_ERROR)
488 K32W_LOG("Started BLE Advertising!");
492 K32W_LOG("OpenDefaultPairingWindow() failed");
497 void AppTask::CancelTimer()
499 if (xTimerStop(sFunctionTimer, 0) == pdFAIL)
501 K32W_LOG("app timer stop() failed");
504 mResetTimerActive = false;
507 void AppTask::StartTimer(uint32_t aTimeoutInMs)
509 if (xTimerIsTimerActive(sFunctionTimer))
511 K32W_LOG("app timer already started!");
515 // timer is not active, change its period to required value (== restart).
516 // FreeRTOS- Block for a maximum of 100 ticks if the change period command
517 // cannot immediately be sent to the timer command queue.
518 if (xTimerChangePeriod(sFunctionTimer, aTimeoutInMs / portTICK_PERIOD_MS, 100) != pdPASS)
520 K32W_LOG("app timer start() failed");
523 mResetTimerActive = true;
526 void AppTask::ActionInitiated(BoltLockManager::Action_t aAction, int32_t aActor)
528 // If the action has been initiated by the lock, update the bolt lock trait
529 // and start flashing the LEDs rapidly to indicate action initiation.
530 if (aAction == BoltLockManager::LOCK_ACTION)
532 K32W_LOG("Lock Action has been initiated")
534 else if (aAction == BoltLockManager::UNLOCK_ACTION)
536 K32W_LOG("Unlock Action has been initiated")
539 sAppTask.mFunction = kFunctionLockUnlock;
540 sLockLED.Blink(50, 50);
543 void AppTask::ActionCompleted(BoltLockManager::Action_t aAction)
545 // if the action has been completed by the lock, update the bolt lock trait.
546 // Turn on the lock LED if in a LOCKED state OR
547 // Turn off the lock LED if in an UNLOCKED state.
548 if (aAction == BoltLockManager::LOCK_ACTION)
550 K32W_LOG("Lock Action has been completed")
553 else if (aAction == BoltLockManager::UNLOCK_ACTION)
555 K32W_LOG("Unlock Action has been completed")
559 sAppTask.mFunction = kFunction_NoneSelected;
562 void AppTask::PostLockActionRequest(int32_t aActor, BoltLockManager::Action_t aAction)
565 event.Type = AppEvent::kEventType_Lock;
566 event.LockEvent.Actor = aActor;
567 event.LockEvent.Action = aAction;
568 event.Handler = LockActionEventHandler;
572 void AppTask::PostEvent(const AppEvent * aEvent)
574 if (sAppEventQueue != NULL)
576 if (!xQueueSend(sAppEventQueue, aEvent, 1))
578 K32W_LOG("Failed to post event to app task event queue");
583 void AppTask::DispatchEvent(AppEvent * aEvent)
587 aEvent->Handler(aEvent);
591 K32W_LOG("Event received with no handler. Dropping event.");
595 void AppTask::UpdateClusterState(void)
597 uint8_t newValue = !BoltLockMgr().IsUnlocked();
599 // write the new on/off value
600 EmberAfStatus status = emberAfWriteAttribute(1, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
601 (uint8_t *) &newValue, ZCL_BOOLEAN_ATTRIBUTE_TYPE);
602 if (status != EMBER_ZCL_STATUS_SUCCESS)
604 ChipLogError(NotSpecified, "ERR: updating on/off %x", status);