3 * Copyright (c) 2020 Project CHIP Authors
4 * Copyright (c) 2019 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 "AppConfig.h"
26 #include "OnboardingCodesUtil.h"
29 #include "attribute-storage.h"
30 #include "gen/attribute-id.h"
31 #include "gen/attribute-type.h"
32 #include "gen/cluster-id.h"
36 #include <setup_payload/QRCodeSetupPayloadGenerator.h>
37 #include <setup_payload/SetupPayload.h>
39 using namespace chip::TLV;
40 using namespace chip::DeviceLayer;
42 #include <platform/CHIPDeviceLayer.h>
43 #if CHIP_ENABLE_OPENTHREAD
44 #include <platform/OpenThread/OpenThreadUtils.h>
45 #include <platform/ThreadStackManager.h>
46 #include <platform/qpg6100/ThreadStackManagerImpl.h>
47 #define JOINER_START_TRIGGER_TIMEOUT 1500
50 #define FACTORY_RESET_TRIGGER_TIMEOUT 3000
51 #define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000
52 #define APP_TASK_STACK_SIZE (2048)
53 #define APP_TASK_PRIORITY 2
54 #define APP_EVENT_QUEUE_SIZE 10
56 static TaskHandle_t sAppTaskHandle;
57 static QueueHandle_t sAppEventQueue;
59 static bool sIsThreadProvisioned = false;
60 static bool sIsThreadEnabled = false;
61 static bool sHaveBLEConnections = false;
62 static bool sHaveServiceConnectivity = false;
64 AppTask AppTask::sAppTask;
66 int AppTask::StartAppTask()
68 sAppEventQueue = xQueueCreate(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent));
69 if (sAppEventQueue == NULL)
71 ChipLogError(NotSpecified, "Failed to allocate app event queue");
72 return CHIP_ERROR_NO_MEMORY;
76 if (xTaskCreate(AppTaskMain, "APP", APP_TASK_STACK_SIZE / sizeof(StackType_t), NULL, 1, &sAppTaskHandle) != pdPASS)
78 return CHIP_ERROR_NO_MEMORY;
86 CHIP_ERROR err = CHIP_NO_ERROR;
88 ChipLogProgress(NotSpecified, "Current Firmware Version: %s", CHIP_DEVICE_CONFIG_DEVICE_FIRMWARE_REVISION);
90 err = BoltLockMgr().Init();
91 if (err != CHIP_NO_ERROR)
93 ChipLogError(NotSpecified, "BoltLockMgr().Init() failed");
96 BoltLockMgr().SetCallbacks(ActionInitiated, ActionCompleted);
98 // Subscribe with our button callback to the qvCHIP button handler.
99 qvCHIP_SetBtnCallback(ButtonEventHandler);
101 qvCHIP_LedSet(LOCK_STATE_LED, !BoltLockMgr().IsUnlocked());
103 // Init ZCL Data Model
105 UpdateClusterState();
107 ConfigurationMgr().LogDeviceConfig();
108 PrintOnboardingCodes(chip::RendezvousInformationFlags::kBLE);
113 void AppTask::AppTaskMain(void * pvParameter)
117 uint64_t mLastChangeTimeUS = 0;
119 err = sAppTask.Init();
120 if (err != CHIP_NO_ERROR)
122 ChipLogError(NotSpecified, "AppTask.Init() failed");
126 ChipLogProgress(NotSpecified, "App Task started");
127 SetDeviceName("QPG6100LockDemo._chip._udp.local.");
131 BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, pdMS_TO_TICKS(10));
132 while (eventReceived == pdTRUE)
134 sAppTask.DispatchEvent(&event);
135 eventReceived = xQueueReceive(sAppEventQueue, &event, 0);
138 // Collect connectivity and configuration state from the CHIP stack. Because
139 // the CHIP event loop is being run in a separate task, the stack must be
140 // locked while these values are queried. However we use a non-blocking
141 // lock request (TryLockCHIPStack()) to avoid blocking other UI activities
142 // when the CHIP task is busy (e.g. with a long crypto operation).
143 if (PlatformMgr().TryLockChipStack())
145 sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned();
146 sIsThreadEnabled = ConnectivityMgr().IsThreadEnabled();
147 sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0);
148 sHaveServiceConnectivity = ConnectivityMgr().HaveServiceConnectivity();
149 PlatformMgr().UnlockChipStack();
152 // Update the status LED if factory reset has not been initiated.
154 // If system has "full connectivity", keep the LED On constantly.
156 // If thread and service provisioned, but not attached to the thread network
157 // yet OR no connectivity to the service OR subscriptions are not fully
158 // established THEN blink the LED Off for a short period of time.
160 // If the system has ble connection(s) uptill the stage above, THEN blink
161 // the LEDs at an even rate of 100ms.
163 // Otherwise, blink the LED ON for a very short time.
164 if (sAppTask.mFunction != kFunction_FactoryReset)
166 // Consider the system to be "fully connected" if it has service
168 if (sHaveServiceConnectivity)
170 qvCHIP_LedSet(SYSTEM_STATE_LED, true);
172 else if (sIsThreadProvisioned && sIsThreadEnabled)
174 qvCHIP_LedBlink(SYSTEM_STATE_LED, 950, 50);
176 else if (sHaveBLEConnections)
178 qvCHIP_LedBlink(SYSTEM_STATE_LED, 100, 100);
182 qvCHIP_LedBlink(SYSTEM_STATE_LED, 50, 950);
186 uint64_t nowUS = chip::System::Layer::GetClock_Monotonic();
187 uint64_t nextChangeTimeUS = mLastChangeTimeUS + 5 * 1000 * 1000UL;
189 if (nowUS > nextChangeTimeUS)
192 mLastChangeTimeUS = nowUS;
197 void AppTask::LockActionEventHandler(AppEvent * aEvent)
199 bool initiated = false;
200 BoltLockManager::Action_t action;
202 int err = CHIP_NO_ERROR;
204 if (aEvent->Type == AppEvent::kEventType_Lock)
206 action = static_cast<BoltLockManager::Action_t>(aEvent->LockEvent.Action);
207 actor = aEvent->LockEvent.Actor;
209 else if (aEvent->Type == AppEvent::kEventType_Button)
211 if (BoltLockMgr().IsUnlocked())
213 action = BoltLockManager::LOCK_ACTION;
217 action = BoltLockManager::UNLOCK_ACTION;
219 actor = AppEvent::kEventType_Button;
223 err = CHIP_ERROR_MAX;
226 if (err == CHIP_NO_ERROR)
228 initiated = BoltLockMgr().InitiateAction(actor, action);
232 ChipLogProgress(NotSpecified, "Action is already in progress or active.");
237 void AppTask::ButtonEventHandler(uint8_t btnIdx, bool btnPressed)
239 if (btnIdx != APP_LOCK_BUTTON && btnIdx != APP_FUNCTION_BUTTON)
244 AppEvent button_event = {};
245 button_event.Type = AppEvent::kEventType_Button;
246 button_event.ButtonEvent.ButtonIdx = btnIdx;
247 button_event.ButtonEvent.Action = btnPressed;
249 if (btnIdx == APP_LOCK_BUTTON && btnPressed == true)
251 button_event.Handler = LockActionEventHandler;
252 sAppTask.PostEvent(&button_event);
254 else if (btnIdx == APP_FUNCTION_BUTTON)
256 button_event.Handler = FunctionHandler;
257 sAppTask.PostEvent(&button_event);
261 void AppTask::TimerEventHandler(chip::System::Layer * aLayer, void * aAppState, chip::System::Error aError)
264 event.Type = AppEvent::kEventType_Timer;
265 event.TimerEvent.Context = aAppState;
266 event.Handler = FunctionTimerEventHandler;
267 sAppTask.PostEvent(&event);
270 void AppTask::FunctionTimerEventHandler(AppEvent * aEvent)
272 if (aEvent->Type != AppEvent::kEventType_Timer)
277 // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT,
278 // initiate factory reset
279 if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate)
281 #if CHIP_ENABLE_OPENTHREAD
282 ChipLogProgress(NotSpecified, "Release button now to Start Thread Joiner");
283 ChipLogProgress(NotSpecified, "Hold to trigger Factory Reset");
284 sAppTask.mFunction = kFunction_Joiner;
285 sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT);
287 else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_Joiner)
290 ChipLogProgress(NotSpecified, "Factory Reset Triggered. Release button within %ums to cancel.",
291 FACTORY_RESET_CANCEL_WINDOW_TIMEOUT);
293 // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to
294 // cancel, if required.
295 sAppTask.StartTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT);
297 sAppTask.mFunction = kFunction_FactoryReset;
299 // Turn off all LEDs before starting blink to make sure blink is
301 qvCHIP_LedSet(SYSTEM_STATE_LED, false);
302 qvCHIP_LedSet(LOCK_STATE_LED, false);
304 qvCHIP_LedBlink(SYSTEM_STATE_LED, 500, 500);
305 qvCHIP_LedBlink(LOCK_STATE_LED, 500, 500);
307 else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset)
309 // Actually trigger Factory Reset
310 sAppTask.mFunction = kFunction_NoneSelected;
311 ConfigurationMgr().InitiateFactoryReset();
315 void AppTask::FunctionHandler(AppEvent * aEvent)
317 if (aEvent->ButtonEvent.ButtonIdx != APP_FUNCTION_BUTTON)
322 // To trigger software update: press the APP_FUNCTION_BUTTON button briefly (<
323 // FACTORY_RESET_TRIGGER_TIMEOUT) To initiate factory reset: press the
324 // APP_FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT +
325 // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT All LEDs start blinking after
326 // FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated.
327 // To cancel factory reset: release the APP_FUNCTION_BUTTON once all LEDs
328 // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT
329 if (aEvent->ButtonEvent.Action == true)
331 if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected)
333 #if CHIP_ENABLE_OPENTHREAD
334 sAppTask.StartTimer(JOINER_START_TRIGGER_TIMEOUT);
336 sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT);
339 sAppTask.mFunction = kFunction_SoftwareUpdate;
344 // If the button was released before factory reset got initiated, trigger a
346 if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate)
348 sAppTask.CancelTimer();
350 sAppTask.mFunction = kFunction_NoneSelected;
352 ChipLogError(NotSpecified, "Software Update currently not supported.");
354 #if CHIP_ENABLE_OPENTHREAD
355 else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_Joiner)
357 sAppTask.CancelTimer();
358 sAppTask.mFunction = kFunction_NoneSelected;
360 CHIP_ERROR error = ThreadStackMgr().JoinerStart();
361 ChipLogProgress(NotSpecified, "Thread joiner triggered: %s", chip::ErrorStr(error));
364 else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset)
366 // Set lock status LED back to show state of lock.
367 qvCHIP_LedSet(LOCK_STATE_LED, !BoltLockMgr().IsUnlocked());
369 sAppTask.CancelTimer();
371 // Change the function to none selected since factory reset has been
373 sAppTask.mFunction = kFunction_NoneSelected;
375 ChipLogProgress(NotSpecified, "Factory Reset has been Canceled");
380 void AppTask::CancelTimer()
382 SystemLayer.CancelTimer(TimerEventHandler, this);
383 mFunctionTimerActive = false;
386 void AppTask::StartTimer(uint32_t aTimeoutInMs)
390 SystemLayer.CancelTimer(TimerEventHandler, this);
391 err = SystemLayer.StartTimer(aTimeoutInMs, TimerEventHandler, this);
394 mFunctionTimerActive = true;
396 if (err != CHIP_NO_ERROR)
398 ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err));
402 void AppTask::ActionInitiated(BoltLockManager::Action_t aAction, int32_t aActor)
404 // If the action has been initiated by the lock, update the bolt lock trait
405 // and start flashing the LEDs rapidly to indicate action initiation.
406 if (aAction == BoltLockManager::LOCK_ACTION)
408 ChipLogProgress(NotSpecified, "Lock Action has been initiated");
410 else if (aAction == BoltLockManager::UNLOCK_ACTION)
412 ChipLogProgress(NotSpecified, "Unlock Action has been initiated");
415 if (aActor == AppEvent::kEventType_Button)
417 sAppTask.mSyncClusterToButtonAction = true;
420 qvCHIP_LedBlink(LOCK_STATE_LED, 50, 50);
423 void AppTask::ActionCompleted(BoltLockManager::Action_t aAction)
425 // if the action has been completed by the lock, update the bolt lock trait.
426 // Turn on the lock LED if in a LOCKED state OR
427 // Turn off the lock LED if in an UNLOCKED state.
428 if (aAction == BoltLockManager::LOCK_ACTION)
430 ChipLogProgress(NotSpecified, "Lock Action has been completed");
432 qvCHIP_LedSet(LOCK_STATE_LED, true);
434 else if (aAction == BoltLockManager::UNLOCK_ACTION)
436 ChipLogProgress(NotSpecified, "Unlock Action has been completed");
438 qvCHIP_LedSet(LOCK_STATE_LED, false);
441 if (sAppTask.mSyncClusterToButtonAction)
443 sAppTask.UpdateClusterState();
444 sAppTask.mSyncClusterToButtonAction = false;
448 void AppTask::PostLockActionRequest(int32_t aActor, BoltLockManager::Action_t aAction)
451 event.Type = AppEvent::kEventType_Lock;
452 event.LockEvent.Actor = aActor;
453 event.LockEvent.Action = aAction;
454 event.Handler = LockActionEventHandler;
458 void AppTask::PostEvent(const AppEvent * aEvent)
460 if (sAppEventQueue != NULL)
462 if (!xQueueSend(sAppEventQueue, aEvent, 1))
464 ChipLogError(NotSpecified, "Failed to post event to app task event queue");
469 void AppTask::DispatchEvent(AppEvent * aEvent)
473 aEvent->Handler(aEvent);
477 ChipLogError(NotSpecified, "Event received with no handler. Dropping event.");
481 void AppTask::UpdateClusterState(void)
483 uint8_t newValue = !BoltLockMgr().IsUnlocked();
485 // write the new on/off value
486 EmberAfStatus status = emberAfWriteAttribute(1, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
487 (uint8_t *) &newValue, ZCL_BOOLEAN_ATTRIBUTE_TYPE);
488 if (status != EMBER_ZCL_STATUS_SUCCESS)
490 ChipLogError(NotSpecified, "ERR: updating on/off %x", status);