Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / examples / lighting-app / qpg6100 / src / AppTask.cpp
1 /*
2  *
3  *    Copyright (c) 2021 Project CHIP Authors
4  *    All rights reserved.
5  *
6  *    Licensed under the Apache License, Version 2.0 (the "License");
7  *    you may not use this file except in compliance with the License.
8  *    You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *    Unless required by applicable law or agreed to in writing, software
13  *    distributed under the License is distributed on an "AS IS" BASIS,
14  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *    See the License for the specific language governing permissions and
16  *    limitations under the License.
17  */
18
19 #include "qvCHIP.h"
20
21 #include "AppConfig.h"
22 #include "AppEvent.h"
23 #include "AppTask.h"
24
25 #include "OnboardingCodesUtil.h"
26
27 #include "Server.h"
28 #include "attribute-storage.h"
29 #include "gen/attribute-id.h"
30 #include "gen/attribute-type.h"
31 #include "gen/cluster-id.h"
32
33 #include "Service.h"
34
35 #include <setup_payload/QRCodeSetupPayloadGenerator.h>
36 #include <setup_payload/SetupPayload.h>
37
38 using namespace chip::TLV;
39 using namespace chip::DeviceLayer;
40
41 #include <platform/CHIPDeviceLayer.h>
42 #if CHIP_ENABLE_OPENTHREAD
43 #include <platform/OpenThread/OpenThreadUtils.h>
44 #include <platform/ThreadStackManager.h>
45 #include <platform/qpg6100/ThreadStackManagerImpl.h>
46 #define JOINER_START_TRIGGER_TIMEOUT 1500
47 #endif
48
49 #define FACTORY_RESET_TRIGGER_TIMEOUT 3000
50 #define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000
51 #define APP_TASK_STACK_SIZE (2048)
52 #define APP_TASK_PRIORITY 2
53 #define APP_EVENT_QUEUE_SIZE 10
54
55 static TaskHandle_t sAppTaskHandle;
56 static QueueHandle_t sAppEventQueue;
57
58 static bool sIsThreadProvisioned     = false;
59 static bool sIsThreadEnabled         = false;
60 static bool sHaveBLEConnections      = false;
61 static bool sHaveServiceConnectivity = false;
62
63 AppTask AppTask::sAppTask;
64
65 int AppTask::StartAppTask()
66 {
67     sAppEventQueue = xQueueCreate(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent));
68     if (sAppEventQueue == NULL)
69     {
70         ChipLogError(NotSpecified, "Failed to allocate app event queue");
71         return CHIP_ERROR_NO_MEMORY;
72     }
73
74     // Start App task.
75     if (xTaskCreate(AppTaskMain, "APP", APP_TASK_STACK_SIZE / sizeof(StackType_t), NULL, 1, &sAppTaskHandle) != pdPASS)
76     {
77         return CHIP_ERROR_NO_MEMORY;
78     }
79
80     return CHIP_NO_ERROR;
81 }
82
83 int AppTask::Init()
84 {
85     CHIP_ERROR err = CHIP_NO_ERROR;
86
87     ChipLogProgress(NotSpecified, "Current Firmware Version: %s", CHIP_DEVICE_CONFIG_DEVICE_FIRMWARE_REVISION);
88
89     err = LightingMgr().Init();
90     if (err != CHIP_NO_ERROR)
91     {
92         ChipLogError(NotSpecified, "LightingMgr().Init() failed");
93         return err;
94     }
95     LightingMgr().SetCallbacks(ActionInitiated, ActionCompleted);
96
97     // Subscribe with our button callback to the qvCHIP button handler.
98     qvCHIP_SetBtnCallback(ButtonEventHandler);
99
100     // Init ZCL Data Model
101     InitServer();
102     UpdateClusterState();
103
104     ConfigurationMgr().LogDeviceConfig();
105     PrintOnboardingCodes(chip::RendezvousInformationFlags::kBLE);
106
107     return err;
108 }
109
110 void AppTask::AppTaskMain(void * pvParameter)
111 {
112     int err;
113     AppEvent event;
114     uint64_t mLastChangeTimeUS = 0;
115
116     err = sAppTask.Init();
117     if (err != CHIP_NO_ERROR)
118     {
119         ChipLogError(NotSpecified, "AppTask.Init() failed");
120         // appError(err);
121     }
122
123     ChipLogProgress(NotSpecified, "App Task started");
124     SetDeviceName("QPG6100LightingDemo._chip._udp.local.");
125
126     while (true)
127     {
128         BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, pdMS_TO_TICKS(10));
129         while (eventReceived == pdTRUE)
130         {
131             sAppTask.DispatchEvent(&event);
132             eventReceived = xQueueReceive(sAppEventQueue, &event, 0);
133         }
134
135         // Collect connectivity and configuration state from the CHIP stack. Because
136         // the CHIP event loop is being run in a separate task, the stack must be
137         // locked while these values are queried.  However we use a non-blocking
138         // lock request (TryLockCHIPStack()) to avoid blocking other UI activities
139         // when the CHIP task is busy (e.g. with a long crypto operation).
140         if (PlatformMgr().TryLockChipStack())
141         {
142             sIsThreadProvisioned     = ConnectivityMgr().IsThreadProvisioned();
143             sIsThreadEnabled         = ConnectivityMgr().IsThreadEnabled();
144             sHaveBLEConnections      = (ConnectivityMgr().NumBLEConnections() != 0);
145             sHaveServiceConnectivity = ConnectivityMgr().HaveServiceConnectivity();
146             PlatformMgr().UnlockChipStack();
147         }
148
149         // Update the status LED if factory reset has not been initiated.
150         //
151         // If system has "full connectivity", keep the LED On constantly.
152         //
153         // If thread and service provisioned, but not attached to the thread network
154         // yet OR no connectivity to the service OR subscriptions are not fully
155         // established THEN blink the LED Off for a short period of time.
156         //
157         // If the system has ble connection(s) uptill the stage above, THEN blink
158         // the LEDs at an even rate of 100ms.
159         //
160         // Otherwise, blink the LED ON for a very short time.
161         if (sAppTask.mFunction != kFunction_FactoryReset)
162         {
163             // Consider the system to be "fully connected" if it has service
164             // connectivity
165             if (sHaveServiceConnectivity)
166             {
167                 qvCHIP_LedSet(SYSTEM_STATE_LED, true);
168             }
169             else if (sIsThreadProvisioned && sIsThreadEnabled)
170             {
171                 qvCHIP_LedBlink(SYSTEM_STATE_LED, 950, 50);
172             }
173             else if (sHaveBLEConnections)
174             {
175                 qvCHIP_LedBlink(SYSTEM_STATE_LED, 100, 100);
176             }
177             else
178             {
179                 qvCHIP_LedBlink(SYSTEM_STATE_LED, 50, 950);
180             }
181         }
182
183         uint64_t nowUS            = chip::System::Layer::GetClock_Monotonic();
184         uint64_t nextChangeTimeUS = mLastChangeTimeUS + 5 * 1000 * 1000UL;
185
186         if (nowUS > nextChangeTimeUS)
187         {
188             PublishService();
189             mLastChangeTimeUS = nowUS;
190         }
191     }
192 }
193
194 void AppTask::LightingActionEventHandler(AppEvent * aEvent)
195 {
196     LightingManager::Action_t action;
197     if (aEvent->Type == AppEvent::kEventType_Button)
198     {
199         if (LightingMgr().IsTurnedOn())
200         {
201             action = LightingManager::OFF_ACTION;
202         }
203         else
204         {
205             action = LightingManager::ON_ACTION;
206         }
207         LightingMgr().InitiateAction(action, 0, 0, 0);
208     }
209     if (aEvent->Type == AppEvent::kEventType_Level && aEvent->ButtonEvent.Action != 0)
210     {
211         uint8_t val = 0x0;
212         val         = LightingMgr().GetLevel() == 0x7f ? 0x1 : 0x7f;
213         action      = LightingManager::LEVEL_ACTION;
214         LightingMgr().InitiateAction(action, 0, 1, &val);
215     }
216     return;
217 }
218
219 void AppTask::ButtonEventHandler(uint8_t btnIdx, bool btnPressed)
220 {
221     ChipLogProgress(NotSpecified, "ButtonEventHandler %d, %d", btnIdx, btnPressed);
222     if (btnIdx != APP_ON_OFF_BUTTON && btnIdx != APP_FUNCTION_BUTTON && btnIdx != APP_LEVEL_BUTTON)
223     {
224         return;
225     }
226
227     AppEvent button_event              = {};
228     button_event.Type                  = AppEvent::kEventType_Button;
229     button_event.ButtonEvent.ButtonIdx = btnIdx;
230     button_event.ButtonEvent.Action    = btnPressed;
231
232     if (btnIdx == APP_ON_OFF_BUTTON && btnPressed == true)
233     {
234         button_event.Handler = LightingActionEventHandler;
235         sAppTask.PostEvent(&button_event);
236     }
237     else if (btnIdx == APP_LEVEL_BUTTON)
238     {
239         button_event.Type    = AppEvent::kEventType_Level;
240         button_event.Handler = LightingActionEventHandler;
241         sAppTask.PostEvent(&button_event);
242     }
243     else if (btnIdx == APP_FUNCTION_BUTTON)
244     {
245         button_event.Type    = AppEvent::kEventType_Level;
246         button_event.Handler = FunctionHandler;
247         sAppTask.PostEvent(&button_event);
248     }
249 }
250
251 void AppTask::TimerEventHandler(chip::System::Layer * aLayer, void * aAppState, chip::System::Error aError)
252 {
253     AppEvent event;
254     event.Type               = AppEvent::kEventType_Timer;
255     event.TimerEvent.Context = aAppState;
256     event.Handler            = FunctionTimerEventHandler;
257     sAppTask.PostEvent(&event);
258 }
259
260 void AppTask::FunctionTimerEventHandler(AppEvent * aEvent)
261 {
262     if (aEvent->Type != AppEvent::kEventType_Timer)
263     {
264         return;
265     }
266
267     // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT,
268     // initiate factory reset
269     if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate)
270     {
271 #if CHIP_ENABLE_OPENTHREAD
272         ChipLogProgress(NotSpecified, "Release button now to Start Thread Joiner");
273         ChipLogProgress(NotSpecified, "Hold to trigger Factory Reset");
274         sAppTask.mFunction = kFunction_Joiner;
275         sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT);
276     }
277     else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_Joiner)
278     {
279 #endif
280         ChipLogProgress(NotSpecified, "Factory Reset Triggered. Release button within %ums to cancel.",
281                         FACTORY_RESET_CANCEL_WINDOW_TIMEOUT);
282
283         // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to
284         // cancel, if required.
285         sAppTask.StartTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT);
286
287         sAppTask.mFunction = kFunction_FactoryReset;
288
289         // Turn off all LEDs before starting blink to make sure blink is
290         // co-ordinated.
291         qvCHIP_LedSet(SYSTEM_STATE_LED, false);
292
293         qvCHIP_LedBlink(SYSTEM_STATE_LED, 500, 500);
294     }
295     else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset)
296     {
297         // Actually trigger Factory Reset
298         sAppTask.mFunction = kFunction_NoneSelected;
299         ConfigurationMgr().InitiateFactoryReset();
300     }
301 }
302
303 void AppTask::FunctionHandler(AppEvent * aEvent)
304 {
305     if (aEvent->ButtonEvent.ButtonIdx != APP_FUNCTION_BUTTON)
306     {
307         return;
308     }
309
310     // To trigger software update: press the APP_FUNCTION_BUTTON button briefly (<
311     // FACTORY_RESET_TRIGGER_TIMEOUT) To initiate factory reset: press the
312     // APP_FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT +
313     // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT All LEDs start blinking after
314     // FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated.
315     // To cancel factory reset: release the APP_FUNCTION_BUTTON once all LEDs
316     // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT
317     if (aEvent->ButtonEvent.Action == true)
318     {
319         if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected)
320         {
321 #if CHIP_ENABLE_OPENTHREAD
322             sAppTask.StartTimer(JOINER_START_TRIGGER_TIMEOUT);
323 #else
324             sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT);
325 #endif
326
327             sAppTask.mFunction = kFunction_SoftwareUpdate;
328         }
329     }
330     else
331     {
332         // If the button was released before factory reset got initiated, trigger a
333         // software update.
334         if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate)
335         {
336             sAppTask.CancelTimer();
337
338             sAppTask.mFunction = kFunction_NoneSelected;
339
340             ChipLogError(NotSpecified, "Software Update currently not supported.");
341         }
342 #if CHIP_ENABLE_OPENTHREAD
343         else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_Joiner)
344         {
345             sAppTask.CancelTimer();
346             sAppTask.mFunction = kFunction_NoneSelected;
347
348             CHIP_ERROR error = ThreadStackMgr().JoinerStart();
349             ChipLogProgress(NotSpecified, "Thread joiner triggered: %s", chip::ErrorStr(error));
350         }
351 #endif
352         else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset)
353         {
354             sAppTask.CancelTimer();
355
356             // Change the function to none selected since factory reset has been
357             // canceled.
358             sAppTask.mFunction = kFunction_NoneSelected;
359
360             ChipLogProgress(NotSpecified, "Factory Reset has been Canceled");
361         }
362     }
363 }
364
365 void AppTask::CancelTimer()
366 {
367     SystemLayer.CancelTimer(TimerEventHandler, this);
368     mFunctionTimerActive = false;
369 }
370
371 void AppTask::StartTimer(uint32_t aTimeoutInMs)
372 {
373     CHIP_ERROR err;
374
375     SystemLayer.CancelTimer(TimerEventHandler, this);
376     err = SystemLayer.StartTimer(aTimeoutInMs, TimerEventHandler, this);
377     SuccessOrExit(err);
378
379     mFunctionTimerActive = true;
380 exit:
381     if (err != CHIP_NO_ERROR)
382     {
383         ChipLogError(NotSpecified, "StartTimer failed %s: ", chip::ErrorStr(err));
384     }
385 }
386
387 void AppTask::ActionInitiated(LightingManager::Action_t aAction)
388 {
389     // Placeholder for light action
390     if (aAction == LightingManager::ON_ACTION)
391     {
392         ChipLogProgress(NotSpecified, "Light goes on");
393     }
394     else if (aAction == LightingManager::OFF_ACTION)
395     {
396         ChipLogProgress(NotSpecified, "Light goes off ");
397     }
398 }
399
400 void AppTask::ActionCompleted(LightingManager::Action_t aAction)
401 {
402     // Placeholder for light action completed
403     if (aAction == LightingManager::ON_ACTION)
404     {
405         ChipLogProgress(NotSpecified, "Light On Action has been completed");
406     }
407     else if (aAction == LightingManager::OFF_ACTION)
408     {
409         ChipLogProgress(NotSpecified, "Light Off Action has been completed");
410     }
411
412     if (sAppTask.mSyncClusterToButtonAction)
413     {
414         sAppTask.UpdateClusterState();
415         sAppTask.mSyncClusterToButtonAction = false;
416     }
417 }
418
419 void AppTask::PostEvent(const AppEvent * aEvent)
420 {
421     if (sAppEventQueue != NULL)
422     {
423         if (!xQueueSend(sAppEventQueue, aEvent, 1))
424         {
425             ChipLogError(NotSpecified, "Failed to post event to app task event queue");
426         }
427     }
428 }
429
430 void AppTask::DispatchEvent(AppEvent * aEvent)
431 {
432     if (aEvent->Handler)
433     {
434         aEvent->Handler(aEvent);
435     }
436     else
437     {
438         ChipLogError(NotSpecified, "Event received with no handler. Dropping event.");
439     }
440 }
441
442 void AppTask::UpdateClusterState(void)
443 {
444     uint8_t newValue = !LightingMgr().IsTurnedOn();
445     // write the new on/off value
446     EmberAfStatus status = emberAfWriteAttribute(1, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
447                                                  (uint8_t *) &newValue, ZCL_BOOLEAN_ATTRIBUTE_TYPE);
448     if (status != EMBER_ZCL_STATUS_SUCCESS)
449     {
450         ChipLogError(NotSpecified, "ERR: updating on/off %x", status);
451     }
452
453     ChipLogProgress(NotSpecified, "UpdateClusterState");
454     newValue = LightingMgr().GetLevel();
455     // TODO understand well enough to implement the level cluster ZCL_CURRENT_LEVEL_ATTRIBUTE_ID
456     status = emberAfWriteAttribute(1, ZCL_LEVEL_CONTROL_CLUSTER_ID, ZCL_CURRENT_LEVEL_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
457                                    (uint8_t *) &newValue, ZCL_DATA8_ATTRIBUTE_TYPE);
458
459     if (status != EMBER_ZCL_STATUS_SUCCESS)
460     {
461         ChipLogError(NotSpecified, "ERR: updating level %x", status);
462     }
463 }