blob: f398b3496edd0c3352ce5524a9874714f0eadefa [file] [log] [blame]
/*
*
* Copyright (c) 2022 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AppTask.h"
#include "AppConfig.h"
#include "AppEvent.h"
#include "BindingHandler.h"
#include "ButtonHandler.h"
#include "CHIPDeviceManager.h"
#include "DeviceCallbacks.h"
#include "LEDWidget.h"
#include "LightSwitch.h"
#include "init_Matter.h"
#include "lega_rtos_api.h"
#include <app/clusters/identify-server/identify-server.h>
#include <app/clusters/network-commissioning/network-commissioning.h>
#include <app/server/Dnssd.h>
#include <app/server/OnboardingCodesUtil.h>
#include <app/server/Server.h>
#include <app/util/attribute-storage.h>
#include <assert.h>
#include <credentials/DeviceAttestationCredsProvider.h>
#include <credentials/examples/DeviceAttestationCredsExample.h>
#include <lib/support/CodeUtils.h>
#include <platform/ASR/NetworkCommissioningDriver.h>
#include <platform/CHIPDeviceLayer.h>
#include <queue.h>
#include <setup_payload/QRCodeSetupPayloadGenerator.h>
#include <setup_payload/SetupPayload.h>
using namespace ::chip;
using namespace ::chip::app;
using namespace ::chip::Credentials;
using namespace ::chip::DeviceManager;
using namespace ::chip::DeviceLayer;
using namespace ::chip::System;
LightSwitch kLightSwitch1;
namespace {
TaskHandle_t sAppTaskHandle;
QueueHandle_t sAppEventQueue;
LEDWidget sStatusLED;
constexpr EndpointId kLightSwitch1_EndpointId = 1;
LightSwitch GenericSwitch;
constexpr EndpointId kGenericSwitchEndpointId = 2;
constexpr size_t kAppEventQueueSize = 10;
constexpr EndpointId kNetworkCommissioningEndpointMain = 0;
constexpr EndpointId kNetworkCommissioningEndpointSecondary = 0xFFFE;
app::Clusters::NetworkCommissioning::Instance
sWiFiNetworkCommissioningInstance(kNetworkCommissioningEndpointMain /* Endpoint Id */,
&(NetworkCommissioning::ASRWiFiDriver::GetInstance()));
void NetWorkCommissioningInstInit()
{
sWiFiNetworkCommissioningInstance.Init();
// We only have network commissioning on endpoint 0.
emberAfEndpointEnableDisable(kNetworkCommissioningEndpointSecondary, false);
}
Clusters::Identify::EffectIdentifierEnum sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect;
namespace {
void OnTriggerIdentifyEffectCompleted(chip::System::Layer * systemLayer, void * appState)
{
sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect;
}
} // namespace
void OnTriggerIdentifyEffect(Identify * identify)
{
sIdentifyEffect = identify->mCurrentEffectIdentifier;
if (identify->mCurrentEffectIdentifier == Clusters::Identify::EffectIdentifierEnum::kChannelChange)
{
ChipLogProgress(Zcl, "IDENTIFY_EFFECT_IDENTIFIER_CHANNEL_CHANGE - Not supported, use effect varriant %d",
to_underlying(identify->mEffectVariant));
sIdentifyEffect = static_cast<Clusters::Identify::EffectIdentifierEnum>(identify->mEffectVariant);
}
switch (sIdentifyEffect)
{
case Clusters::Identify::EffectIdentifierEnum::kBlink:
case Clusters::Identify::EffectIdentifierEnum::kBreathe:
case Clusters::Identify::EffectIdentifierEnum::kOkay:
(void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(5), OnTriggerIdentifyEffectCompleted,
identify);
break;
case Clusters::Identify::EffectIdentifierEnum::kFinishEffect:
(void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify);
(void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(1), OnTriggerIdentifyEffectCompleted,
identify);
break;
case Clusters::Identify::EffectIdentifierEnum::kStopEffect:
(void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify);
sIdentifyEffect = Clusters::Identify::EffectIdentifierEnum::kStopEffect;
break;
default:
ChipLogProgress(Zcl, "No identifier effect");
}
}
Identify gIdentify = {
chip::EndpointId{ 1 },
[](Identify *) { ChipLogProgress(Zcl, "onIdentifyStart"); },
[](Identify *) { ChipLogProgress(Zcl, "onIdentifyStop"); },
Clusters::Identify::IdentifyTypeEnum::kVisibleIndicator,
OnTriggerIdentifyEffect,
};
} // namespace
AppTask AppTask::sAppTask;
static DeviceCallbacks EchoCallbacks;
CHIP_ERROR AppTask::StartAppTask()
{
CHIP_ERROR err = CHIP_NO_ERROR;
sAppEventQueue = xQueueCreate(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent));
if (sAppEventQueue == NULL)
{
ASR_LOG("Failed to allocate app event queue");
appError(APP_ERROR_EVENT_QUEUE_FAILED);
}
// Start App task.
xTaskCreate(AppTaskMain, TASK_NAME, APP_TASK_STACK_SIZE, 0, 2, &sAppTaskHandle);
err = (sAppTaskHandle == nullptr) ? APP_ERROR_CREATE_TASK_FAILED : CHIP_NO_ERROR;
return err;
}
CHIP_ERROR AppTask::Init()
{
ASR_LOG("App Task started");
if (MatterInitializer::Init_Matter_Stack(MATTER_DEVICE_NAME) != CHIP_NO_ERROR)
return CHIP_ERROR_INTERNAL;
CHIPDeviceManager & deviceMgr = CHIPDeviceManager::GetInstance();
if (deviceMgr.Init(&EchoCallbacks) != CHIP_NO_ERROR)
appError(CHIP_ERROR_INTERNAL);
if (MatterInitializer::Init_Matter_Server() != CHIP_NO_ERROR)
return CHIP_ERROR_INTERNAL;
NetWorkCommissioningInstInit();
kLightSwitch1.Init(kLightSwitch1_EndpointId);
GenericSwitch.InitGeneric(kGenericSwitchEndpointId);
// Initialize LEDs
sStatusLED.Init(LIGHT_LED);
sStatusLED.Set(false);
UpdateStatusLED();
// Initialise WSTK buttons PB0 and PB1 (including debounce).
ButtonHandler::Init();
UpdateClusterState();
ASR_LOG("Current Software Version: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING);
ConfigurationMgr().LogDeviceConfig();
// Print setup info
#if CONFIG_NETWORK_LAYER_BLE
PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE));
#else
PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kOnNetwork));
#endif /* CONFIG_NETWORK_LAYER_BLE */
return CHIP_NO_ERROR;
}
void AppTask::AppTaskMain(void * pvParameter)
{
AppEvent event;
CHIP_ERROR err = sAppTask.Init();
if (err != CHIP_NO_ERROR)
{
ASR_LOG("AppTask.Init() failed");
appError(err);
}
while (true)
{
BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, pdMS_TO_TICKS(10));
while (eventReceived == pdTRUE)
{
sAppTask.DispatchEvent(&event);
eventReceived = xQueueReceive(sAppEventQueue, &event, 0);
}
sStatusLED.Animate();
}
}
void AppTask::PostEvent(const AppEvent * aEvent)
{
if (sAppEventQueue != NULL)
{
BaseType_t status;
if (lega_rtos_is_in_interrupt_context())
{
BaseType_t higherPrioTaskWoken = pdFALSE;
status = xQueueSendFromISR(sAppEventQueue, aEvent, &higherPrioTaskWoken);
#ifdef portYIELD_FROM_ISR
portYIELD_FROM_ISR(higherPrioTaskWoken);
#elif portEND_SWITCHING_ISR // portYIELD_FROM_ISR or portEND_SWITCHING_ISR
portEND_SWITCHING_ISR(higherPrioTaskWoken);
#else // portYIELD_FROM_ISR or portEND_SWITCHING_ISR
#error "Must have portYIELD_FROM_ISR or portEND_SWITCHING_ISR"
#endif // portYIELD_FROM_ISR or portEND_SWITCHING_ISR
}
else
{
status = xQueueSend(sAppEventQueue, aEvent, 1);
}
if (!status)
ASR_LOG("Failed to post event to app task event queue");
}
else
{
ASR_LOG("Event Queue is NULL should never happen");
}
}
void AppTask::DispatchEvent(AppEvent * aEvent)
{
if (aEvent->Handler)
{
aEvent->Handler(aEvent);
}
else
{
ASR_LOG("Event received with no handler. Dropping event.");
}
}
void AppTask::UpdateClusterState(void) {}
void AppTask::ButtonPushHandler(AppEvent * aEvent)
{
if (aEvent->Type == AppEvent::kEventType_Button)
{
switch (aEvent->ButtonEvent.PinNo)
{
case SWITCH1_BUTTON:
break;
case SWITCH2_BUTTON:
ASR_LOG("GenericSwitch: InitialPress");
GenericSwitch.GenericSwitchInitialPress();
break;
default:
break;
}
}
}
void AppTask::ButtonReleaseHandler(AppEvent * aEvent)
{
if (aEvent->Type == AppEvent::kEventType_Button)
{
switch (aEvent->ButtonEvent.PinNo)
{
case SWITCH1_BUTTON:
ASR_LOG("Switch1 Toggle!");
kLightSwitch1.InitiateActionSwitch(LightSwitch::Action::Toggle);
break;
case SWITCH2_BUTTON:
ASR_LOG("GenericSwitch: ShortRelease");
GenericSwitch.GenericSwitchReleasePress();
break;
default:
break;
}
}
}
void AppTask::StartBLEAdvertisingHandler(AppEvent * aEvent)
{
/// Don't allow on starting Matter service BLE advertising after Thread provisioning.
if (Server::GetInstance().GetFabricTable().FabricCount() != 0)
{
ASR_LOG("Matter service BLE advertising not started - device is already commissioned");
return;
}
if (ConnectivityMgr().IsBLEAdvertisingEnabled())
{
ASR_LOG("BLE advertising is already enabled");
return;
}
ASR_LOG("Enabling BLE advertising...");
if (Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow() != CHIP_NO_ERROR)
{
ASR_LOG("OpenBasicCommissioningWindow() failed");
}
}
void AppTask::UpdateStatusLED()
{
bool sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0);
bool sIsWiFiBLEAdvertising = ConnectivityMgr().IsBLEAdvertisingEnabled();
bool sIsWiFiProvisioned = ConnectivityMgr().IsWiFiStationProvisioned();
bool sIsWiFiEnabled = ConnectivityMgr().IsWiFiStationEnabled();
// Status LED indicates:
// - blinking 1 s - advertising, ready to commission
// - blinking 200 ms - commissioning in progress
// - constant lightning means commissioned with Thread network
if (sIsWiFiBLEAdvertising && !sHaveBLEConnections)
{
sStatusLED.Blink(50, 950);
}
else if (sIsWiFiProvisioned && sIsWiFiEnabled)
{
sStatusLED.Set(true);
}
else if (sHaveBLEConnections)
{
sStatusLED.Blink(30, 170);
}
else
{
sStatusLED.Blink(500);
}
}
void AppTask::ButtonEventHandler(uint8_t btnIdx, uint8_t btnAction)
{
AppEvent buttonEvent;
buttonEvent.Type = AppEvent::kEventType_Button;
if (btnIdx == SWITCH1_BUTTON)
{
if (btnAction == BUTTON_PRESSED)
{
buttonEvent.ButtonEvent.PinNo = SWITCH1_BUTTON;
buttonEvent.ButtonEvent.Action = AppEvent::kButtonPushEvent;
buttonEvent.Handler = ButtonPushHandler;
sAppTask.PostEvent(&buttonEvent);
}
else if (btnAction == BUTTON_RELEASED)
{
buttonEvent.ButtonEvent.PinNo = SWITCH1_BUTTON;
buttonEvent.ButtonEvent.Action = AppEvent::kButtonReleaseEvent;
buttonEvent.Handler = ButtonReleaseHandler;
sAppTask.PostEvent(&buttonEvent);
}
}
else if (btnIdx == SWITCH2_BUTTON)
{
if (btnAction == BUTTON_PRESSED)
{
buttonEvent.ButtonEvent.PinNo = SWITCH2_BUTTON;
buttonEvent.ButtonEvent.Action = AppEvent::kButtonPushEvent;
buttonEvent.Handler = ButtonPushHandler;
sAppTask.PostEvent(&buttonEvent);
}
else if (btnAction == BUTTON_RELEASED)
{
buttonEvent.ButtonEvent.PinNo = SWITCH2_BUTTON;
buttonEvent.ButtonEvent.Action = AppEvent::kButtonReleaseEvent;
buttonEvent.Handler = ButtonReleaseHandler;
sAppTask.PostEvent(&buttonEvent);
}
}
}