app_task.cpp /* * Copyright (c) 2021 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ #include "app_task.h" #include #include #include #include #ifdef CONFIG_AWS_IOT_INTEGRATION #include "aws_iot_integration.h" #endif #include "app/matter_init.h" #include "app/task_executor.h" #if defined(CONFIG_PWM) #include "pwm/pwm_device.h" #endif #ifdef CONFIG_CHIP_OTA_REQUESTOR #include "dfu/ota/ota_util.h" #endif #include #include #include #include #include #include #include k_timer sSensorTimer; LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); using namespace ::chip; using namespace ::chip::app; using namespace ::chip::DeviceLayer; namespace { constexpr EndpointId kLightEndpointId = 1; constexpr EndpointId kAllSenEndpointId=2; constexpr EndpointId kSensorEndpointId=3; constexpr uint8_t kDefaultMinLevel = 0; constexpr uint8_t kDefaultMaxLevel = 254; constexpr uint16_t kTriggerEffectTimeout = 5000; constexpr uint16_t kTriggerEffectFinishTimeout = 1000; k_timer sTriggerEffectTimer; Identify sIdentify = { kLightEndpointId, AppTask::IdentifyStartHandler, AppTask::IdentifyStopHandler, Clusters::Identify::IdentifyTypeEnum::kVisibleIndicator, AppTask::TriggerIdentifyEffectHandler }; bool sIsTriggerEffectActive = false; #if defined(CONFIG_PWM) const struct pwm_dt_spec sLightPwmDevice = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led1)); #endif // Define a custom attribute persister which makes actual write of the CurrentLevel attribute value // to the non-volatile storage only when it has remained constant for 5 seconds. This is to reduce // the flash wearout when the attribute changes frequently as a result of MoveToLevel command. // DeferredAttribute object describes a deferred attribute, but also holds a buffer with a value to // be written, so it must live so long as the DeferredAttributePersistenceProvider object. DeferredAttribute gCurrentLevelPersister(ConcreteAttributePath(kLightEndpointId, Clusters::LevelControl::Id, Clusters::LevelControl::Attributes::CurrentLevel::Id)); DeferredAttributePersistenceProvider gDeferredAttributePersister(Server::GetInstance().GetDefaultAttributePersister(), Span(&gCurrentLevelPersister, 1), System::Clock::Milliseconds32(5000)); #define APPLICATION_BUTTON_MASK DK_BTN2_MSK } /* namespace */ void AppTask::IdentifyStartHandler(Identify *) { Nrf::PostTask( [] { Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Blink(Nrf::LedConsts::kIdentifyBlinkRate_ms); }); } void AppTask::IdentifyStopHandler(Identify *) { Nrf::PostTask([] { Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Set(false); #if defined(CONFIG_PWM) Instance().mPWMDevice.ApplyLevel(); #endif }); } void AppTask::TriggerEffectTimerTimeoutCallback(k_timer *timer) { LOG_INF("Identify effect completed"); sIsTriggerEffectActive = false; Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Set(false); #if defined(CONFIG_PWM) Instance().mPWMDevice.ApplyLevel(); #endif } void AppTask::TriggerIdentifyEffectHandler(Identify *identify) { switch (identify->mCurrentEffectIdentifier) { /* Just handle all effects in the same way. */ case Clusters::Identify::EffectIdentifierEnum::kBlink: case Clusters::Identify::EffectIdentifierEnum::kBreathe: case Clusters::Identify::EffectIdentifierEnum::kOkay: case Clusters::Identify::EffectIdentifierEnum::kChannelChange: LOG_INF("Identify effect identifier changed to %d", static_cast(identify->mCurrentEffectIdentifier)); sIsTriggerEffectActive = false; k_timer_stop(&sTriggerEffectTimer); k_timer_start(&sTriggerEffectTimer, K_MSEC(kTriggerEffectTimeout), K_NO_WAIT); #if defined(CONFIG_PWM) Instance().mPWMDevice.SuppressOutput(); #endif Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Blink(Nrf::LedConsts::kIdentifyBlinkRate_ms); break; case Clusters::Identify::EffectIdentifierEnum::kFinishEffect: LOG_INF("Identify effect finish triggered"); k_timer_stop(&sTriggerEffectTimer); k_timer_start(&sTriggerEffectTimer, K_MSEC(kTriggerEffectFinishTimeout), K_NO_WAIT); break; case Clusters::Identify::EffectIdentifierEnum::kStopEffect: if (sIsTriggerEffectActive) { sIsTriggerEffectActive = false; k_timer_stop(&sTriggerEffectTimer); Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Set(false); #if defined(CONFIG_PWM) Instance().mPWMDevice.ApplyLevel(); #endif } break; default: LOG_ERR("Received invalid effect identifier."); break; } } void AppTask::LightingActionEventHandler(const LightingEvent &event) { #if defined(CONFIG_PWM) Nrf::PWMDevice::Action_t action = Nrf::PWMDevice::INVALID_ACTION; int32_t actor = 0; if (event.Actor == LightingActor::Button) { action = Instance().mPWMDevice.IsTurnedOn() ? Nrf::PWMDevice::OFF_ACTION : Nrf::PWMDevice::ON_ACTION; actor = static_cast(event.Actor); } if (action == Nrf::PWMDevice::INVALID_ACTION || !Instance().mPWMDevice.InitiateAction(action, actor, NULL)) { LOG_INF("An action could not be initiated."); } #else Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Set(!Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).GetState()); #endif } void AppTask::ButtonEventHandler(Nrf::ButtonState state, Nrf::ButtonMask hasChanged) { if ((APPLICATION_BUTTON_MASK & hasChanged) & state) { Nrf::PostTask([] { LightingEvent event; event.Actor = LightingActor::Button; SensorActivateHandler(); LightingActionEventHandler(event); }); } } #ifdef CONFIG_AWS_IOT_INTEGRATION bool AppTask::AWSIntegrationCallback(struct aws_iot_integration_cb_data *data) { LOG_INF("Attribute change requested from AWS IoT: %d", data->value); Protocols::InteractionModel::Status status; VerifyOrDie(data->error == 0); if (data->attribute_id == ATTRIBUTE_ID_ONOFF) { /* write the new on/off value */ status = Clusters::OnOff::Attributes::OnOff::Set(kLightEndpointId, data->value); if (status != Protocols::InteractionModel::Status::Success) { LOG_ERR("Updating on/off cluster failed: %x", to_underlying(status)); return false; } } else if (data->attribute_id == ATTRIBUTE_ID_LEVEL_CONTROL) { /* write the current level */ status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kLightEndpointId, data->value); if (status != Protocols::InteractionModel::Status::Success) { LOG_ERR("Updating level cluster failed: %x", to_underlying(status)); return false; } } return true; } #endif /* CONFIG_AWS_IOT_INTEGRATION */ #if defined(CONFIG_PWM) void AppTask::ActionInitiated(Nrf::PWMDevice::Action_t action, int32_t actor) { if (action == Nrf::PWMDevice::ON_ACTION) { LOG_INF("Turn On Action has been initiated"); } else if (action == Nrf::PWMDevice::OFF_ACTION) { LOG_INF("Turn Off Action has been initiated"); } else if (action == Nrf::PWMDevice::LEVEL_ACTION) { LOG_INF("Level Action has been initiated"); } } void AppTask::ActionCompleted(Nrf::PWMDevice::Action_t action, int32_t actor) { if (action == Nrf::PWMDevice::ON_ACTION) { LOG_INF("Turn On Action has been completed"); } else if (action == Nrf::PWMDevice::OFF_ACTION) { LOG_INF("Turn Off Action has been completed"); } else if (action == Nrf::PWMDevice::LEVEL_ACTION) { LOG_INF("Level Action has been completed"); } if (actor == static_cast(LightingActor::Button)) { Instance().UpdateClusterState(); } } #endif /* CONFIG_PWM */ void AppTask::UpdateClusterState() { SystemLayer().ScheduleLambda([this] { #if defined(CONFIG_PWM) /* write the new on/off value */ Protocols::InteractionModel::Status status = Clusters::OnOff::Attributes::OnOff::Set(kLightEndpointId, mPWMDevice.IsTurnedOn()); #else Protocols::InteractionModel::Status status = Clusters::OnOff::Attributes::OnOff::Set( kLightEndpointId, Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).GetState()); #endif if (status != Protocols::InteractionModel::Status::Success) { LOG_ERR("Updating on/off cluster failed: %x", to_underlying(status)); } #if defined(CONFIG_PWM) /* write the current level */ status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kLightEndpointId, mPWMDevice.GetLevel()); #else /* write the current level */ if (Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).GetState()) { status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kLightEndpointId, 100); } else { status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kLightEndpointId, 0); } #endif if (status != Protocols::InteractionModel::Status::Success) { LOG_ERR("Updating level cluster failed: %x", to_underlying(status)); } }); } void AppTask::InitPWMDDevice() { #if defined(CONFIG_PWM) /* Initialize lighting device (PWM) */ uint8_t minLightLevel = kDefaultMinLevel; Clusters::LevelControl::Attributes::MinLevel::Get(kLightEndpointId, &minLightLevel); uint8_t maxLightLevel = kDefaultMaxLevel; Clusters::LevelControl::Attributes::MaxLevel::Get(kLightEndpointId, &maxLightLevel); Clusters::LevelControl::Attributes::CurrentLevel::TypeInfo::Type currentLevel; Clusters::LevelControl::Attributes::CurrentLevel::Get(kLightEndpointId, currentLevel); int ret = mPWMDevice.Init(&sLightPwmDevice, minLightLevel, maxLightLevel, currentLevel.ValueOr(kDefaultMaxLevel)); if (ret != 0) { LOG_ERR("Failed to initialize PWD device."); } mPWMDevice.SetCallbacks(ActionInitiated, ActionCompleted); #endif } //pj void AppTask::SensorActivateHandler() { AppTask::Instance().StartSensorTimer(5000);//StartSensorTimer(500); } void AppTask::SensorDeactivateHandler() { StopSensorTimer(); } AppTask::AppTask():co2_cluster(kAllSenEndpointId, chip::app::Clusters::CarbonDioxideConcentrationMeasurement::Id, chip::app::Clusters::ConcentrationMeasurement::MeasurementMediumEnum::kAir, chip::app::Clusters::ConcentrationMeasurement::MeasurementUnitEnum::kPpm) { // Initialize the CO2 cluster // co2_cluster.Init(); CHIP_ERROR initResult = co2_cluster.Init(); // Check if initialization not successful if (initResult != CHIP_NO_ERROR) { // Log the error LOG_ERR("Failed to initialize CO2 cluster, error code: "); // Set the initialization success flag to false mInitSuccess = false; } } void AppTask::SensorMeasureHandler() { printf("************ Temp value ************\n"); //kAllSenEndpointIdk int16_t temperature = int16_t(rand()%5000); printf("Sample Sensor Temperature : %d\n",temperature); chip::app::Clusters::TemperatureMeasurement::Attributes::MeasuredValue::Set( /* endpoint ID 2*/ kAllSenEndpointId, /* temperature in 0.01*C int16_t(rand() % 5000)*/temperature); int16_t humidity = int16_t(rand()%100); printf("Sample Sensor Humidity : %d\n",humidity); chip::app::Clusters::RelativeHumidityMeasurement::Attributes::MeasuredValue::Set( /* endpoint ID 3 */ kAllSenEndpointId, /* temperature in 0.01*C int16_t(rand() % 5000)*/humidity); float value = float(rand() % 5000); AppTask& appTask = AppTask::Instance(); appTask.co2_cluster.SetMeasuredValue(chip::app::DataModel::MakeNullable(2.0f)); //(chip::app::DataModel::Nullable(value));//(value); LOG_INF("Sample Sensor CO2 : %f\n",value); // appTask.co2_cluster.SetMeasuredValue(DataModel::Nullable(value)); } void SensorTimerHandler(k_timer *timer) { Nrf::PostTask([] { AppTask::SensorMeasureHandler(); }); } void AppTask::StartSensorTimer(int time) { k_timer_start(&sSensorTimer, K_MSEC(time), K_MSEC(time)); } void AppTask::StopSensorTimer() { k_timer_stop(&sSensorTimer); } CHIP_ERROR AppTask::Init() { /* Initialize Matter stack */ ReturnErrorOnFailure(Nrf::Matter::PrepareServer(Nrf::Matter::InitData{ .mPostServerInitClbk = [] { app::SetAttributePersistenceProvider(&gDeferredAttributePersister); return CHIP_NO_ERROR; } })); if (!Nrf::GetBoard().Init(ButtonEventHandler)) { LOG_ERR("User interface initialization failed."); return CHIP_ERROR_INCORRECT_STATE; } // co2_cluster(); /* Register Matter event handler that controls the connectivity status LED based on the captured Matter network * state. */ ReturnErrorOnFailure(Nrf::Matter::RegisterEventHandler(Nrf::Board::DefaultMatterEventHandler, 0)); #ifdef CONFIG_AWS_IOT_INTEGRATION int retAws = aws_iot_integration_register_callback(AWSIntegrationCallback); if (retAws) { LOG_ERR("aws_iot_integration_register_callback() failed"); return chip::System::MapErrorZephyr(retAws); } #endif //pj k_timer_init(&sSensorTimer, &SensorTimerHandler, nullptr); k_timer_user_data_set(&sSensorTimer, this); return Nrf::Matter::StartServer(); } CHIP_ERROR AppTask::StartApp() { ReturnErrorOnFailure(Init()); while (true) { Nrf::DispatchNextTask(); } return CHIP_NO_ERROR; }