Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / examples / lighting-app / efr32 / src / AppTask.cpp
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
20 #include "AppTask.h"
21 #include "AppConfig.h"
22 #include "AppEvent.h"
23 #include "ButtonHandler.h"
24 #include "DataModelHandler.h"
25 #include "LEDWidget.h"
26 #include "OnboardingCodesUtil.h"
27 #include "Server.h"
28 #include "Service.h"
29 #include "attribute-storage.h"
30 #include "gen/attribute-id.h"
31 #include "gen/attribute-type.h"
32 #include "gen/cluster-id.h"
33 #include "lcd.h"
34 #include "qrcodegen.h"
35
36 #include <assert.h>
37
38 #include <setup_payload/QRCodeSetupPayloadGenerator.h>
39 #include <setup_payload/SetupPayload.h>
40
41 #include <platform/EFR32/freertos_bluetooth.h>
42
43 #include <lib/support/CodeUtils.h>
44
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>
50 #endif
51
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
58 namespace {
59
60 TimerHandle_t sFunctionTimer; // FreeRTOS app sw timer.
61
62 TaskHandle_t sAppTaskHandle;
63 QueueHandle_t sAppEventQueue;
64
65 LEDWidget sStatusLED;
66 LEDWidget sLightLED;
67
68 bool sIsThreadProvisioned     = false;
69 bool sIsThreadEnabled         = false;
70 bool sHaveBLEConnections      = false;
71 bool sHaveServiceConnectivity = false;
72
73 uint8_t sAppEventQueueBuffer[APP_EVENT_QUEUE_SIZE * sizeof(AppEvent)];
74 StaticQueue_t sAppEventQueueStruct;
75
76 StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)];
77 StaticTask_t appTaskStruct;
78 } // namespace
79 using namespace chip::TLV;
80 using namespace ::chip::DeviceLayer;
81
82 AppTask AppTask::sAppTask;
83
84 int AppTask::StartAppTask()
85 {
86     int err = CHIP_ERROR_MAX;
87
88     sAppEventQueue = xQueueCreateStatic(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent), sAppEventQueueBuffer, &sAppEventQueueStruct);
89     if (sAppEventQueue == NULL)
90     {
91         EFR32_LOG("Failed to allocate app event queue");
92         appError(err);
93     }
94
95     // Start App task.
96     sAppTaskHandle = xTaskCreateStatic(AppTaskMain, APP_TASK_NAME, ArraySize(appStack), NULL, 1, appStack, &appTaskStruct);
97     if (sAppTaskHandle != NULL)
98     {
99         err = CHIP_NO_ERROR;
100     }
101     return err;
102 }
103
104 int AppTask::Init()
105 {
106     CHIP_ERROR err = CHIP_NO_ERROR;
107
108     // Init ZCL Data Model
109     InitServer();
110
111     // Initialise WSTK buttons PB0 and PB1 (including debounce).
112     ButtonHandler::Init();
113
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
120     );
121     if (sFunctionTimer == NULL)
122     {
123         EFR32_LOG("funct timer create failed");
124         appError(err);
125     }
126
127     EFR32_LOG("Current Firmware Version: %s", CHIP_DEVICE_CONFIG_DEVICE_FIRMWARE_REVISION);
128     err = LightMgr().Init();
129     if (err != CHIP_NO_ERROR)
130     {
131         EFR32_LOG("LightMgr().Init() failed");
132         appError(err);
133     }
134
135     LightMgr().SetCallbacks(ActionInitiated, ActionCompleted);
136
137     // Initialize LEDs
138     LEDWidget::InitGpio();
139     sStatusLED.Init(SYSTEM_STATE_LED);
140
141     sLightLED.Init(LIGHT_LED);
142     sLightLED.Set(LightMgr().IsLightOn());
143     UpdateClusterState();
144
145     ConfigurationMgr().LogDeviceConfig();
146
147 // Print setup info on LCD if available
148 #ifdef DISPLAY_ENABLED
149     std::string QRCode;
150
151     if (GetQRCode(QRCode, chip::RendezvousInformationFlags::kBLE) == CHIP_NO_ERROR)
152     {
153         LCDWriteQRCode((uint8_t *) QRCode.c_str());
154     }
155     else
156     {
157         EFR32_LOG("Getting QR code failed!");
158     }
159 #else
160     PrintOnboardingCodes(chip::RendezvousInformationFlags::kBLE);
161 #endif
162
163     return err;
164 }
165
166 void AppTask::AppTaskMain(void * pvParameter)
167 {
168     int err;
169     AppEvent event;
170     uint64_t mLastChangeTimeUS = 0;
171
172     err = sAppTask.Init();
173     if (err != CHIP_NO_ERROR)
174     {
175         EFR32_LOG("AppTask.Init() failed");
176         appError(err);
177     }
178
179     EFR32_LOG("App Task started");
180     SetDeviceName("EFR32LightingDemo._chip._udp.local.");
181
182     while (true)
183     {
184         BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, pdMS_TO_TICKS(10));
185         while (eventReceived == pdTRUE)
186         {
187             sAppTask.DispatchEvent(&event);
188             eventReceived = xQueueReceive(sAppEventQueue, &event, 0);
189         }
190
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())
197         {
198             sIsThreadProvisioned     = ConnectivityMgr().IsThreadProvisioned();
199             sIsThreadEnabled         = ConnectivityMgr().IsThreadEnabled();
200             sHaveBLEConnections      = (ConnectivityMgr().NumBLEConnections() != 0);
201             sHaveServiceConnectivity = ConnectivityMgr().HaveServiceConnectivity();
202             PlatformMgr().UnlockChipStack();
203         }
204
205         // Update the status LED if factory reset has not been initiated.
206         //
207         // If system has "full connectivity", keep the LED On constantly.
208         //
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.
212         //
213         // If the system has ble connection(s) uptill the stage above, THEN blink
214         // the LEDs at an even rate of 100ms.
215         //
216         // Otherwise, blink the LED ON for a very short time.
217         if (sAppTask.mFunction != kFunction_FactoryReset)
218         {
219             // Consider the system to be "fully connected" if it has service
220             // connectivity
221             if (sHaveServiceConnectivity)
222             {
223                 sStatusLED.Set(true);
224             }
225             else if (sIsThreadProvisioned && sIsThreadEnabled)
226             {
227                 sStatusLED.Blink(950, 50);
228             }
229             else if (sHaveBLEConnections)
230             {
231                 sStatusLED.Blink(100, 100);
232             }
233             else
234             {
235                 sStatusLED.Blink(50, 950);
236             }
237         }
238
239         sStatusLED.Animate();
240         sLightLED.Animate();
241
242         uint64_t nowUS            = chip::System::Platform::Layer::GetClock_Monotonic();
243         uint64_t nextChangeTimeUS = mLastChangeTimeUS + 5 * 1000 * 1000UL;
244
245         if (nowUS > nextChangeTimeUS)
246         {
247             PublishService();
248             mLastChangeTimeUS = nowUS;
249         }
250     }
251 }
252
253 void AppTask::LightActionEventHandler(AppEvent * aEvent)
254 {
255     bool initiated = false;
256     LightingManager::Action_t action;
257     int32_t actor;
258     int err = CHIP_NO_ERROR;
259
260     if (aEvent->Type == AppEvent::kEventType_Light)
261     {
262         action = static_cast<LightingManager::Action_t>(aEvent->LightEvent.Action);
263         actor  = aEvent->LightEvent.Actor;
264     }
265     else if (aEvent->Type == AppEvent::kEventType_Button)
266     {
267         if (LightMgr().IsLightOn())
268         {
269             action = LightingManager::OFF_ACTION;
270         }
271         else
272         {
273             action = LightingManager::ON_ACTION;
274         }
275         actor = AppEvent::kEventType_Button;
276     }
277     else
278     {
279         err = CHIP_ERROR_MAX;
280     }
281
282     if (err == CHIP_NO_ERROR)
283     {
284         initiated = LightMgr().InitiateAction(actor, action);
285
286         if (!initiated)
287         {
288             EFR32_LOG("Action is already in progress or active.");
289         }
290     }
291 }
292
293 void AppTask::ButtonEventHandler(uint8_t btnIdx, uint8_t btnAction)
294 {
295     if (btnIdx != APP_LIGHT_SWITCH && btnIdx != APP_FUNCTION_BUTTON)
296     {
297         return;
298     }
299
300     AppEvent button_event              = {};
301     button_event.Type                  = AppEvent::kEventType_Button;
302     button_event.ButtonEvent.ButtonIdx = btnIdx;
303     button_event.ButtonEvent.Action    = btnAction;
304
305     if (btnIdx == APP_LIGHT_SWITCH && btnAction == APP_BUTTON_PRESSED)
306     {
307         button_event.Handler = LightActionEventHandler;
308         sAppTask.PostEvent(&button_event);
309     }
310     else if (btnIdx == APP_FUNCTION_BUTTON)
311     {
312         button_event.Handler = FunctionHandler;
313         sAppTask.PostEvent(&button_event);
314     }
315 }
316
317 void AppTask::TimerEventHandler(TimerHandle_t xTimer)
318 {
319     AppEvent event;
320     event.Type               = AppEvent::kEventType_Timer;
321     event.TimerEvent.Context = (void *) xTimer;
322     event.Handler            = FunctionTimerEventHandler;
323     sAppTask.PostEvent(&event);
324 }
325
326 void AppTask::FunctionTimerEventHandler(AppEvent * aEvent)
327 {
328     if (aEvent->Type != AppEvent::kEventType_Timer)
329     {
330         return;
331     }
332
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)
336     {
337         EFR32_LOG("Factory Reset Triggered. Release button within %ums to cancel.", FACTORY_RESET_CANCEL_WINDOW_TIMEOUT);
338
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);
342
343         sAppTask.mFunction = kFunction_FactoryReset;
344
345         // Turn off all LEDs before starting blink to make sure blink is
346         // co-ordinated.
347         sStatusLED.Set(false);
348         sLightLED.Set(false);
349
350         sStatusLED.Blink(500);
351         sLightLED.Blink(500);
352     }
353     else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset)
354     {
355         // Actually trigger Factory Reset
356         sAppTask.mFunction = kFunction_NoneSelected;
357         ConfigurationMgr().InitiateFactoryReset();
358     }
359 }
360
361 void AppTask::FunctionHandler(AppEvent * aEvent)
362 {
363     if (aEvent->ButtonEvent.ButtonIdx != APP_FUNCTION_BUTTON)
364     {
365         return;
366     }
367
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)
376     {
377         if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected)
378         {
379             sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT);
380             sAppTask.mFunction = kFunction_StartBleAdv;
381         }
382     }
383     else
384     {
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)
387         {
388             sAppTask.CancelTimer();
389             sAppTask.mFunction = kFunction_NoneSelected;
390
391             if (!ConnectivityMgr().IsThreadProvisioned())
392             {
393                 // Enable BLE advertisements
394                 ConnectivityMgr().SetBLEAdvertisingEnabled(true);
395                 ConnectivityMgr().SetBLEAdvertisingMode(ConnectivityMgr().kFastAdvertising);
396             }
397             else
398             {
399                 EFR32_LOG("Network is already provisioned, Ble advertissement not enabled");
400             }
401         }
402         else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset)
403         {
404             // Set Light status LED back to show state of light.
405             sLightLED.Set(LightMgr().IsLightOn());
406
407             sAppTask.CancelTimer();
408
409             // Change the function to none selected since factory reset has been
410             // canceled.
411             sAppTask.mFunction = kFunction_NoneSelected;
412
413             EFR32_LOG("Factory Reset has been Canceled");
414         }
415     }
416 }
417
418 void AppTask::CancelTimer()
419 {
420     if (xTimerStop(sFunctionTimer, 0) == pdFAIL)
421     {
422         EFR32_LOG("app timer stop() failed");
423         appError(CHIP_ERROR_MAX);
424     }
425
426     mFunctionTimerActive = false;
427 }
428
429 void AppTask::StartTimer(uint32_t aTimeoutInMs)
430 {
431     if (xTimerIsTimerActive(sFunctionTimer))
432     {
433         EFR32_LOG("app timer already started!");
434         CancelTimer();
435     }
436
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)
441     {
442         EFR32_LOG("app timer start() failed");
443         appError(CHIP_ERROR_MAX);
444     }
445
446     mFunctionTimerActive = true;
447 }
448
449 void AppTask::ActionInitiated(LightingManager::Action_t aAction, int32_t aActor)
450 {
451     // Action initiated, update the light led
452     if (aAction == LightingManager::ON_ACTION)
453     {
454         EFR32_LOG("Turning light ON")
455         sLightLED.Set(true);
456     }
457     else if (aAction == LightingManager::OFF_ACTION)
458     {
459         EFR32_LOG("Turning light OFF")
460         sLightLED.Set(false);
461     }
462
463     if (aActor == AppEvent::kEventType_Button)
464     {
465         sAppTask.mSyncClusterToButtonAction = true;
466     }
467 }
468
469 void AppTask::ActionCompleted(LightingManager::Action_t aAction)
470 {
471     // action has been completed bon the light
472     if (aAction == LightingManager::ON_ACTION)
473     {
474         EFR32_LOG("Light ON")
475     }
476     else if (aAction == LightingManager::OFF_ACTION)
477     {
478         EFR32_LOG("Light OFF")
479     }
480
481     if (sAppTask.mSyncClusterToButtonAction)
482     {
483         UpdateClusterState();
484         sAppTask.mSyncClusterToButtonAction = false;
485     }
486 }
487
488 void AppTask::PostLightActionRequest(int32_t aActor, LightingManager::Action_t aAction)
489 {
490     AppEvent event;
491     event.Type              = AppEvent::kEventType_Light;
492     event.LightEvent.Actor  = aActor;
493     event.LightEvent.Action = aAction;
494     event.Handler           = LightActionEventHandler;
495     PostEvent(&event);
496 }
497
498 void AppTask::PostEvent(const AppEvent * aEvent)
499 {
500     if (sAppEventQueue != NULL)
501     {
502         if (!xQueueSend(sAppEventQueue, aEvent, 1))
503         {
504             EFR32_LOG("Failed to post event to app task event queue");
505         }
506     }
507     else
508     {
509         EFR32_LOG("Event Queue is NULL should never happen");
510     }
511 }
512
513 void AppTask::DispatchEvent(AppEvent * aEvent)
514 {
515     if (aEvent->Handler)
516     {
517         aEvent->Handler(aEvent);
518     }
519     else
520     {
521         EFR32_LOG("Event received with no handler. Dropping event.");
522     }
523 }
524
525 void AppTask::UpdateClusterState(void)
526 {
527     uint8_t newValue = LightMgr().IsLightOn();
528
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)
533     {
534         EFR32_LOG("ERR: updating on/off %x", status);
535     }
536 }