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.
21 #include "AppConfig.h"
23 #include "ButtonHandler.h"
24 #include "DataModelHandler.h"
25 #include "LEDWidget.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"
34 #include "qrcodegen.h"
38 #include <setup_payload/QRCodeSetupPayloadGenerator.h>
39 #include <setup_payload/SetupPayload.h>
41 #include <platform/EFR32/freertos_bluetooth.h>
43 #include <lib/support/CodeUtils.h>
45 #include <platform/CHIPDeviceLayer.h>
46 #if CHIP_ENABLE_OPENTHREAD
47 #include <platform/EFR32/ThreadStackManagerImpl.h>
48 #include <platform/OpenThread/OpenThreadUtils.h>
49 #include <platform/ThreadStackManager.h>
52 #define FACTORY_RESET_TRIGGER_TIMEOUT 3000
53 #define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000
54 #define APP_TASK_STACK_SIZE (1536)
55 #define APP_TASK_PRIORITY 2
56 #define APP_EVENT_QUEUE_SIZE 10
57 #define EXAMPLE_VENDOR_ID 0xcafe
60 TimerHandle_t sFunctionTimer; // FreeRTOS app sw timer.
62 TaskHandle_t sAppTaskHandle;
63 QueueHandle_t sAppEventQueue;
68 bool sIsThreadProvisioned = false;
69 bool sIsThreadEnabled = false;
70 bool sHaveBLEConnections = false;
71 bool sHaveServiceConnectivity = false;
73 uint8_t sAppEventQueueBuffer[APP_EVENT_QUEUE_SIZE * sizeof(AppEvent)];
74 StaticQueue_t sAppEventQueueStruct;
76 StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)];
77 StaticTask_t appTaskStruct;
79 using namespace chip::TLV;
80 using namespace ::chip::DeviceLayer;
82 AppTask AppTask::sAppTask;
84 int AppTask::StartAppTask()
86 int err = CHIP_ERROR_MAX;
88 sAppEventQueue = xQueueCreateStatic(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent), sAppEventQueueBuffer, &sAppEventQueueStruct);
89 if (sAppEventQueue == NULL)
91 EFR32_LOG("Failed to allocate app event queue");
96 sAppTaskHandle = xTaskCreateStatic(AppTaskMain, APP_TASK_NAME, ArraySize(appStack), NULL, 1, appStack, &appTaskStruct);
97 if (sAppTaskHandle != NULL)
106 CHIP_ERROR err = CHIP_NO_ERROR;
108 // Init ZCL Data Model
111 // Initialise WSTK buttons PB0 and PB1 (including debounce).
112 ButtonHandler::Init();
114 // Create FreeRTOS sw timer for Function Selection.
115 sFunctionTimer = xTimerCreate("FnTmr", // Just a text name, not used by the RTOS kernel
116 1, // == default timer period (mS)
117 false, // no timer reload (==one-shot)
118 (void *) this, // init timer id = app task obj context
119 TimerEventHandler // timer callback handler
121 if (sFunctionTimer == NULL)
123 EFR32_LOG("funct timer create failed");
127 EFR32_LOG("Current Firmware Version: %s", CHIP_DEVICE_CONFIG_DEVICE_FIRMWARE_REVISION);
128 err = LightMgr().Init();
129 if (err != CHIP_NO_ERROR)
131 EFR32_LOG("LightMgr().Init() failed");
135 LightMgr().SetCallbacks(ActionInitiated, ActionCompleted);
138 LEDWidget::InitGpio();
139 sStatusLED.Init(SYSTEM_STATE_LED);
141 sLightLED.Init(LIGHT_LED);
142 sLightLED.Set(LightMgr().IsLightOn());
143 UpdateClusterState();
145 ConfigurationMgr().LogDeviceConfig();
147 // Print setup info on LCD if available
148 #ifdef DISPLAY_ENABLED
151 if (GetQRCode(QRCode, chip::RendezvousInformationFlags::kBLE) == CHIP_NO_ERROR)
153 LCDWriteQRCode((uint8_t *) QRCode.c_str());
157 EFR32_LOG("Getting QR code failed!");
160 PrintOnboardingCodes(chip::RendezvousInformationFlags::kBLE);
166 void AppTask::AppTaskMain(void * pvParameter)
170 uint64_t mLastChangeTimeUS = 0;
172 err = sAppTask.Init();
173 if (err != CHIP_NO_ERROR)
175 EFR32_LOG("AppTask.Init() failed");
179 EFR32_LOG("App Task started");
180 SetDeviceName("EFR32LightingDemo._chip._udp.local.");
184 BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, pdMS_TO_TICKS(10));
185 while (eventReceived == pdTRUE)
187 sAppTask.DispatchEvent(&event);
188 eventReceived = xQueueReceive(sAppEventQueue, &event, 0);
191 // Collect connectivity and configuration state from the CHIP stack. Because
192 // the CHIP event loop is being run in a separate task, the stack must be
193 // locked while these values are queried. However we use a non-blocking
194 // lock request (TryLockCHIPStack()) to avoid blocking other UI activities
195 // when the CHIP task is busy (e.g. with a long crypto operation).
196 if (PlatformMgr().TryLockChipStack())
198 sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned();
199 sIsThreadEnabled = ConnectivityMgr().IsThreadEnabled();
200 sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0);
201 sHaveServiceConnectivity = ConnectivityMgr().HaveServiceConnectivity();
202 PlatformMgr().UnlockChipStack();
205 // Update the status LED if factory reset has not been initiated.
207 // If system has "full connectivity", keep the LED On constantly.
209 // If thread and service provisioned, but not attached to the thread network
210 // yet OR no connectivity to the service OR subscriptions are not fully
211 // established THEN blink the LED Off for a short period of time.
213 // If the system has ble connection(s) uptill the stage above, THEN blink
214 // the LEDs at an even rate of 100ms.
216 // Otherwise, blink the LED ON for a very short time.
217 if (sAppTask.mFunction != kFunction_FactoryReset)
219 // Consider the system to be "fully connected" if it has service
221 if (sHaveServiceConnectivity)
223 sStatusLED.Set(true);
225 else if (sIsThreadProvisioned && sIsThreadEnabled)
227 sStatusLED.Blink(950, 50);
229 else if (sHaveBLEConnections)
231 sStatusLED.Blink(100, 100);
235 sStatusLED.Blink(50, 950);
239 sStatusLED.Animate();
242 uint64_t nowUS = chip::System::Platform::Layer::GetClock_Monotonic();
243 uint64_t nextChangeTimeUS = mLastChangeTimeUS + 5 * 1000 * 1000UL;
245 if (nowUS > nextChangeTimeUS)
248 mLastChangeTimeUS = nowUS;
253 void AppTask::LightActionEventHandler(AppEvent * aEvent)
255 bool initiated = false;
256 LightingManager::Action_t action;
258 int err = CHIP_NO_ERROR;
260 if (aEvent->Type == AppEvent::kEventType_Light)
262 action = static_cast<LightingManager::Action_t>(aEvent->LightEvent.Action);
263 actor = aEvent->LightEvent.Actor;
265 else if (aEvent->Type == AppEvent::kEventType_Button)
267 if (LightMgr().IsLightOn())
269 action = LightingManager::OFF_ACTION;
273 action = LightingManager::ON_ACTION;
275 actor = AppEvent::kEventType_Button;
279 err = CHIP_ERROR_MAX;
282 if (err == CHIP_NO_ERROR)
284 initiated = LightMgr().InitiateAction(actor, action);
288 EFR32_LOG("Action is already in progress or active.");
293 void AppTask::ButtonEventHandler(uint8_t btnIdx, uint8_t btnAction)
295 if (btnIdx != APP_LIGHT_SWITCH && btnIdx != APP_FUNCTION_BUTTON)
300 AppEvent button_event = {};
301 button_event.Type = AppEvent::kEventType_Button;
302 button_event.ButtonEvent.ButtonIdx = btnIdx;
303 button_event.ButtonEvent.Action = btnAction;
305 if (btnIdx == APP_LIGHT_SWITCH && btnAction == APP_BUTTON_PRESSED)
307 button_event.Handler = LightActionEventHandler;
308 sAppTask.PostEvent(&button_event);
310 else if (btnIdx == APP_FUNCTION_BUTTON)
312 button_event.Handler = FunctionHandler;
313 sAppTask.PostEvent(&button_event);
317 void AppTask::TimerEventHandler(TimerHandle_t xTimer)
320 event.Type = AppEvent::kEventType_Timer;
321 event.TimerEvent.Context = (void *) xTimer;
322 event.Handler = FunctionTimerEventHandler;
323 sAppTask.PostEvent(&event);
326 void AppTask::FunctionTimerEventHandler(AppEvent * aEvent)
328 if (aEvent->Type != AppEvent::kEventType_Timer)
333 // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT,
334 // initiate factory reset
335 if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv)
337 EFR32_LOG("Factory Reset Triggered. Release button within %ums to cancel.", FACTORY_RESET_CANCEL_WINDOW_TIMEOUT);
339 // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to
340 // cancel, if required.
341 sAppTask.StartTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT);
343 sAppTask.mFunction = kFunction_FactoryReset;
345 // Turn off all LEDs before starting blink to make sure blink is
347 sStatusLED.Set(false);
348 sLightLED.Set(false);
350 sStatusLED.Blink(500);
351 sLightLED.Blink(500);
353 else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset)
355 // Actually trigger Factory Reset
356 sAppTask.mFunction = kFunction_NoneSelected;
357 ConfigurationMgr().InitiateFactoryReset();
361 void AppTask::FunctionHandler(AppEvent * aEvent)
363 if (aEvent->ButtonEvent.ButtonIdx != APP_FUNCTION_BUTTON)
368 // To trigger software update: press the APP_FUNCTION_BUTTON button briefly (<
369 // FACTORY_RESET_TRIGGER_TIMEOUT) To initiate factory reset: press the
370 // APP_FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT +
371 // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT All LEDs start blinking after
372 // FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated.
373 // To cancel factory reset: release the APP_FUNCTION_BUTTON once all LEDs
374 // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT
375 if (aEvent->ButtonEvent.Action == APP_BUTTON_PRESSED)
377 if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected)
379 sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT);
380 sAppTask.mFunction = kFunction_StartBleAdv;
385 // If the button was released before factory reset got initiated, start BLE advertissement in fast mode
386 if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv)
388 sAppTask.CancelTimer();
389 sAppTask.mFunction = kFunction_NoneSelected;
391 if (!ConnectivityMgr().IsThreadProvisioned())
393 // Enable BLE advertisements
394 ConnectivityMgr().SetBLEAdvertisingEnabled(true);
395 ConnectivityMgr().SetBLEAdvertisingMode(ConnectivityMgr().kFastAdvertising);
399 EFR32_LOG("Network is already provisioned, Ble advertissement not enabled");
402 else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset)
404 // Set Light status LED back to show state of light.
405 sLightLED.Set(LightMgr().IsLightOn());
407 sAppTask.CancelTimer();
409 // Change the function to none selected since factory reset has been
411 sAppTask.mFunction = kFunction_NoneSelected;
413 EFR32_LOG("Factory Reset has been Canceled");
418 void AppTask::CancelTimer()
420 if (xTimerStop(sFunctionTimer, 0) == pdFAIL)
422 EFR32_LOG("app timer stop() failed");
423 appError(CHIP_ERROR_MAX);
426 mFunctionTimerActive = false;
429 void AppTask::StartTimer(uint32_t aTimeoutInMs)
431 if (xTimerIsTimerActive(sFunctionTimer))
433 EFR32_LOG("app timer already started!");
437 // timer is not active, change its period to required value (== restart).
438 // FreeRTOS- Block for a maximum of 100 ticks if the change period command
439 // cannot immediately be sent to the timer command queue.
440 if (xTimerChangePeriod(sFunctionTimer, aTimeoutInMs / portTICK_PERIOD_MS, 100) != pdPASS)
442 EFR32_LOG("app timer start() failed");
443 appError(CHIP_ERROR_MAX);
446 mFunctionTimerActive = true;
449 void AppTask::ActionInitiated(LightingManager::Action_t aAction, int32_t aActor)
451 // Action initiated, update the light led
452 if (aAction == LightingManager::ON_ACTION)
454 EFR32_LOG("Turning light ON")
457 else if (aAction == LightingManager::OFF_ACTION)
459 EFR32_LOG("Turning light OFF")
460 sLightLED.Set(false);
463 if (aActor == AppEvent::kEventType_Button)
465 sAppTask.mSyncClusterToButtonAction = true;
469 void AppTask::ActionCompleted(LightingManager::Action_t aAction)
471 // action has been completed bon the light
472 if (aAction == LightingManager::ON_ACTION)
474 EFR32_LOG("Light ON")
476 else if (aAction == LightingManager::OFF_ACTION)
478 EFR32_LOG("Light OFF")
481 if (sAppTask.mSyncClusterToButtonAction)
483 UpdateClusterState();
484 sAppTask.mSyncClusterToButtonAction = false;
488 void AppTask::PostLightActionRequest(int32_t aActor, LightingManager::Action_t aAction)
491 event.Type = AppEvent::kEventType_Light;
492 event.LightEvent.Actor = aActor;
493 event.LightEvent.Action = aAction;
494 event.Handler = LightActionEventHandler;
498 void AppTask::PostEvent(const AppEvent * aEvent)
500 if (sAppEventQueue != NULL)
502 if (!xQueueSend(sAppEventQueue, aEvent, 1))
504 EFR32_LOG("Failed to post event to app task event queue");
509 EFR32_LOG("Event Queue is NULL should never happen");
513 void AppTask::DispatchEvent(AppEvent * aEvent)
517 aEvent->Handler(aEvent);
521 EFR32_LOG("Event received with no handler. Dropping event.");
525 void AppTask::UpdateClusterState(void)
527 uint8_t newValue = LightMgr().IsLightOn();
529 // write the new on/off value
530 EmberAfStatus status = emberAfWriteAttribute(1, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
531 (uint8_t *) &newValue, ZCL_BOOLEAN_ATTRIBUTE_TYPE);
532 if (status != EMBER_ZCL_STATUS_SUCCESS)
534 EFR32_LOG("ERR: updating on/off %x", status);