diff --git a/examples/all-clusters-app/nrfconnect/main/AppTask.cpp b/examples/all-clusters-app/nrfconnect/main/AppTask.cpp index c43c9f583d131b..8f7c646bd8471d 100644 --- a/examples/all-clusters-app/nrfconnect/main/AppTask.cpp +++ b/examples/all-clusters-app/nrfconnect/main/AppTask.cpp @@ -18,7 +18,7 @@ #include "AppTask.h" #include "AppConfig.h" #include "AppEvent.h" -#include "Utils.h" +#include "LEDUtil.h" #include "binding-handler.h" #include diff --git a/examples/lighting-app/nrfconnect/CMakeLists.txt b/examples/lighting-app/nrfconnect/CMakeLists.txt index 934b0baca23b39..2fddf8e453a8d6 100644 --- a/examples/lighting-app/nrfconnect/CMakeLists.txt +++ b/examples/lighting-app/nrfconnect/CMakeLists.txt @@ -56,12 +56,12 @@ target_include_directories(app PRIVATE target_sources(app PRIVATE main/AppTask.cpp - main/LightingManager.cpp main/main.cpp main/ZclCallbacks.cpp ${GEN_DIR}/lighting-app/zap-generated/callback-stub.cpp ${GEN_DIR}/lighting-app/zap-generated/IMClusterCommandHandler.cpp ${NRFCONNECT_COMMON}/util/LEDWidget.cpp + ${NRFCONNECT_COMMON}/util/PWMDevice.cpp ${NRFCONNECT_COMMON}/util/ThreadUtil.cpp) chip_configure_data_model(app diff --git a/examples/lighting-app/nrfconnect/boards/nrf52840dk_nrf52840.overlay b/examples/lighting-app/nrfconnect/boards/nrf52840dk_nrf52840.overlay index aac348e1b971a5..d8784e3714a921 100644 --- a/examples/lighting-app/nrfconnect/boards/nrf52840dk_nrf52840.overlay +++ b/examples/lighting-app/nrfconnect/boards/nrf52840dk_nrf52840.overlay @@ -48,6 +48,8 @@ }; &pwm0 { + /delete-property/ ch0-pin; + /delete-property/ ch0-inverted; ch1-pin = < 0xe >; ch1-inverted; }; diff --git a/examples/lighting-app/nrfconnect/main/AppTask.cpp b/examples/lighting-app/nrfconnect/main/AppTask.cpp index c6d6add3700301..4de9d4eeb5f73d 100644 --- a/examples/lighting-app/nrfconnect/main/AppTask.cpp +++ b/examples/lighting-app/nrfconnect/main/AppTask.cpp @@ -21,7 +21,7 @@ #include "AppConfig.h" #include "AppEvent.h" #include "LEDWidget.h" -#include "LightingManager.h" +#include "PWMDevice.h" #include "ThreadUtil.h" #include @@ -132,20 +132,19 @@ CHIP_ERROR AppTask::Init() UpdateStatusLED(); - // Initialize lighting manager + // 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); - int ret = LightingMgr().Init(LIGHTING_PWM_DEVICE, LIGHTING_PWM_CHANNEL, minLightLevel, maxLightLevel); + int ret = mPWMDevice.Init(LIGHTING_PWM_DEVICE, LIGHTING_PWM_CHANNEL, minLightLevel, maxLightLevel, maxLightLevel); if (ret != 0) { return chip::System::MapErrorZephyr(ret); } - - LightingMgr().SetCallbacks(ActionInitiated, ActionCompleted); + mPWMDevice.SetCallbacks(ActionInitiated, ActionCompleted); // Initialize buttons ret = dk_buttons_init(ButtonEventHandler); @@ -210,21 +209,21 @@ CHIP_ERROR AppTask::StartApp() void AppTask::LightingActionEventHandler(AppEvent * aEvent) { - LightingManager::Action_t action = LightingManager::INVALID_ACTION; - int32_t actor = 0; + PWMDevice::Action_t action = PWMDevice::INVALID_ACTION; + int32_t actor = 0; if (aEvent->Type == AppEvent::kEventType_Lighting) { - action = static_cast(aEvent->LightingEvent.Action); + action = static_cast(aEvent->LightingEvent.Action); actor = aEvent->LightingEvent.Actor; } else if (aEvent->Type == AppEvent::kEventType_Button) { - action = LightingMgr().IsTurnedOn() ? LightingManager::OFF_ACTION : LightingManager::ON_ACTION; + action = GetAppTask().mPWMDevice.IsTurnedOn() ? PWMDevice::OFF_ACTION : PWMDevice::ON_ACTION; actor = AppEvent::kEventType_Button; } - if (action != LightingManager::INVALID_ACTION && !LightingMgr().InitiateAction(action, actor, 0, NULL)) + if (action != PWMDevice::INVALID_ACTION && GetAppTask().mPWMDevice.InitiateAction(action, actor, NULL)) LOG_INF("Action is already in progress or active."); } @@ -502,33 +501,33 @@ void AppTask::StartTimer(uint32_t aTimeoutInMs) mFunctionTimerActive = true; } -void AppTask::ActionInitiated(LightingManager::Action_t aAction, int32_t aActor) +void AppTask::ActionInitiated(PWMDevice::Action_t aAction, int32_t aActor) { - if (aAction == LightingManager::ON_ACTION) + if (aAction == PWMDevice::ON_ACTION) { LOG_INF("Turn On Action has been initiated"); } - else if (aAction == LightingManager::OFF_ACTION) + else if (aAction == PWMDevice::OFF_ACTION) { LOG_INF("Turn Off Action has been initiated"); } - else if (aAction == LightingManager::LEVEL_ACTION) + else if (aAction == PWMDevice::LEVEL_ACTION) { LOG_INF("Level Action has been initiated"); } } -void AppTask::ActionCompleted(LightingManager::Action_t aAction, int32_t aActor) +void AppTask::ActionCompleted(PWMDevice::Action_t aAction, int32_t aActor) { - if (aAction == LightingManager::ON_ACTION) + if (aAction == PWMDevice::ON_ACTION) { LOG_INF("Turn On Action has been completed"); } - else if (aAction == LightingManager::OFF_ACTION) + else if (aAction == PWMDevice::OFF_ACTION) { LOG_INF("Turn Off Action has been completed"); } - else if (aAction == LightingManager::LEVEL_ACTION) + else if (aAction == PWMDevice::LEVEL_ACTION) { LOG_INF("Level Action has been completed"); } @@ -539,7 +538,7 @@ void AppTask::ActionCompleted(LightingManager::Action_t aAction, int32_t aActor) } } -void AppTask::PostLightingActionRequest(LightingManager::Action_t aAction) +void AppTask::PostLightingActionRequest(PWMDevice::Action_t aAction) { AppEvent event; event.Type = AppEvent::kEventType_Lighting; @@ -571,14 +570,14 @@ void AppTask::DispatchEvent(AppEvent * aEvent) void AppTask::UpdateClusterState() { // write the new on/off value - EmberAfStatus status = Clusters::OnOff::Attributes::OnOff::Set(kLightEndpointId, LightingMgr().IsTurnedOn()); + EmberAfStatus status = Clusters::OnOff::Attributes::OnOff::Set(kLightEndpointId, mPWMDevice.IsTurnedOn()); if (status != EMBER_ZCL_STATUS_SUCCESS) { LOG_ERR("Updating on/off cluster failed: %x", status); } - status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kLightEndpointId, LightingMgr().GetLevel()); + status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kLightEndpointId, mPWMDevice.GetLevel()); if (status != EMBER_ZCL_STATUS_SUCCESS) { diff --git a/examples/lighting-app/nrfconnect/main/ZclCallbacks.cpp b/examples/lighting-app/nrfconnect/main/ZclCallbacks.cpp index 07267413a17fe4..457ebdf257cc1b 100644 --- a/examples/lighting-app/nrfconnect/main/ZclCallbacks.cpp +++ b/examples/lighting-app/nrfconnect/main/ZclCallbacks.cpp @@ -17,7 +17,7 @@ */ #include "AppTask.h" -#include "LightingManager.h" +#include "PWMDevice.h" #include #include @@ -36,13 +36,13 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & if (clusterId == OnOff::Id && attributeId == OnOff::Attributes::OnOff::Id) { ChipLogProgress(Zcl, "Cluster OnOff: attribute OnOff set to %u", *value); - LightingMgr().InitiateAction(*value ? LightingManager::ON_ACTION : LightingManager::OFF_ACTION, - AppEvent::kEventType_Lighting, size, value); + GetAppTask().GetLightingDevice().InitiateAction(*value ? PWMDevice::ON_ACTION : PWMDevice::OFF_ACTION, + AppEvent::kEventType_Lighting, value); } else if (clusterId == LevelControl::Id && attributeId == LevelControl::Attributes::CurrentLevel::Id) { ChipLogProgress(Zcl, "Cluster LevelControl: attribute CurrentLevel set to %u", *value); - LightingMgr().InitiateAction(LightingManager::LEVEL_ACTION, AppEvent::kEventType_Lighting, size, value); + GetAppTask().GetLightingDevice().InitiateAction(PWMDevice::LEVEL_ACTION, AppEvent::kEventType_Lighting, value); } } diff --git a/examples/lighting-app/nrfconnect/main/include/AppTask.h b/examples/lighting-app/nrfconnect/main/include/AppTask.h index 71767b76284584..4b0be0004efffe 100644 --- a/examples/lighting-app/nrfconnect/main/include/AppTask.h +++ b/examples/lighting-app/nrfconnect/main/include/AppTask.h @@ -20,7 +20,7 @@ #include "AppEvent.h" #include "LEDWidget.h" -#include "LightingManager.h" +#include "PWMDevice.h" #include @@ -42,12 +42,13 @@ class AppTask public: CHIP_ERROR StartApp(); - void PostLightingActionRequest(LightingManager::Action_t aAction); + void PostLightingActionRequest(PWMDevice::Action_t aAction); void PostEvent(AppEvent * event); void UpdateClusterState(); static void IdentifyStartHandler(Identify *); static void IdentifyStopHandler(Identify *); + PWMDevice & GetLightingDevice() { return mPWMDevice; } private: #ifdef CONFIG_CHIP_PW_RPC @@ -57,8 +58,8 @@ class AppTask friend AppTask & GetAppTask(void); CHIP_ERROR Init(); - static void ActionInitiated(LightingManager::Action_t aAction, int32_t aActor); - static void ActionCompleted(LightingManager::Action_t aAction, int32_t aActor); + static void ActionInitiated(PWMDevice::Action_t aAction, int32_t aActor); + static void ActionCompleted(PWMDevice::Action_t aAction, int32_t aActor); void CancelTimer(void); @@ -95,6 +96,7 @@ class AppTask Function_t mFunction = kFunction_NoneSelected; bool mFunctionTimerActive = false; + PWMDevice mPWMDevice; static AppTask sAppTask; }; diff --git a/examples/lighting-app/nrfconnect/main/LightingManager.cpp b/examples/platform/nrfconnect/util/PWMDevice.cpp similarity index 68% rename from examples/lighting-app/nrfconnect/main/LightingManager.cpp rename to examples/platform/nrfconnect/util/PWMDevice.cpp index 3fdd428db53815..929f538f792819 100644 --- a/examples/lighting-app/nrfconnect/main/LightingManager.cpp +++ b/examples/platform/nrfconnect/util/PWMDevice.cpp @@ -16,7 +16,7 @@ * limitations under the License. */ -#include "LightingManager.h" +#include "PWMDevice.h" #include "AppConfig.h" @@ -28,19 +28,14 @@ LOG_MODULE_DECLARE(app, CONFIG_MATTER_LOG_LEVEL); -LightingManager LightingManager::sLight; - -int LightingManager::Init(const device * pwmDevice, uint32_t pwmChannel, uint8_t minLevel, uint8_t maxLevel) +int PWMDevice::Init(const device * aPWMDevice, uint32_t aPWMChannel, uint8_t aMinLevel, uint8_t aMaxLevel, uint8_t aDefaultLevel) { - // We use a gpioPin instead of a LEDWidget here because we want to use PWM - // and other features instead of just on/off. - mState = kState_On; - mMinLevel = minLevel; - mMaxLevel = maxLevel; - mLevel = maxLevel; - mPwmDevice = pwmDevice; - mPwmChannel = pwmChannel; + mMinLevel = aMinLevel; + mMaxLevel = aMaxLevel; + mLevel = aDefaultLevel; + mPwmDevice = aPWMDevice; + mPwmChannel = aPWMChannel; if (!device_is_ready(mPwmDevice)) { @@ -52,13 +47,13 @@ int LightingManager::Init(const device * pwmDevice, uint32_t pwmChannel, uint8_t return 0; } -void LightingManager::SetCallbacks(LightingCallback_fn aActionInitiated_CB, LightingCallback_fn aActionCompleted_CB) +void PWMDevice::SetCallbacks(PWMCallback aActionInitiatedClb, PWMCallback aActionCompletedClb) { - mActionInitiated_CB = aActionInitiated_CB; - mActionCompleted_CB = aActionCompleted_CB; + mActionInitiatedClb = aActionInitiatedClb; + mActionCompletedClb = aActionCompletedClb; } -bool LightingManager::InitiateAction(Action_t aAction, int32_t aActor, uint16_t size, uint8_t * value) +bool PWMDevice::InitiateAction(Action_t aAction, int32_t aActor, uint8_t * aValue) { // TODO: this function is called InitiateAction because we want to implement some features such as ramping up here. bool action_initiated = false; @@ -75,10 +70,10 @@ bool LightingManager::InitiateAction(Action_t aAction, int32_t aActor, uint16_t action_initiated = true; new_state = kState_Off; } - else if (aAction == LEVEL_ACTION && *value != mLevel) + else if (aAction == LEVEL_ACTION && *aValue != mLevel) { action_initiated = true; - if (*value == 0) + if (*aValue == 0) { new_state = kState_Off; } @@ -90,9 +85,9 @@ bool LightingManager::InitiateAction(Action_t aAction, int32_t aActor, uint16_t if (action_initiated) { - if (mActionInitiated_CB) + if (mActionInitiatedClb) { - mActionInitiated_CB(aAction, aActor); + mActionInitiatedClb(aAction, aActor); } if (aAction == ON_ACTION || aAction == OFF_ACTION) @@ -101,32 +96,33 @@ bool LightingManager::InitiateAction(Action_t aAction, int32_t aActor, uint16_t } else if (aAction == LEVEL_ACTION) { - SetLevel(*value); + mState = new_state; + SetLevel(*aValue); } - if (mActionCompleted_CB) + if (mActionCompletedClb) { - mActionCompleted_CB(aAction, aActor); + mActionCompletedClb(aAction, aActor); } } return action_initiated; } -void LightingManager::SetLevel(uint8_t aLevel) +void PWMDevice::SetLevel(uint8_t aLevel) { LOG_INF("Setting brightness level to %u", aLevel); mLevel = aLevel; UpdateLight(); } -void LightingManager::Set(bool aOn) +void PWMDevice::Set(bool aOn) { mState = aOn ? kState_On : kState_Off; UpdateLight(); } -void LightingManager::UpdateLight() +void PWMDevice::UpdateLight() { constexpr uint32_t kPwmWidthUs = 20000u; const uint8_t maxEffectiveLevel = mMaxLevel - mMinLevel; diff --git a/examples/all-clusters-app/nrfconnect/main/include/Utils.h b/examples/platform/nrfconnect/util/include/LEDUtil.h similarity index 97% rename from examples/all-clusters-app/nrfconnect/main/include/Utils.h rename to examples/platform/nrfconnect/util/include/LEDUtil.h index fe02c3aa176ce6..3066f1506b89b5 100644 --- a/examples/all-clusters-app/nrfconnect/main/include/Utils.h +++ b/examples/platform/nrfconnect/util/include/LEDUtil.h @@ -19,7 +19,7 @@ #include -// A lightweight wrrapper for unused LEDs +// A lightweight wrapper for unused LEDs template class UnusedLedsWrapper { diff --git a/examples/lighting-app/nrfconnect/main/include/LightingManager.h b/examples/platform/nrfconnect/util/include/PWMDevice.h similarity index 64% rename from examples/lighting-app/nrfconnect/main/include/LightingManager.h rename to examples/platform/nrfconnect/util/include/PWMDevice.h index 1d9005c077add8..d83a45d92e982b 100644 --- a/examples/lighting-app/nrfconnect/main/include/LightingManager.h +++ b/examples/platform/nrfconnect/util/include/PWMDevice.h @@ -1,6 +1,5 @@ /* - * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2022 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,12 +17,10 @@ #pragma once -#include "AppEvent.h" - #include #include -class LightingManager +class PWMDevice { public: enum Action_t : uint8_t @@ -41,16 +38,18 @@ class LightingManager kState_Off, }; - using LightingCallback_fn = void (*)(Action_t, int32_t); + using PWMCallback = void (*)(Action_t, int32_t); - int Init(const device * pwmDevice, uint32_t pwmChannel, uint8_t minLevel, uint8_t maxLevel); + int Init(const device * aPWMDevice, uint32_t aPWMChannel, uint8_t aMinLevel, uint8_t aMaxLevel, uint8_t aDefaultLevel = 0); bool IsTurnedOn() const { return mState == kState_On; } uint8_t GetLevel() const { return mLevel; } - bool InitiateAction(Action_t aAction, int32_t aActor, uint16_t size, uint8_t * value); - void SetCallbacks(LightingCallback_fn aActionInitiated_CB, LightingCallback_fn aActionCompleted_CB); + uint8_t GetMinLevel() const { return mMinLevel; } + uint8_t GetMaxLevel() const { return mMaxLevel; } + bool InitiateAction(Action_t aAction, int32_t aActor, uint8_t * aValue); + void SetCallbacks(PWMCallback aActionInitiatedClb, PWMCallback aActionCompletedClb); + const device * GetDevice() { return mPwmDevice; } private: - friend LightingManager & LightingMgr(); State_t mState; uint8_t mMinLevel; uint8_t mMaxLevel; @@ -58,17 +57,10 @@ class LightingManager const device * mPwmDevice; uint32_t mPwmChannel; - LightingCallback_fn mActionInitiated_CB; - LightingCallback_fn mActionCompleted_CB; + PWMCallback mActionInitiatedClb; + PWMCallback mActionCompletedClb; void Set(bool aOn); void SetLevel(uint8_t aLevel); void UpdateLight(); - - static LightingManager sLight; }; - -inline LightingManager & LightingMgr(void) -{ - return LightingManager::sLight; -} diff --git a/examples/window-app/nrfconnect/.gitignore b/examples/window-app/nrfconnect/.gitignore new file mode 100644 index 00000000000000..84c048a73cc2e5 --- /dev/null +++ b/examples/window-app/nrfconnect/.gitignore @@ -0,0 +1 @@ +/build/ diff --git a/examples/window-app/nrfconnect/CMakeLists.txt b/examples/window-app/nrfconnect/CMakeLists.txt new file mode 100644 index 00000000000000..038b7485d8b4ab --- /dev/null +++ b/examples/window-app/nrfconnect/CMakeLists.txt @@ -0,0 +1,78 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# 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. +# +cmake_minimum_required(VERSION 3.13.1) + +get_filename_component(CHIP_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/third_party/connectedhomeip REALPATH) +get_filename_component(NRFCONNECT_COMMON ${CHIP_ROOT}/examples/platform/nrfconnect REALPATH) +get_filename_component(GEN_DIR ${CHIP_ROOT}/zzz_generated/ REALPATH) +get_filename_component(WIN_APP_COMMON_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../common/ REALPATH) + + +include(${CHIP_ROOT}/config/nrfconnect/app/check-nrfconnect-version.cmake) + +# Set Kconfig root files that will be processed as a first Kconfig for used child images. +set(mcuboot_KCONFIG_ROOT ${CHIP_ROOT}/config/nrfconnect/chip-module/Kconfig.mcuboot.root) +set(multiprotocol_rpmsg_KCONFIG_ROOT ${CHIP_ROOT}/config/nrfconnect/chip-module/Kconfig.multiprotocol_rpmsg.root) + +if(NOT CONF_FILE STREQUAL "prj_no_dfu.conf") + set(PM_STATIC_YML_FILE ${CMAKE_CURRENT_SOURCE_DIR}/configuration/${BOARD}/pm_static_dfu.yml) +endif() + +list(APPEND ZEPHYR_EXTRA_MODULES ${CHIP_ROOT}/config/nrfconnect/chip-module) +find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) + +# -Wmaybe-uninitialized has too many false positives, including on std::optional +# and chip::Optional. Make it nonfatal. +# +# See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80635 +target_compile_options(app PRIVATE -Werror -Wno-error=maybe-uninitialized) + +project(chip-nrfconnect-window-app-example) + +include(${CHIP_ROOT}/config/nrfconnect/app/enable-gnu-std.cmake) +include(${CHIP_ROOT}/config/nrfconnect/app/flashing.cmake) +include(${CHIP_ROOT}/src/app/chip_data_model.cmake) + +target_include_directories(app PRIVATE + main/include + ${GEN_DIR}/app-common + ${GEN_DIR}/window-app + ${NRFCONNECT_COMMON}/util/include + ${NRFCONNECT_COMMON}/app/include) + +target_sources(app PRIVATE + main/AppTask.cpp + main/main.cpp + main/ZclCallbacks.cpp + main/WindowCovering.cpp + ${GEN_DIR}/window-app/zap-generated/callback-stub.cpp + ${GEN_DIR}/window-app/zap-generated/IMClusterCommandHandler.cpp + ${NRFCONNECT_COMMON}/util/LEDWidget.cpp + ${NRFCONNECT_COMMON}/util/PWMDevice.cpp + ${NRFCONNECT_COMMON}/util/ThreadUtil.cpp) + +chip_configure_data_model(app + INCLUDE_SERVER + ZAP_FILE ${WIN_APP_COMMON_DIR}/window-app.zap +) + +if(CONFIG_CHIP_OTA_REQUESTOR) + target_sources(app PRIVATE ${NRFCONNECT_COMMON}/util/OTAUtil.cpp) +endif() + +if(CONFIG_MCUMGR_SMP_BT) + target_sources(app PRIVATE ${NRFCONNECT_COMMON}/util/DFUOverSMP.cpp) +endif() diff --git a/examples/window-app/nrfconnect/Kconfig b/examples/window-app/nrfconnect/Kconfig new file mode 100644 index 00000000000000..979bf186956cb7 --- /dev/null +++ b/examples/window-app/nrfconnect/Kconfig @@ -0,0 +1,28 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# 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. +# +mainmenu "Matter nRF Connect Lock Example Application" + +config STATE_LEDS + bool "Use LEDs to indicate the device state" + default y + help + Use LEDs to render the current state of the device such as the progress of commissioning of + the device into a network or the factory reset initiation. Note that setting this option to + 'n' does not disable the LED indicating the state of the simulated bolt. + +rsource "../../../config/nrfconnect/chip-module/Kconfig.defaults" +rsource "../../../config/nrfconnect/chip-module/Kconfig.features" +source "Kconfig.zephyr" diff --git a/examples/window-app/nrfconnect/README.md b/examples/window-app/nrfconnect/README.md new file mode 100644 index 00000000000000..8cf7548a42f8fb --- /dev/null +++ b/examples/window-app/nrfconnect/README.md @@ -0,0 +1,523 @@ +# Matter nRF Connect Window Covering Example Application + +The nRF Connect Window Covering Example demonstrates how to remotely control a +window shutter device. It uses buttons to test changing cover position and +device states and LEDs to show the state of these changes. You can use this +example as a reference for creating your own application. + +

+ Nordic Semiconductor logo + nRF52840 DK +

+ +The example is based on +[Matter](https://github.com/project-chip/connectedhomeip) and Nordic +Semiconductor's nRF Connect SDK, and supports remote access and control of a +simulated window shutter over a low-power, 802.15.4 Thread network. + +The example behaves as a Matter accessory, that is a device that can be paired +into an existing Matter network and can be controlled by this network. + +
+ +- [Overview](#overview) + - [Bluetooth LE advertising](#bluetooth-le-advertising) + - [Bluetooth LE rendezvous](#bluetooth-le-rendezvous) + - [Device Firmware Upgrade](#device-firmware-upgrade) +- [Requirements](#requirements) + - [Supported devices](#supported_devices) +- [Device UI](#device-ui) +- [Setting up the environment](#setting-up-the-environment) + - [Using Docker container for setup](#using-docker-container-for-setup) + - [Using native shell for setup](#using-native-shell-for-setup) +- [Building](#building) + - [Removing build artifacts](#removing-build-artifacts) + - [Building with release configuration](#building-with-release-configuration) + - [Building with low-power configuration](#building-with-low-power-configuration) + - [Building with Device Firmware Upgrade support](#building-with-device-firmware-upgrade-support) +- [Configuring the example](#configuring-the-example) + - [Example build types](#example-build-types) +- [Flashing and debugging](#flashing-and-debugging) +- [Testing the example](#testing-the-example) + - [Testing using CHIPTool](#testing-using-chiptool) + - [Testing Device Firmware Upgrade](#testing-device-firmware-upgrade) + +
+ + + +## Overview + +This example is running on the nRF Connect platform, which is based on Nordic +Semiconductor's +[nRF Connect SDK](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/index.html) +and [Zephyr RTOS](https://zephyrproject.org/). Visit Matter's +[nRF Connect platform overview](../../../docs/guides/nrfconnect_platform_overview.md) +to read more about the platform structure and dependencies. + +The Matter device that runs the window shutter application is controlled by the +Matter controller device over the Thread protocol. By default, the Matter device +has Thread disabled, and it should be paired with Matter controller and get +configuration from it. Some actions required before establishing full +communication are described below. + +The example can be configured to use the secure bootloader and utilize it for +performing over-the-air Device Firmware Upgrade using Bluetooth LE. + +### Bluetooth LE advertising + +In this example, to commission the device onto a Matter network, it must be +discoverable over Bluetooth LE. For security reasons, you must start Bluetooth +LE advertising manually after powering up the device by pressing **Button 4**. + +### Bluetooth LE rendezvous + +In this example, the commissioning procedure is done over Bluetooth LE between a +Matter device and the Matter controller, where the controller has the +commissioner role. + +To start the rendezvous, the controller must get the commissioning information +from the Matter device. The data payload is encoded within a QR code, printed to +the UART console, and shared using an NFC tag. NFC tag emulation starts +automatically when Bluetooth LE advertising is started and stays enabled until +Bluetooth LE advertising timeout expires. + +#### Thread provisioning + +Last part of the rendezvous procedure, the provisioning operation involves +sending the Thread network credentials from the Matter controller to the Matter +device. As a result, device is able to join the Thread network and communicate +with other Thread devices in the network. + +### Device Firmware Upgrade + +The example supports over-the-air (OTA) device firmware upgrade (DFU) using the +Matter OTA update, which is mandatory for Matter-compliant devices and enabled +by default. + +For this method, the +[MCUboot](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/mcuboot/index.html) +bootloader solution is used to replace the old firmware image with the new one. + +#### Matter Over-the-Air Update + +The Matter over-the-air update distinguishes two types of nodes: OTA Provider +and OTA Requestor. + +An OTA Provider is a node that hosts a new firmware image and is able to respond +on an OTA Requestor's queries regarding availability of new firmware images or +requests to start sending the update packages. + +An OTA Requestor is a node that wants to download a new firmware image and sends +requests to an OTA Provider to start the update process. + +#### Bootloader + +MCUboot is a secure bootloader used for swapping firmware images of different +versions and generating proper build output files that can be used in the device +firmware upgrade process. + +The bootloader solution requires an area of flash memory to swap application +images during the firmware upgrade. Nordic Semiconductor devices use an external +memory chip for this purpose. The memory chip communicates with the +microcontroller through the QSPI bus. + +See the +[Building with Device Firmware Upgrade support](#building-with-device-firmware-upgrade-support) +section to learn how to change MCUboot and flash configuration in this example. + +
+ + + +## Requirements + +The application requires a specific revision of the nRF Connect SDK to work +correctly. See [Setting up the environment](#setting-up-the-environment) for +more information. + + + +### Supported devices + +The example supports building and running on the following devices: + +| Hardware platform | Build target | Platform image | +| ----------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| [nRF52840 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) | `nrf52840dk_nrf52840` |
nRF52840 DKnRF52840 DK
| +| [nRF5340 DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF5340-DK) | `nrf5340dk_nrf5340_cpuapp` |
nRF5340 DKnRF5340 DK
| + +
+ + + +## Device UI + +This section lists the User Interface elements that you can use to control and +monitor the state of the device. These correspond to PCB components on the +platform image. + +**LED 1** shows the overall state of the device and its connectivity. The +following states are possible: + +- _Short Flash On (50 ms on/950 ms off)_ — The device is in the + unprovisioned (unpaired) state and is waiting for a commissioning + application to connect. + +- _Rapid Even Flashing (100 ms on/100 ms off)_ — The device is in the + unprovisioned state and a commissioning application is connected through + Bluetooth LE. + +- _Short Flash Off (950ms on/50ms off)_ — The device is fully + provisioned, but does not yet have full Thread network or service + connectivity. + +- _Solid On_ — The device is fully provisioned and has full Thread + network and service connectivity. + +**LED 2** indicates the lift position of the shutter, which is represented by +the brightness of the LED. The brightness level ranges from 0 to 255, where the +switched off LED with brightness level 0 indicates a fully opened shutter +(lifted) and 255 indicates a fully closed shutter (lowered). + +**LED 3** indicates the tilt position of the shutter, which is represented by +the brightness of the LED. The brightness level ranges from 0 to 255, where the +switched off LED with brightness level 0 indicates a fully opened shutter +(tilted to a horizontal position) and 255 indicates a fully closed shutter +(tilted to a vertical position). + +**Button 1** can be used for the following purposes: + +- _Pressed for 6 s_ — Initiates the factory reset of the device. + Releasing the button within the 6-second window cancels the factory reset + procedure. **LED 1** and **LED 4** blink in unison when the factory reset + procedure is initiated. + +- _Pressed for less than 3 s_ — Initiates the OTA software update + process. This feature is disabled by default, but can be enabled by + following the + [Building with Device Firmware Upgrade support](#building-with-device-firmware-upgrade-support) + instruction. + +**Button 2** — Pressing the button once moves the shutter towards the open +position by one step. Depending on the current movement mode, the button +decreases the brightness of **LED2** for the lift mode and **LED3** for the tilt +mode. + +**Button 3** — Pressing the button once moves the shutter towards the +closed position by one step. Depending on the current movement mode, the button +increases the brightness of **LED2** for the lift mode and **LED3** for the tilt +mode. + +**Button 2** and **Button 3** — Pressing both buttons at the same time +toggles the shutter movement mode between lift and tilt. After each device +reset, the mode is set to lift by default. + +> **Note**: +> +> Completely opening and closing the shutter requires 20 button presses (steps). +> Each step takes approximately 200 ms to simulate the real shutter movement. +> The shutter position and LED brightness values are stored in non-volatile +> memory and are restored after every device reset. After the firmware update or +> factory reset both LEDs are switched off by default, which corresponds to the +> shutter being fully open, both lift-wise and tilt-wise. + +**Button 4** — Pressing the button once starts the NFC tag emulation and +enables Bluetooth LE advertising for the predefined period of time (15 minutes +by default). + +**SEGGER J-Link USB port** can be used to get logs from the device or +communicate with it using the +[command line interface](../../../docs/guides/nrfconnect_examples_cli.md). + +**NFC port with antenna attached** can be used to start the +[rendezvous](#bluetooth-le-rendezvous) by providing the commissioning +information from the Matter device in a data payload that can be shared using +NFC. + +
+ +## Setting up the environment + +Before building the example, check out the Matter repository and sync submodules +using the following command: + + $ git submodule update --init + +The example requires a specific revision of the nRF Connect SDK. You can either +install it along with the related tools directly on your system or use a Docker +image that has the tools pre-installed. + +If you are a macOS user, you won't be able to use the Docker container to flash +the application onto a Nordic development kit due to +[certain limitations of Docker for macOS](https://docs.docker.com/docker-for-mac/faqs/#can-i-pass-through-a-usb-device-to-a-container). +Use the [native shell](#using-native-shell) for building instead. + +### Using Docker container for setup + +To use the Docker container for setup, complete the following steps: + +1. If you do not have the nRF Connect SDK installed yet, create a directory for + it by running the following command: + + $ mkdir ~/nrfconnect + +2. Download the latest version of the nRF Connect SDK Docker image by running + the following command: + + $ docker pull nordicsemi/nrfconnect-chip + +3. Start Docker with the downloaded image by running the following command, + customized to your needs as described below: + + $ docker run --rm -it -e RUNAS=$(id -u) -v ~/nrfconnect:/var/ncs -v ~/connectedhomeip:/var/chip \ + -v /dev/bus/usb:/dev/bus/usb --device-cgroup-rule "c 189:* rmw" nordicsemi/nrfconnect-chip + + In this command: + + - _~/nrfconnect_ can be replaced with an absolute path to the nRF Connect + SDK source directory. + - _~/connectedhomeip_ must be replaced with an absolute path to the CHIP + source directory. + - _-v /dev/bus/usb:/dev/bus/usb --device-cgroup-rule "c 189:_ rmw"\* + parameters can be omitted if you are not planning to flash the example + onto hardware. These parameters give the container access to USB devices + connected to your computer such as the nRF52840 DK. + - _--rm_ can be omitted if you do not want the container to be + auto-removed when you exit the container shell session. + - _-e RUNAS=\$(id -u)_ is needed to start the container session as the + current user instead of root. + +4. Update the nRF Connect SDK to the most recent supported revision, by running + the following command: + + $ cd /var/chip + $ python3 scripts/setup/nrfconnect/update_ncs.py --update + +Now you can proceed with the [Building](#building) instruction. + +### Using native shell for setup + +To use the native shell for setup, complete the following steps: + +1. Download and install the following additional software: + + - [nRF Command Line Tools](https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Command-Line-Tools) + - [GN meta-build system](https://gn.googlesource.com/gn/) + +2. If you do not have the nRF Connect SDK installed, follow the + [guide](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/gs_assistant.html#) + in the nRF Connect SDK documentation to install the latest stable nRF + Connect SDK version. Since command-line tools will be used for building the + example, installing SEGGER Embedded Studio is not required. + + If you have the SDK already installed, continue to the next step and update + the nRF Connect SDK after initializing environment variables. + +3. Initialize environment variables referred to by the CHIP and the nRF Connect + SDK build scripts. Replace _nrfconnect-dir_ with the path to your nRF + Connect SDK installation directory, and _toolchain-dir_ with the path to GNU + Arm Embedded Toolchain. + + $ source nrfconnect-dir/zephyr/zephyr-env.sh + $ export ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb + $ export GNUARMEMB_TOOLCHAIN_PATH=toolchain-dir + +4. Update the nRF Connect SDK to the most recent supported revision by running + the following command (replace _matter-dir_ with the path to Matter + repository directory): + + $ cd matter-dir + $ python3 scripts/setup/nrfconnect/update_ncs.py --update + +Now you can proceed with the [Building](#building) instruction. + +
+ + + +## Building + +Complete the following steps, regardless of the method used for setting up the +environment: + +1. Navigate to the example's directory: + + $ cd examples/window-app/nrfconnect + +2. Run the following command to build the example, with _build-target_ replaced + with the build target name of the Nordic Semiconductor's kit you own, for + example `nrf52840dk_nrf52840`: + + $ west build -b build-target + + You only need to specify the build target on the first build. See + [Requirements](#requirements) for the build target names of compatible kits. + +The output `zephyr.hex` file will be available in the `build/zephyr/` directory. + +### Removing build artifacts + +If you're planning to build the example for a different kit or make changes to +the configuration, remove all build artifacts before building. To do so, use the +following command: + + $ rm -r build + +### Building with release configuration + +To build the example with release configuration that disables the diagnostic +features like logs and command-line interface, run the following command: + + $ west build -b build-target -- -DCONF_FILE=prj_release.conf + +Remember to replace _build-target_ with the build target name of the Nordic +Semiconductor's kit you own. + +### Building with low-power configuration + +You can build the example using the low-power configuration, which enables +Thread's Sleepy End Device mode and disables debug features, such as the UART +console or the **LED 1** usage. + +To build for the low-power configuration, run the following command with +_build-target_ replaced with the build target name of the Nordic Semiconductor's +kit you own (for example `nrf52840dk_nrf52840`): + + $ west build -b build-target -- -DOVERLAY_CONFIG=overlay-low_power.conf + +For example, use the following command for `nrf52840dk_nrf52840`: + + $ west build -b nrf52840dk_nrf52840 -- -DOVERLAY_CONFIG=overlay-low_power.conf + +### Building with Device Firmware Upgrade support + +Support for DFU using Matter OTA is enabled by default. + +To completely disable support for DFU, run the following command with +_build-target_ replaced with the build target name of the Nordic Semiconductor +kit you are using (for example `nrf52840dk_nrf52840`): + + $ west build -b build-target -- -DCONF_FILE=prj_no_dfu.conf + +> **Note**: +> +> There are two types of Device Firmware Upgrade modes: single-image DFU and +> multi-image DFU. Single-image mode supports upgrading only one firmware image, +> the application image, and should be used for single-core nRF52840 DK devices. +> Multi-image mode allows to upgrade more firmware images and is suitable for +> upgrading the application core and network core firmware in two-core nRF5340 +> DK devices. + +#### Changing bootloader configuration + +To change the default MCUboot configuration, edit the `prj.conf` file located in +the `child_image/mcuboot` directory. + +Make sure to keep the configuration consistent with changes made to the +application configuration. This is necessary for the configuration to work, as +the bootloader image is a separate application from the user application and it +has its own configuration file. + +#### Changing flash memory settings + +In the default configuration, the MCUboot uses the +[Partition Manager](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/scripts/partition_manager/partition_manager.html#partition-manager) +to configure flash partitions used for the bootloader application image slot +purposes. You can change these settings by defining +[static partitions](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/scripts/partition_manager/partition_manager.html#ug-pm-static). +This example uses this option to define using an external flash. + +To modify the flash settings of your board (that is, your _build-target_, for +example `nrf52840dk_nrf52840`), edit the `pm_static_dfu.yml` file located in the +`configuration/build-target/` directory. + +
+ + + +## Configuring the example + +The Zephyr ecosystem is based on Kconfig files and the settings can be modified +using the menuconfig utility. + +To open the menuconfig utility, run the following command from the example +directory: + + $ west build -b build-target -t menuconfig + +Remember to replace _build-target_ with the build target name of the Nordic +Semiconductor's kit you own. + +Changes done with menuconfig will be lost if the `build` directory is deleted. +To make them persistent, save the configuration options in the `prj.conf` file. + +### Example build types + +The example uses different configuration files depending on the supported +features. Configuration files are provided for different build types and they +are located in the application root directory. + +The `prj.conf` file represents a debug build type. Other build types are covered +by dedicated files with the build type added as a suffix to the prj part, as per +the following list. For example, the release build type file name is +`prj_release.conf`. If a board has other configuration files, for example +associated with partition layout or child image configuration, these follow the +same pattern. + +Before you start testing the application, you can select one of the build types +supported by the sample. This sample supports the following build types, +depending on the selected board: + +- debug — Debug version of the application - can be used to enable + additional features for verifying the application behavior, such as logs or + command-line shell. +- release — Release version of the application - can be used to enable + only the necessary application functionalities to optimize its performance. +- no_dfu — Debug version of the application without Device Firmware + Upgrade feature support - can be used only for the nRF52840 DK and nRF5340 + DK, as those platforms have DFU enabled by default. + +For more information, see the +[Configuring nRF Connect SDK examples](../../../docs/guides/nrfconnect_examples_configuration.md) +page. + +
+ + + +## Flashing and debugging + +To flash the application to the device, use the west tool and run the following +command from the example directory: + + $ west flash --erase + +If you have multiple development kits connected, west will prompt you to pick +the correct one. + +To debug the application on target, run the following command from the example +directory: + + $ west debug + +
+ +## Testing the example + +Check the [CLI tutorial](../../../docs/guides/nrfconnect_examples_cli.md) to +learn how to use command-line interface of the application. + +### Testing using CHIPTool + +Read the +[Android commissioning guide](../../../docs/guides/nrfconnect_android_commissioning.md) +to see how to use [CHIPTool](../../../src/android/CHIPTool/README.md) for +Android smartphones to commission and control the application within a +Matter-enabled Thread network. + +### Testing Device Firmware Upgrade + +Read the +[DFU tutorial](../../../docs/guides/nrfconnect_examples_software_update.md) to +see how to upgrade your device firmware. diff --git a/examples/window-app/nrfconnect/boards/nrf52840dk_nrf52840.overlay b/examples/window-app/nrfconnect/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 00000000000000..b16a44113b8aef --- /dev/null +++ b/examples/window-app/nrfconnect/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 Project CHIP Authors + * + * 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. + */ + +/ { + chosen { + nordic,pm-ext-flash = &mx25r64; + }; + + /* + * In some default configurations within the nRF Connect SDK, + * e.g. on nRF52840, the chosen zephyr,entropy node is &cryptocell. + * This devicetree overlay ensures that default is overridden wherever it + * is set, as this application uses the RNG node for entropy exclusively. + */ + chosen { + zephyr,entropy = &rng; + }; + + /* + * By default, PWM module is only configured for led0 (LED1 on the board). + * The window-app, however, uses LED2 to show the state of the window cover, + * by using the LED's brightness level. + */ + aliases { + pwm-led1 = &pwm_led1; + pwm-led2 = &pwm_led2; + }; + + pwmleds { + pwm_led1: pwm_led_1 { + pwms = < &pwm0 0xe >; + }; + pwm_led2: pwm_led_2 { + pwms = < &pwm1 0xf >; + }; + }; +}; + +/* Disable unused peripherals to reduce power consumption */ +&adc { + status = "disabled"; +}; +&uart1 { + status = "disabled"; +}; +&gpio1 { + status = "disabled"; +}; +&i2c0 { + status = "disabled"; +}; +&spi1 { + status = "disabled"; +}; +&spi3 { + status = "disabled"; +}; +&usbd { + status = "disabled"; +}; + +&pwm0 { + /delete-property/ ch0-pin; + /delete-property/ ch0-inverted; + ch1-pin = < 0xe >; + ch1-inverted; +}; + +&pwm1 { + status = "okay"; + ch1-pin = < 0xf >; + ch1-inverted; +}; diff --git a/examples/window-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay b/examples/window-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 00000000000000..88368b93afe890 --- /dev/null +++ b/examples/window-app/nrfconnect/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022 Project CHIP Authors + * + * 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. + */ + +/ { + chosen { + nordic,pm-ext-flash = &mx25r64; + }; + + /* + * By default, PWM module is only configured for led0 (LED1 on the board). + * The lighting-app, however, uses LED2 to show the state of the lighting, + * including its brightness level. + */ + aliases { + pwm-led1 = &pwm_led1; + pwm-led2 = &pwm_led2; + }; + + pwmleds { + compatible = "pwm-leds"; + pwm_led1: pwm_led_1 { + pwms = < &pwm0 0x1d >; + }; + pwm_led2: pwm_led_2 { + pwms = < &pwm1 0x1e >; + }; + }; + + soc { + /* Add a flash controller which has the compatible + * 'zephyr,sim-flash'. This will ensure that the flash + * simulator can use it. None of the other properties in this + * node is used for anything. + */ + nordic_ram_flash_controller: nordic_ram-flash-controller@0 { + compatible = "zephyr,sim-flash"; + reg = <0x00000000 DT_SIZE_K(40)>; + #address-cells = <1>; + #size-cells = <1>; + erase-value = <0xff>; + label = "nordic_ram_flash_flash_controller"; + + /* This node label must match that used in the flash + * simulator. + */ + flash_sim0: flash_sim@0 { + status = "okay"; + compatible = "soc-nv-flash"; + label = "simulated_flash"; + erase-block-size = <4096>; + write-block-size = <4>; + reg = <0x00000000 DT_SIZE_K(256)>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* This partition must be defined for + * MCUboot to find the partition ID + * of the primary slot for image 1, + * which is stored in this partition. + */ + slot2_partition: partition@0 { + label = "image-2"; + reg = <0x00000000 0x00000A000>; + }; + }; + }; + }; + }; + +}; + +/* Disable unused peripherals to reduce power consumption */ +&adc { + status = "disabled"; +}; +&gpio1 { + status = "disabled"; +}; +&i2c1 { + status = "disabled"; +}; +&spi2 { + status = "disabled"; +}; +&usbd { + status = "disabled"; +}; + +&pwm0 { + /delete-property/ ch0-pin; + /delete-property/ ch0-inverted; + ch1-pin = < 0x1d >; + ch1-inverted; +}; + +&pwm1 { + status = "okay"; + ch1-pin = < 0x1e >; + ch1-inverted; +}; diff --git a/examples/window-app/nrfconnect/child_image/mcuboot/prj.conf b/examples/window-app/nrfconnect/child_image/mcuboot/prj.conf new file mode 100644 index 00000000000000..287c7829c6a5cf --- /dev/null +++ b/examples/window-app/nrfconnect/child_image/mcuboot/prj.conf @@ -0,0 +1,30 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# 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. +# + +# This target uses Kconfig.mcuboot.defaults to set options common for all +# samples using mcuboot. This file should contain only options specific for this sample +# mcuboot configuration or overrides of default values. + +CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" + +# Bootloader size optimization +# Disable not used modules that cannot be set in Kconfig.mcuboot.defaults due to overriding +# in board files. +CONFIG_GPIO=n +CONFIG_CONSOLE=n +CONFIG_SERIAL=n +CONFIG_UART_CONSOLE=n +CONFIG_USE_SEGGER_RTT=n diff --git a/examples/window-app/nrfconnect/child_image/mcuboot/prj_release.conf b/examples/window-app/nrfconnect/child_image/mcuboot/prj_release.conf new file mode 100644 index 00000000000000..287c7829c6a5cf --- /dev/null +++ b/examples/window-app/nrfconnect/child_image/mcuboot/prj_release.conf @@ -0,0 +1,30 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# 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. +# + +# This target uses Kconfig.mcuboot.defaults to set options common for all +# samples using mcuboot. This file should contain only options specific for this sample +# mcuboot configuration or overrides of default values. + +CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" + +# Bootloader size optimization +# Disable not used modules that cannot be set in Kconfig.mcuboot.defaults due to overriding +# in board files. +CONFIG_GPIO=n +CONFIG_CONSOLE=n +CONFIG_SERIAL=n +CONFIG_UART_CONSOLE=n +CONFIG_USE_SEGGER_RTT=n diff --git a/examples/window-app/nrfconnect/child_image/multiprotocol_rpmsg/prj.conf b/examples/window-app/nrfconnect/child_image/multiprotocol_rpmsg/prj.conf new file mode 100644 index 00000000000000..f43614c64500d7 --- /dev/null +++ b/examples/window-app/nrfconnect/child_image/multiprotocol_rpmsg/prj.conf @@ -0,0 +1,25 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# 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. +# + +# This target uses Kconfig.multiprotocol_rpmsg.defaults to set options common for all +# samples using multiprotocol_rpmsg. This file should contain only options specific for this sample +# multiprotocol_rpmsg configuration or overrides of default values. + +# Disable not used modules that cannot be set in Kconfig.multiprotocol_rpmsg.defaults due to overriding +# in board files. + +CONFIG_SERIAL=n +CONFIG_UART_CONSOLE=n diff --git a/examples/window-app/nrfconnect/child_image/multiprotocol_rpmsg/prj_no_dfu.conf b/examples/window-app/nrfconnect/child_image/multiprotocol_rpmsg/prj_no_dfu.conf new file mode 100644 index 00000000000000..f43614c64500d7 --- /dev/null +++ b/examples/window-app/nrfconnect/child_image/multiprotocol_rpmsg/prj_no_dfu.conf @@ -0,0 +1,25 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# 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. +# + +# This target uses Kconfig.multiprotocol_rpmsg.defaults to set options common for all +# samples using multiprotocol_rpmsg. This file should contain only options specific for this sample +# multiprotocol_rpmsg configuration or overrides of default values. + +# Disable not used modules that cannot be set in Kconfig.multiprotocol_rpmsg.defaults due to overriding +# in board files. + +CONFIG_SERIAL=n +CONFIG_UART_CONSOLE=n diff --git a/examples/window-app/nrfconnect/child_image/multiprotocol_rpmsg/prj_release.conf b/examples/window-app/nrfconnect/child_image/multiprotocol_rpmsg/prj_release.conf new file mode 100644 index 00000000000000..f43614c64500d7 --- /dev/null +++ b/examples/window-app/nrfconnect/child_image/multiprotocol_rpmsg/prj_release.conf @@ -0,0 +1,25 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# 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. +# + +# This target uses Kconfig.multiprotocol_rpmsg.defaults to set options common for all +# samples using multiprotocol_rpmsg. This file should contain only options specific for this sample +# multiprotocol_rpmsg configuration or overrides of default values. + +# Disable not used modules that cannot be set in Kconfig.multiprotocol_rpmsg.defaults due to overriding +# in board files. + +CONFIG_SERIAL=n +CONFIG_UART_CONSOLE=n diff --git a/examples/window-app/nrfconnect/configuration/nrf52840dk_nrf52840/pm_static_dfu.yml b/examples/window-app/nrfconnect/configuration/nrf52840dk_nrf52840/pm_static_dfu.yml new file mode 100644 index 00000000000000..9c26550c0902be --- /dev/null +++ b/examples/window-app/nrfconnect/configuration/nrf52840dk_nrf52840/pm_static_dfu.yml @@ -0,0 +1,38 @@ +mcuboot: + address: 0x0 + size: 0x7000 + region: flash_primary +mcuboot_pad: + address: 0x7000 + size: 0x200 +app: + address: 0x7200 + size: 0xf0e00 +mcuboot_primary: + orig_span: &id001 + - mcuboot_pad + - app + span: *id001 + address: 0x7000 + size: 0xf1000 + region: flash_primary +mcuboot_primary_app: + orig_span: &id002 + - app + span: *id002 + address: 0x7200 + size: 0xf0e00 +settings_storage: + address: 0xf8000 + size: 0x8000 + region: flash_primary +mcuboot_secondary: + address: 0x0 + size: 0xf1000 + device: MX25R64 + region: external_flash +external_flash: + address: 0xf1000 + size: 0x70f000 + device: MX25R64 + region: external_flash diff --git a/examples/window-app/nrfconnect/configuration/nrf5340dk_nrf5340_cpuapp/pm_static_dfu.yml b/examples/window-app/nrfconnect/configuration/nrf5340dk_nrf5340_cpuapp/pm_static_dfu.yml new file mode 100644 index 00000000000000..9f9f677d8af4fc --- /dev/null +++ b/examples/window-app/nrfconnect/configuration/nrf5340dk_nrf5340_cpuapp/pm_static_dfu.yml @@ -0,0 +1,52 @@ +mcuboot: + address: 0x0 + size: 0xC000 + region: flash_primary +mcuboot_pad: + address: 0xC000 + size: 0x200 +app: + address: 0xC200 + size: 0xebe00 +mcuboot_primary: + orig_span: &id001 + - mcuboot_pad + - app + span: *id001 + address: 0xC000 + size: 0xec000 + region: flash_primary +mcuboot_primary_app: + orig_span: &id002 + - app + span: *id002 + address: 0xC200 + size: 0xebe00 +settings_storage: + address: 0xf8000 + size: 0x8000 + region: flash_primary +mcuboot_primary_1: + address: 0x0 + size: 0x40000 + device: flash_ctrl + region: ram_flash +mcuboot_secondary: + address: 0x0 + size: 0xec000 + device: MX25R64 + region: external_flash +mcuboot_secondary_1: + address: 0xec000 + size: 0x40000 + device: MX25R64 + region: external_flash +external_flash: + address: 0x12C000 + size: 0x6D4000 + device: MX25R64 + region: external_flash +pcd_sram: + address: 0x20000000 + size: 0x2000 + region: sram_primary diff --git a/examples/window-app/nrfconnect/main/AppTask.cpp b/examples/window-app/nrfconnect/main/AppTask.cpp new file mode 100644 index 00000000000000..a03f5e28401a56 --- /dev/null +++ b/examples/window-app/nrfconnect/main/AppTask.cpp @@ -0,0 +1,514 @@ +/* + * 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 "LEDUtil.h" +#include "WindowCovering.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#if CONFIG_CHIP_OTA_REQUESTOR +#include "OTAUtil.h" +#endif + +#include +#include +#include + +#define FACTORY_RESET_TRIGGER_TIMEOUT 3000 +#define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000 +#define MOVEMENT_START_WINDOW_TIMEOUT 2000 +#define APP_EVENT_QUEUE_SIZE 10 +#define BUTTON_PUSH_EVENT 1 +#define BUTTON_RELEASE_EVENT 0 + +LOG_MODULE_DECLARE(app, CONFIG_MATTER_LOG_LEVEL); +K_MSGQ_DEFINE(sAppEventQueue, sizeof(AppEvent), APP_EVENT_QUEUE_SIZE, alignof(AppEvent)); + +static LEDWidget sStatusLED; +static UnusedLedsWrapper<1> sUnusedLeds{ { DK_LED4 } }; +static k_timer sFunctionTimer; +namespace LedConsts { +constexpr uint32_t kBlinkRate_ms{ 500 }; +namespace StatusLed { +namespace Unprovisioned { +constexpr uint32_t kOn_ms{ 100 }; +constexpr uint32_t kOff_ms{ kOn_ms }; +} // namespace Unprovisioned +namespace Provisioned { +constexpr uint32_t kOn_ms{ 50 }; +constexpr uint32_t kOff_ms{ 950 }; +} // namespace Provisioned + +} // namespace StatusLed +} // namespace LedConsts + +using namespace ::chip; +using namespace ::chip::Credentials; +using namespace ::chip::DeviceLayer; + +CHIP_ERROR AppTask::Init() +{ + // Initialize CHIP stack + LOG_INF("Init CHIP stack"); + + CHIP_ERROR err = chip::Platform::MemoryInit(); + if (err != CHIP_NO_ERROR) + { + LOG_ERR("Platform::MemoryInit() failed"); + return err; + } + + err = PlatformMgr().InitChipStack(); + if (err != CHIP_NO_ERROR) + { + LOG_ERR("PlatformMgr().InitChipStack() failed"); + return err; + } + + err = ThreadStackMgr().InitThreadStack(); + if (err != CHIP_NO_ERROR) + { + LOG_ERR("ThreadStackMgr().InitThreadStack() failed"); + return err; + } + +#ifdef CONFIG_OPENTHREAD_MTD_SED + err = ConnectivityMgr().SetThreadDeviceType(ConnectivityManager::kThreadDeviceType_SleepyEndDevice); +#else + err = ConnectivityMgr().SetThreadDeviceType(ConnectivityManager::kThreadDeviceType_MinimalEndDevice); +#endif + if (err != CHIP_NO_ERROR) + { + LOG_ERR("ConnectivityMgr().SetThreadDeviceType() failed"); + return err; + } + + // Initialize LEDs + LEDWidget::InitGpio(); + LEDWidget::SetStateUpdateCallback(LEDStateUpdateHandler); + + sStatusLED.Init(SYSTEM_STATE_LED); + + UpdateStatusLED(); + + // Initialize buttons + auto ret = dk_buttons_init(ButtonEventHandler); + if (ret) + { + LOG_ERR("dk_buttons_init() failed"); + return chip::System::MapErrorZephyr(ret); + } + + // Initialize function timer user data + k_timer_init(&sFunctionTimer, &AppTask::FunctionTimerTimeoutCallback, nullptr); + k_timer_user_data_set(&sFunctionTimer, this); + + // Initialize CHIP server + SetDeviceAttestationCredentialsProvider(Examples::GetExampleDACProvider()); + + static chip::CommonCaseDeviceServerInitParams initParams; + (void) initParams.InitializeStaticResourcesBeforeServerInit(); + ReturnErrorOnFailure(chip::Server::GetInstance().Init(initParams)); +#if CONFIG_CHIP_OTA_REQUESTOR + InitBasicOTARequestor(); +#endif + ConfigurationMgr().LogDeviceConfig(); + PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE)); + + // Add CHIP event handler and start CHIP thread. + // Note that all the initialization code should happen prior to this point to avoid data races + // between the main and the CHIP threads + PlatformMgr().AddEventHandler(ChipEventHandler, 0); + err = PlatformMgr().StartEventLoopTask(); + if (err != CHIP_NO_ERROR) + { + LOG_ERR("PlatformMgr().StartEventLoopTask() failed"); + return err; + } + + WindowCovering::Instance().PositionLEDUpdate(WindowCovering::MoveType::LIFT); + WindowCovering::Instance().PositionLEDUpdate(WindowCovering::MoveType::TILT); + + return err; +} + +CHIP_ERROR AppTask::StartApp() +{ + ReturnErrorOnFailure(Init()); + + AppEvent event{}; + + while (true) + { + k_msgq_get(&sAppEventQueue, &event, K_FOREVER); + DispatchEvent(&event); + } + + return CHIP_NO_ERROR; +} + +void AppTask::ButtonEventHandler(uint32_t aButtonState, uint32_t aHasChanged) +{ + AppEvent event; + event.Type = AppEvent::Type::Button; + + if (FUNCTION_BUTTON_MASK & aHasChanged) + { + event.ButtonEvent.PinNo = FUNCTION_BUTTON; + event.ButtonEvent.Action = (FUNCTION_BUTTON_MASK & aButtonState) ? BUTTON_PUSH_EVENT : BUTTON_RELEASE_EVENT; + event.Handler = FunctionHandler; + PostEvent(&event); + } + + if (BLE_ADVERTISEMENT_START_BUTTON_MASK & aButtonState & aHasChanged) + { + event.ButtonEvent.PinNo = BLE_ADVERTISEMENT_START_BUTTON; + event.ButtonEvent.Action = BUTTON_PUSH_EVENT; + event.Handler = StartBLEAdvertisementHandler; + PostEvent(&event); + } + + if (OPEN_BUTTON_MASK & aHasChanged) + { + event.ButtonEvent.PinNo = OPEN_BUTTON; + event.ButtonEvent.Action = (OPEN_BUTTON_MASK & aButtonState) ? BUTTON_PUSH_EVENT : BUTTON_RELEASE_EVENT; + event.Handler = OpenHandler; + PostEvent(&event); + } + + if (CLOSE_BUTTON_MASK & aHasChanged) + { + event.ButtonEvent.PinNo = CLOSE_BUTTON; + event.ButtonEvent.Action = (CLOSE_BUTTON_MASK & aButtonState) ? BUTTON_PUSH_EVENT : BUTTON_RELEASE_EVENT; + event.Handler = CloseHandler; + PostEvent(&event); + } +} + +void AppTask::FunctionTimerTimeoutCallback(k_timer * aTimer) +{ + if (!aTimer) + return; + + AppEvent event; + event.Type = AppEvent::Type::Timer; + event.TimerEvent.Context = k_timer_user_data_get(aTimer); + event.Handler = FunctionTimerEventHandler; + PostEvent(&event); +} + +void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) +{ + if (!aEvent) + return; + if (aEvent->Type != AppEvent::Type::Timer) + return; + + // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT, initiate factory reset + if (Instance().mFunctionTimerActive && Instance().mMode == OperatingMode::Normal) + { + LOG_INF("Factory Reset Triggered. Release button within %ums to cancel.", FACTORY_RESET_TRIGGER_TIMEOUT); + + // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to cancel, if required. + StartTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); + Instance().mMode = OperatingMode::FactoryReset; + +#ifdef CONFIG_STATE_LEDS + // Turn off all LEDs before starting blink to make sure blink is co-ordinated. + sStatusLED.Set(false); + sUnusedLeds.Set(false); + + sStatusLED.Blink(LedConsts::kBlinkRate_ms); + sUnusedLeds.Blink(LedConsts::kBlinkRate_ms); +#endif + } + else if (Instance().mFunctionTimerActive && Instance().mMode == OperatingMode::FactoryReset) + { + // Actually trigger Factory Reset + Instance().mMode = OperatingMode::Normal; + ConfigurationMgr().InitiateFactoryReset(); + } +} + +void AppTask::FunctionHandler(AppEvent * aEvent) +{ + if (!aEvent) + return; + if (aEvent->ButtonEvent.PinNo != FUNCTION_BUTTON) + return; + + // To initiate factory reset: press the FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT + FACTORY_RESET_CANCEL_WINDOW_TIMEOUT + // All LEDs start blinking after FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated. + // To cancel factory reset: release the FUNCTION_BUTTON once all LEDs start blinking within the + // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT + if (aEvent->ButtonEvent.Action == BUTTON_PUSH_EVENT) + { + if (!Instance().mFunctionTimerActive && Instance().mMode == OperatingMode::Normal) + { + StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT); + } + } + else + { + if (Instance().mFunctionTimerActive && Instance().mMode == OperatingMode::FactoryReset) + { + sUnusedLeds.Set(false); + + UpdateStatusLED(); + CancelTimer(); + + // Change the function to none selected since factory reset has been canceled. + Instance().mMode = OperatingMode::Normal; + + LOG_INF("Factory Reset has been Canceled"); + } + else if (Instance().mFunctionTimerActive) + { + CancelTimer(); + Instance().mMode = OperatingMode::Normal; + } + } +} + +void AppTask::StartBLEAdvertisementHandler(AppEvent *) +{ + if (Server::GetInstance().GetFabricTable().FabricCount() != 0) + { + LOG_INF("Matter service BLE advertising not started - device is already commissioned"); + return; + } + + if (ConnectivityMgr().IsBLEAdvertisingEnabled()) + { + LOG_INF("BLE advertising is already enabled"); + return; + } + + if (Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow() != CHIP_NO_ERROR) + { + LOG_ERR("OpenBasicCommissioningWindow() failed"); + } +} + +void AppTask::OpenHandler(AppEvent * aEvent) +{ + if (!aEvent) + return; + if (aEvent->ButtonEvent.PinNo != OPEN_BUTTON || Instance().mMode != OperatingMode::Normal) + return; + + if (aEvent->ButtonEvent.Action == BUTTON_PUSH_EVENT) + { + Instance().mOpenButtonIsPressed = true; + if (Instance().mCloseButtonIsPressed) + { + Instance().ToggleMoveType(); + } + } + else if (aEvent->ButtonEvent.Action == BUTTON_RELEASE_EVENT) + { + if (!Instance().mCloseButtonIsPressed) + { + if (!Instance().mMoveTypeRecentlyChanged) + { + WindowCovering::Instance().SetSingleStepTarget(OperationalState::MovingUpOrOpen); + } + else + { + Instance().mMoveTypeRecentlyChanged = false; + } + } + Instance().mOpenButtonIsPressed = false; + } +} + +void AppTask::CloseHandler(AppEvent * aEvent) +{ + if (!aEvent) + return; + if (aEvent->ButtonEvent.PinNo != CLOSE_BUTTON || Instance().mMode != OperatingMode::Normal) + return; + + if (aEvent->ButtonEvent.Action == BUTTON_PUSH_EVENT) + { + Instance().mCloseButtonIsPressed = true; + if (Instance().mOpenButtonIsPressed) + { + Instance().ToggleMoveType(); + } + } + else if (aEvent->ButtonEvent.Action == BUTTON_RELEASE_EVENT) + { + if (!Instance().mOpenButtonIsPressed) + { + if (!Instance().mMoveTypeRecentlyChanged) + { + WindowCovering::Instance().SetSingleStepTarget(OperationalState::MovingDownOrClose); + } + else + { + Instance().mMoveTypeRecentlyChanged = false; + } + } + Instance().mCloseButtonIsPressed = false; + } +} + +void AppTask::ToggleMoveType() +{ + if (WindowCovering::Instance().GetMoveType() == WindowCovering::MoveType::LIFT) + { + WindowCovering::Instance().SetMoveType(WindowCovering::MoveType::TILT); + LOG_INF("Window covering move: tilt"); + } + else + { + WindowCovering::Instance().SetMoveType(WindowCovering::MoveType::LIFT); + LOG_INF("Window covering move: lift"); + } + mMoveTypeRecentlyChanged = true; +} + +void AppTask::UpdateLedStateEventHandler(AppEvent * aEvent) +{ + if (!aEvent) + return; + if (aEvent->Type == AppEvent::Type::UpdateLedState) + { + aEvent->UpdateLedStateEvent.LedWidget->UpdateState(); + } +} + +void AppTask::LEDStateUpdateHandler(LEDWidget & aLedWidget) +{ + AppEvent event; + event.Type = AppEvent::Type::UpdateLedState; + event.Handler = UpdateLedStateEventHandler; + event.UpdateLedStateEvent.LedWidget = &aLedWidget; + PostEvent(&event); +} + +void AppTask::UpdateStatusLED() +{ +#ifdef CONFIG_STATE_LEDS + /* Update the status LED. + * + * If thread and service provisioned, keep the LED On constantly. + * + * If the system has ble connection(s) uptill the stage above, THEN blink the LED at an even + * rate of 100ms. + * + * Otherwise, blink the LED On for a very short time. */ + if (Instance().mIsThreadProvisioned && Instance().mIsThreadEnabled) + { + sStatusLED.Set(true); + } + else if (Instance().mHaveBLEConnections) + { + sStatusLED.Blink(LedConsts::StatusLed::Unprovisioned::kOn_ms, LedConsts::StatusLed::Unprovisioned::kOff_ms); + } + else + { + sStatusLED.Blink(LedConsts::StatusLed::Provisioned::kOn_ms, LedConsts::StatusLed::Provisioned::kOff_ms); + } +#endif +} + +void AppTask::ChipEventHandler(const ChipDeviceEvent * aEvent, intptr_t) +{ + if (!aEvent) + return; + switch (aEvent->Type) + { + case DeviceEventType::kCHIPoBLEAdvertisingChange: +#ifdef CONFIG_CHIP_NFC_COMMISSIONING + if (aEvent->CHIPoBLEAdvertisingChange.Result == kActivity_Started) + { + if (NFCMgr().IsTagEmulationStarted()) + { + LOG_INF("NFC Tag emulation is already started"); + } + else + { + ShareQRCodeOverNFC(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)); + } + } + else if (aEvent->CHIPoBLEAdvertisingChange.Result == kActivity_Stopped) + { + NFCMgr().StopTagEmulation(); + } +#endif + Instance().mHaveBLEConnections = ConnectivityMgr().NumBLEConnections() != 0; + UpdateStatusLED(); + break; + case DeviceEventType::kThreadStateChange: + Instance().mIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned(); + Instance().mIsThreadEnabled = ConnectivityMgr().IsThreadEnabled(); + UpdateStatusLED(); + break; + default: + break; + } +} + +void AppTask::CancelTimer() +{ + k_timer_stop(&sFunctionTimer); + Instance().mFunctionTimerActive = false; +} + +void AppTask::StartTimer(uint32_t aTimeoutInMs) +{ + k_timer_start(&sFunctionTimer, K_MSEC(aTimeoutInMs), K_NO_WAIT); + Instance().mFunctionTimerActive = true; +} + +void AppTask::PostEvent(AppEvent * aEvent) +{ + if (!aEvent) + return; + if (k_msgq_put(&sAppEventQueue, aEvent, K_NO_WAIT)) + { + LOG_INF("Failed to post event to app task event queue"); + } +} + +void AppTask::DispatchEvent(AppEvent * aEvent) +{ + if (!aEvent) + return; + if (aEvent->Handler) + { + aEvent->Handler(aEvent); + } + else + { + LOG_INF("Event received with no handler. Dropping event."); + } +} diff --git a/examples/window-app/nrfconnect/main/WindowCovering.cpp b/examples/window-app/nrfconnect/main/WindowCovering.cpp new file mode 100644 index 00000000000000..4da326ed16f0b4 --- /dev/null +++ b/examples/window-app/nrfconnect/main/WindowCovering.cpp @@ -0,0 +1,324 @@ +/* + * 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 "WindowCovering.h" +#include "AppConfig.h" +#include "PWMDevice.h" + +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(app, CONFIG_MATTER_LOG_LEVEL); + +using namespace ::chip::Credentials; +using namespace ::chip::DeviceLayer; +using namespace chip::app::Clusters::WindowCovering; + +static k_timer sLiftTimer; +static k_timer sTiltTimer; +static constexpr uint32_t sMoveTimeoutMs{ 200 }; + +WindowCovering::WindowCovering() +{ + mLiftLED.Init(LIFT_STATE_LED); + mTiltLED.Init(TILT_STATE_LED); + + if (mLiftIndicator.Init(LIFT_PWM_DEVICE, LIFT_PWM_CHANNEL, 0, 255) != 0) + { + LOG_ERR("Cannot initialize the lift indicator"); + } + if (mTiltIndicator.Init(TILT_PWM_DEVICE, TILT_PWM_CHANNEL, 0, 255) != 0) + { + LOG_ERR("Cannot initialize the tilt indicator"); + } + + k_timer_init(&sLiftTimer, MoveTimerTimeoutCallback, nullptr); + k_timer_init(&sTiltTimer, MoveTimerTimeoutCallback, nullptr); +} + +void WindowCovering::MoveTimerTimeoutCallback(k_timer * aTimer) +{ + if (!aTimer) + return; + + if (aTimer == &sLiftTimer) + { + chip::DeviceLayer::PlatformMgr().ScheduleWork(DriveCurrentLiftPosition); + } + else if (aTimer == &sTiltTimer) + { + chip::DeviceLayer::PlatformMgr().ScheduleWork(DriveCurrentTiltPosition); + } +} + +void WindowCovering::DriveCurrentLiftPosition(intptr_t) +{ + NPercent100ths current{}; + NPercent100ths target{}; + NPercent100ths positionToSet{}; + + VerifyOrReturn(Attributes::CurrentPositionLiftPercent100ths::Get(Endpoint(), current) == EMBER_ZCL_STATUS_SUCCESS); + VerifyOrReturn(Attributes::TargetPositionLiftPercent100ths::Get(Endpoint(), target) == EMBER_ZCL_STATUS_SUCCESS); + + UpdateOperationalStatus(MoveType::LIFT, ComputeOperationalState(target, current)); + + positionToSet.SetNonNull(CalculateSingleStep(MoveType::LIFT)); + LiftPositionSet(Endpoint(), positionToSet); + + // assume single move completed + Instance().mInLiftMove = false; + + VerifyOrReturn(Attributes::CurrentPositionLiftPercent100ths::Get(Endpoint(), current) == EMBER_ZCL_STATUS_SUCCESS); + + if (!TargetCompleted(MoveType::LIFT, current, target)) + { + // continue to move + StartTimer(MoveType::LIFT, sMoveTimeoutMs); + } + else + { + // the OperationalStatus should indicate no-lift-movement after the target is completed + UpdateOperationalStatus(MoveType::LIFT, ComputeOperationalState(target, current)); + } +} + +chip::Percent100ths WindowCovering::CalculateSingleStep(MoveType aMoveType) +{ + EmberAfStatus status{}; + chip::Percent100ths percent100ths{}; + NPercent100ths current{}; + OperationalState opState{}; + + OperationalStatus opStatus = OperationalStatusGet(Endpoint()); + + if (aMoveType == MoveType::LIFT) + { + status = Attributes::CurrentPositionLiftPercent100ths::Get(Endpoint(), current); + opState = opStatus.lift; + } + else if (aMoveType == MoveType::TILT) + { + status = Attributes::CurrentPositionTiltPercent100ths::Get(Endpoint(), current); + opState = opStatus.tilt; + } + + if ((status == EMBER_ZCL_STATUS_SUCCESS) && !current.IsNull()) + { + static constexpr auto sPercentDelta{ WC_PERCENT100THS_MAX_CLOSED / 20 }; + percent100ths = ComputePercent100thsStep(opState, current.Value(), sPercentDelta); + } + else + { + LOG_ERR("Cannot read the current lift position. Error: %d", static_cast(status)); + } + + return percent100ths; +} + +bool WindowCovering::TargetCompleted(MoveType aMoveType, NPercent100ths aCurrent, NPercent100ths aTarget) +{ + OperationalStatus currentOpStatus = OperationalStatusGet(Endpoint()); + OperationalState currentOpState = (aMoveType == MoveType::LIFT) ? currentOpStatus.lift : currentOpStatus.tilt; + + if (!aCurrent.IsNull() && !aTarget.IsNull()) + { + switch (currentOpState) + { + case OperationalState::MovingDownOrClose: + return (aCurrent.Value() >= aTarget.Value()); + case OperationalState::MovingUpOrOpen: + return (aCurrent.Value() <= aTarget.Value()); + default: + return true; + } + } + else + { + LOG_ERR("Invalid target/current positions"); + } + return false; +} + +void WindowCovering::StartTimer(MoveType aMoveType, uint32_t aTimeoutMs) +{ + if (aMoveType == MoveType::LIFT) + { + k_timer_start(&sLiftTimer, K_MSEC(sMoveTimeoutMs), K_NO_WAIT); + } + else if (aMoveType == MoveType::TILT) + { + k_timer_start(&sTiltTimer, K_MSEC(sMoveTimeoutMs), K_NO_WAIT); + } +} + +void WindowCovering::DriveCurrentTiltPosition(intptr_t) +{ + NPercent100ths current{}; + NPercent100ths target{}; + NPercent100ths positionToSet{}; + + VerifyOrReturn(Attributes::CurrentPositionTiltPercent100ths::Get(Endpoint(), current) == EMBER_ZCL_STATUS_SUCCESS); + VerifyOrReturn(Attributes::TargetPositionTiltPercent100ths::Get(Endpoint(), target) == EMBER_ZCL_STATUS_SUCCESS); + + UpdateOperationalStatus(MoveType::TILT, ComputeOperationalState(target, current)); + + positionToSet.SetNonNull(CalculateSingleStep(MoveType::TILT)); + TiltPositionSet(Endpoint(), positionToSet); + + // assume single move completed + Instance().mInTiltMove = false; + + VerifyOrReturn(Attributes::CurrentPositionTiltPercent100ths::Get(Endpoint(), current) == EMBER_ZCL_STATUS_SUCCESS); + + if (!TargetCompleted(MoveType::TILT, current, target)) + { + // continue to move + StartTimer(MoveType::TILT, sMoveTimeoutMs); + } + else + { + // the OperationalStatus should indicate no-tilt-movement after the target is completed + UpdateOperationalStatus(MoveType::TILT, ComputeOperationalState(target, current)); + } +} + +void WindowCovering::StartMove(MoveType aMoveType) +{ + switch (aMoveType) + { + case MoveType::LIFT: + if (!mInLiftMove) + { + mInLiftMove = true; + StartTimer(aMoveType, sMoveTimeoutMs); + } + break; + case MoveType::TILT: + if (!mInTiltMove) + { + mInTiltMove = true; + StartTimer(aMoveType, sMoveTimeoutMs); + } + break; + default: + break; + }; +} + +void WindowCovering::SetSingleStepTarget(OperationalState aDirection) +{ + UpdateOperationalStatus(mCurrentUIMoveType, aDirection); + SetTargetPosition(aDirection, CalculateSingleStep(mCurrentUIMoveType)); +} + +void WindowCovering::UpdateOperationalStatus(MoveType aMoveType, OperationalState aDirection) +{ + OperationalStatus currentOpStatus = OperationalStatusGet(Endpoint()); + + switch (aMoveType) + { + case MoveType::LIFT: + currentOpStatus.lift = aDirection; + break; + case MoveType::TILT: + currentOpStatus.tilt = aDirection; + break; + case MoveType::NONE: + break; + default: + break; + } + + OperationalStatusSetWithGlobalUpdated(Endpoint(), currentOpStatus); +} + +void WindowCovering::SetTargetPosition(OperationalState aDirection, chip::Percent100ths aPosition) +{ + EmberAfStatus status{}; + if (Instance().mCurrentUIMoveType == MoveType::LIFT) + { + status = Attributes::TargetPositionLiftPercent100ths::Set(Endpoint(), aPosition); + } + else if (Instance().mCurrentUIMoveType == MoveType::TILT) + { + status = Attributes::TargetPositionTiltPercent100ths::Set(Endpoint(), aPosition); + } + + if (status != EMBER_ZCL_STATUS_SUCCESS) + { + LOG_ERR("Cannot set the target position. Error: %d", static_cast(status)); + } +} + +void WindowCovering::PositionLEDUpdate(MoveType aMoveType) +{ + EmberAfStatus status{}; + NPercent100ths currentPosition{}; + + if (aMoveType == MoveType::LIFT) + { + status = Attributes::CurrentPositionLiftPercent100ths::Get(Endpoint(), currentPosition); + if (EMBER_ZCL_STATUS_SUCCESS == status && !currentPosition.IsNull()) + { + Instance().SetBrightness(MoveType::LIFT, currentPosition.Value()); + } + } + else if (aMoveType == MoveType::TILT) + { + status = Attributes::CurrentPositionTiltPercent100ths::Get(Endpoint(), currentPosition); + if (EMBER_ZCL_STATUS_SUCCESS == status && !currentPosition.IsNull()) + { + Instance().SetBrightness(MoveType::TILT, currentPosition.Value()); + } + } +} + +void WindowCovering::SetBrightness(MoveType aMoveType, uint16_t aPosition) +{ + uint8_t brightness = PositionToBrightness(aPosition, aMoveType); + if (aMoveType == MoveType::LIFT) + { + mLiftIndicator.InitiateAction(PWMDevice::LEVEL_ACTION, 0, &brightness); + } + else if (aMoveType == MoveType::TILT) + { + mTiltIndicator.InitiateAction(PWMDevice::LEVEL_ACTION, 0, &brightness); + } +} + +uint8_t WindowCovering::PositionToBrightness(uint16_t aPosition, MoveType aMoveType) +{ + AbsoluteLimits pwmLimits{}; + + if (aMoveType == MoveType::LIFT) + { + pwmLimits.open = mLiftIndicator.GetMinLevel(); + pwmLimits.closed = mLiftIndicator.GetMaxLevel(); + } + else if (aMoveType == MoveType::TILT) + { + pwmLimits.open = mTiltIndicator.GetMinLevel(); + pwmLimits.closed = mTiltIndicator.GetMaxLevel(); + } + + return Percent100thsToValue(pwmLimits, aPosition); +} diff --git a/examples/window-app/nrfconnect/main/ZclCallbacks.cpp b/examples/window-app/nrfconnect/main/ZclCallbacks.cpp new file mode 100644 index 00000000000000..f05dbdec8a9a19 --- /dev/null +++ b/examples/window-app/nrfconnect/main/ZclCallbacks.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2022 Project CHIP Authors + * + * 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. + */ + +/** + * @file + * This file implements the handler for data model messages. + */ + +#include "WindowCovering.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::chip; +using namespace ::chip::app::Clusters::WindowCovering; + +void MatterPostAttributeChangeCallback(const app::ConcreteAttributePath & attributePath, uint8_t mask, uint8_t type, uint16_t size, + uint8_t * value) +{ + switch (attributePath.mClusterId) + { + case app::Clusters::Identify::Id: + ChipLogProgress(Zcl, "Identify cluster ID: " ChipLogFormatMEI " Type: %u Value: %u, length: %u", + ChipLogValueMEI(attributePath.mAttributeId), type, *value, size); + break; + case app::Clusters::WindowCovering::Id: + ChipLogProgress(Zcl, "Window covering cluster ID: " ChipLogFormatMEI " Type: %u Value: %u, length: %u", + ChipLogValueMEI(attributePath.mAttributeId), type, *value, size); + break; + default: + break; + } +} + +/* Forwards all attributes changes */ +void MatterWindowCoveringClusterServerAttributeChangedCallback(const app::ConcreteAttributePath & attributePath) +{ + if (attributePath.mEndpointId == WindowCovering::Endpoint()) + { + switch (attributePath.mAttributeId) + { + case Attributes::TargetPositionLiftPercent100ths::Id: + WindowCovering::Instance().StartMove(WindowCovering::MoveType::LIFT); + break; + case Attributes::TargetPositionTiltPercent100ths::Id: + WindowCovering::Instance().StartMove(WindowCovering::MoveType::TILT); + break; + case Attributes::CurrentPositionLiftPercent100ths::Id: + WindowCovering::Instance().PositionLEDUpdate(WindowCovering::MoveType::LIFT); + break; + case Attributes::CurrentPositionTiltPercent100ths::Id: + WindowCovering::Instance().PositionLEDUpdate(WindowCovering::MoveType::TILT); + break; + }; + } +} diff --git a/examples/window-app/nrfconnect/main/include/AppConfig.h b/examples/window-app/nrfconnect/main/include/AppConfig.h new file mode 100644 index 00000000000000..46770401c0e2eb --- /dev/null +++ b/examples/window-app/nrfconnect/main/include/AppConfig.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#pragma once + +#define FUNCTION_BUTTON DK_BTN1 +#define FUNCTION_BUTTON_MASK DK_BTN1_MSK +#define OPEN_BUTTON DK_BTN2 +#define OPEN_BUTTON_MASK DK_BTN2_MSK +#define CLOSE_BUTTON DK_BTN3 +#define CLOSE_BUTTON_MASK DK_BTN3_MSK +#define BLE_ADVERTISEMENT_START_BUTTON DK_BTN4 +#define BLE_ADVERTISEMENT_START_BUTTON_MASK DK_BTN4_MSK + +#define SYSTEM_STATE_LED DK_LED1 +#define LIFT_STATE_LED DK_LED2 +#define TILT_STATE_LED DK_LED3 + +#define LIFT_PWM_DEVICE DEVICE_DT_GET(DT_PWMS_CTLR(DT_ALIAS(pwm_led1))) +#define LIFT_PWM_CHANNEL DT_PWMS_CHANNEL(DT_ALIAS(pwm_led1)) + +#define TILT_PWM_DEVICE DEVICE_DT_GET(DT_PWMS_CTLR(DT_ALIAS(pwm_led2))) +#define TILT_PWM_CHANNEL DT_PWMS_CHANNEL(DT_ALIAS(pwm_led2)) diff --git a/examples/window-app/nrfconnect/main/include/AppEvent.h b/examples/window-app/nrfconnect/main/include/AppEvent.h new file mode 100644 index 00000000000000..f6cac85b182b32 --- /dev/null +++ b/examples/window-app/nrfconnect/main/include/AppEvent.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#pragma once + +#include + +class LEDWidget; + +struct AppEvent +{ + using EventHandler = void (*)(AppEvent *); + + enum class Type : uint8_t + { + None, + Button, + Timer, + UpdateLedState, + }; + + Type Type{ Type::None }; + + union + { + struct + { + uint8_t PinNo; + uint8_t Action; + } ButtonEvent; + struct + { + void * Context; + } TimerEvent; + struct + { + LEDWidget * LedWidget; + } UpdateLedStateEvent; + }; + + EventHandler Handler; +}; diff --git a/examples/window-app/nrfconnect/main/include/AppTask.h b/examples/window-app/nrfconnect/main/include/AppTask.h new file mode 100644 index 00000000000000..eb0d2f9cf83bbe --- /dev/null +++ b/examples/window-app/nrfconnect/main/include/AppTask.h @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#pragma once + +#include "WindowCovering.h" +#include + +struct k_timer; +class AppEvent; +class LEDWidget; + +class AppTask +{ +public: + static AppTask & Instance(void) + { + static AppTask sAppTask; + return sAppTask; + }; + CHIP_ERROR StartApp(); + +private: + enum class OperatingMode : uint8_t + { + Normal, + FactoryReset, + MoveSelection, + Movement, + Invalid + }; + CHIP_ERROR Init(); + void DispatchEvent(AppEvent * aEvent); + void ToggleMoveType(); + + // statics needed to interact with zephyr C API + static void CancelTimer(); + static void StartTimer(uint32_t aTimeoutInMs); + static void FunctionTimerEventHandler(AppEvent * aEvent); + static void MovementTimerEventHandler(AppEvent * aEvent); + static void FunctionHandler(AppEvent * aEvent); + static void ButtonEventHandler(uint32_t aButtonsState, uint32_t aHasChanged); + static void TimerTimeoutCallback(k_timer * aTimer); + static void FunctionTimerTimeoutCallback(k_timer * aTimer); + static void PostEvent(AppEvent * aEvent); + static void UpdateStatusLED(); + static void LEDStateUpdateHandler(LEDWidget & aLedWidget); + static void UpdateLedStateEventHandler(AppEvent * aEvent); + static void StartBLEAdvertisementHandler(AppEvent * aEvent); + static void ChipEventHandler(const chip::DeviceLayer::ChipDeviceEvent * aEvent, intptr_t aArg); + static void OpenHandler(AppEvent * aEvent); + static void CloseHandler(AppEvent * aEvent); + + OperatingMode mMode{ OperatingMode::Normal }; + OperationalState mMoveType{ OperationalState::MovingUpOrOpen }; + bool mFunctionTimerActive{ false }; + bool mMovementTimerActive{ false }; + bool mIsThreadProvisioned{ false }; + bool mIsThreadEnabled{ false }; + bool mHaveBLEConnections{ false }; + bool mOpenButtonIsPressed{ false }; + bool mCloseButtonIsPressed{ false }; + bool mMoveTypeRecentlyChanged{ false }; +}; diff --git a/examples/window-app/nrfconnect/main/include/CHIPProjectConfig.h b/examples/window-app/nrfconnect/main/include/CHIPProjectConfig.h new file mode 100644 index 00000000000000..49000724fc6c47 --- /dev/null +++ b/examples/window-app/nrfconnect/main/include/CHIPProjectConfig.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/** + * @file + * Example project configuration file for CHIP. + * + * This is a place to put application or project-specific overrides + * to the default configuration values for general CHIP features. + * + */ + +#pragma once + +// Use a default pairing code if one hasn't been provisioned in flash. +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE 20202021 +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR 0xF00 + +#define CHIP_DEVICE_CONFIG_SED_SLOW_POLLING_INTERVAL 2000_ms32 diff --git a/examples/window-app/nrfconnect/main/include/WindowCovering.h b/examples/window-app/nrfconnect/main/include/WindowCovering.h new file mode 100644 index 00000000000000..9d4d17b548d089 --- /dev/null +++ b/examples/window-app/nrfconnect/main/include/WindowCovering.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#pragma once + +#include "LEDWidget.h" +#include "PWMDevice.h" + +#include + +#include + +using namespace chip::app::Clusters::WindowCovering; + +class WindowCovering +{ +public: + enum class MoveType : uint8_t + { + LIFT = 0, + TILT, + NONE + }; + + WindowCovering(); + static WindowCovering & Instance() + { + static WindowCovering sInstance; + return sInstance; + } + + void StartMove(MoveType aMoveType); + void SetSingleStepTarget(OperationalState aDirection); + void SetMoveType(MoveType aMoveType) { mCurrentUIMoveType = aMoveType; } + MoveType GetMoveType() { return mCurrentUIMoveType; } + void PositionLEDUpdate(MoveType aMoveType); + + static constexpr chip::EndpointId Endpoint() { return 1; }; + +private: + void SetBrightness(MoveType aMoveType, uint16_t aPosition); + void SetTargetPosition(OperationalState aDirection, chip::Percent100ths aPosition); + uint8_t PositionToBrightness(uint16_t aPosition, MoveType aMoveType); + + static void UpdateOperationalStatus(MoveType aMoveType, OperationalState aDirection); + static bool TargetCompleted(MoveType aMoveType, NPercent100ths aCurrent, NPercent100ths aTarget); + static void StartTimer(MoveType aMoveType, uint32_t aTimeoutMs); + static chip::Percent100ths CalculateSingleStep(MoveType aMoveType); + static void DriveCurrentLiftPosition(intptr_t); + static void DriveCurrentTiltPosition(intptr_t); + static void MoveTimerTimeoutCallback(k_timer * aTimer); + + MoveType mCurrentUIMoveType; + LEDWidget mLiftLED; + LEDWidget mTiltLED; + PWMDevice mLiftIndicator; + PWMDevice mTiltIndicator; + bool mInLiftMove{ false }; + bool mInTiltMove{ false }; +}; diff --git a/examples/window-app/nrfconnect/main/main.cpp b/examples/window-app/nrfconnect/main/main.cpp new file mode 100644 index 00000000000000..bb67947911cad1 --- /dev/null +++ b/examples/window-app/nrfconnect/main/main.cpp @@ -0,0 +1,32 @@ +/* + * 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 + +LOG_MODULE_REGISTER(app, CONFIG_MATTER_LOG_LEVEL); + +using namespace ::chip; + +int main() +{ + CHIP_ERROR err = AppTask::Instance().StartApp(); + + LOG_ERR("Exited with code %" CHIP_ERROR_FORMAT, err.Format()); + return err == CHIP_NO_ERROR ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/examples/window-app/nrfconnect/overlay-low_power.conf b/examples/window-app/nrfconnect/overlay-low_power.conf new file mode 100644 index 00000000000000..c962425f6ef937 --- /dev/null +++ b/examples/window-app/nrfconnect/overlay-low_power.conf @@ -0,0 +1,30 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# 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. +# + +# Enable MTD Sleepy End Device +CONFIG_CHIP_ENABLE_SLEEPY_END_DEVICE_SUPPORT=y + +# Disable UART console +CONFIG_SHELL=n +CONFIG_LOG=n +CONFIG_UART_CONSOLE=n +CONFIG_SERIAL=n + +# Suspend devices when the CPU goes to sleep +CONFIG_PM_DEVICE=y + +# Disable auxiliary state LEDs +CONFIG_STATE_LEDS=n diff --git a/examples/window-app/nrfconnect/prj.conf b/examples/window-app/nrfconnect/prj.conf new file mode 100644 index 00000000000000..1ffcaee2b430a4 --- /dev/null +++ b/examples/window-app/nrfconnect/prj.conf @@ -0,0 +1,51 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# 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. +# + +CONFIG_CHIP=y +CONFIG_STD_CPP14=y + +# This sample uses Kconfig.defaults to set options common for all +# samples. This file should contain only options specific for this sample +# or overrides of default values. + +# Add support for LEDs and buttons on Nordic development kits +CONFIG_DK_LIBRARY=y +CONFIG_PWM=y + +# OpenThread configs +CONFIG_OPENTHREAD_THREAD_VERSION_1_2=y +CONFIG_OPENTHREAD_NORDIC_LIBRARY_MTD=y +CONFIG_OPENTHREAD_MTD=y +CONFIG_OPENTHREAD_FTD=n + +# Default OpenThread network settings +CONFIG_OPENTHREAD_PANID=4660 +CONFIG_OPENTHREAD_CHANNEL=15 +CONFIG_OPENTHREAD_NETWORK_NAME="OpenThread" +CONFIG_OPENTHREAD_XPANID="11:11:11:11:22:22:22:22" + +# Bluetooth overrides +CONFIG_BT_DEVICE_NAME="MatterWinCov" + +# Additional configs for debbugging experience. +CONFIG_THREAD_NAME=y +CONFIG_MPU_STACK_GUARD=y +CONFIG_RESET_ON_FATAL_ERROR=n + +# CHIP configuration +CONFIG_CHIP_PROJECT_CONFIG="main/include/CHIPProjectConfig.h" +# 32784 == 0x8010 (example window-app) +CONFIG_CHIP_DEVICE_PRODUCT_ID=32784 diff --git a/examples/window-app/nrfconnect/prj_no_dfu.conf b/examples/window-app/nrfconnect/prj_no_dfu.conf new file mode 100644 index 00000000000000..1bb9717bae150f --- /dev/null +++ b/examples/window-app/nrfconnect/prj_no_dfu.conf @@ -0,0 +1,53 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# 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. +# + +CONFIG_CHIP=y +CONFIG_STD_CPP14=y + +# This sample uses Kconfig.defaults to set options common for all +# samples. This file should contain only options specific for this sample +# or overrides of default values. + +# Add support for LEDs and buttons on Nordic development kits +CONFIG_DK_LIBRARY=y + +# OpenThread configs +CONFIG_OPENTHREAD_THREAD_VERSION_1_2=y +CONFIG_OPENTHREAD_NORDIC_LIBRARY_MTD=y +CONFIG_OPENTHREAD_MTD=y +CONFIG_OPENTHREAD_FTD=n + +# Default OpenThread network settings +CONFIG_OPENTHREAD_PANID=4660 +CONFIG_OPENTHREAD_CHANNEL=15 +CONFIG_OPENTHREAD_NETWORK_NAME="OpenThread" +CONFIG_OPENTHREAD_XPANID="11:11:11:11:22:22:22:22" + +# Bluetooth overrides +CONFIG_BT_DEVICE_NAME="MatterWinCov" + +# Additional configs for debbugging experience. +CONFIG_THREAD_NAME=y +CONFIG_MPU_STACK_GUARD=y +CONFIG_RESET_ON_FATAL_ERROR=n + +# Disable Matter OTA DFU +CONFIG_CHIP_OTA_REQUESTOR=n + +# CHIP configuration +CONFIG_CHIP_PROJECT_CONFIG="main/include/CHIPProjectConfig.h" +# 32784 == 0x8010 (example window-app) +CONFIG_CHIP_DEVICE_PRODUCT_ID=32784 diff --git a/examples/window-app/nrfconnect/prj_release.conf b/examples/window-app/nrfconnect/prj_release.conf new file mode 100644 index 00000000000000..dcc71d6d218c46 --- /dev/null +++ b/examples/window-app/nrfconnect/prj_release.conf @@ -0,0 +1,60 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# +# 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. +# + +CONFIG_CHIP=y +CONFIG_STD_CPP14=y + +# This sample uses Kconfig.defaults to set options common for all +# samples. This file should contain only options specific for this sample +# or overrides of default values. + +# Add support for LEDs and buttons on Nordic development kits +CONFIG_DK_LIBRARY=y + +# OpenThread configs +CONFIG_OPENTHREAD_THREAD_VERSION_1_2=y +CONFIG_OPENTHREAD_NORDIC_LIBRARY_MTD=y +CONFIG_OPENTHREAD_MTD=y +CONFIG_OPENTHREAD_FTD=n + +# Default OpenThread network settings +CONFIG_OPENTHREAD_PANID=4660 +CONFIG_OPENTHREAD_CHANNEL=15 +CONFIG_OPENTHREAD_NETWORK_NAME="OpenThread" +CONFIG_OPENTHREAD_XPANID="11:11:11:11:22:22:22:22" + +# Bluetooth overrides +CONFIG_BT_DEVICE_NAME="MatterWinCov" + +# Enable system reset on fatal error +CONFIG_RESET_ON_FATAL_ERROR=y + +# CHIP configuration +CONFIG_CHIP_PROJECT_CONFIG="main/include/CHIPProjectConfig.h" +# 32784 == 0x8010 (example window-app) +CONFIG_CHIP_DEVICE_PRODUCT_ID=32784 + +# Disable all debug features +CONFIG_SHELL=n +CONFIG_OPENTHREAD_SHELL=n +CONFIG_CONSOLE=n +CONFIG_UART_CONSOLE=n +CONFIG_SERIAL=n +CONFIG_LOG=n +CONFIG_LOG_MODE_MINIMAL=n +CONFIG_ASSERT_VERBOSE=n +CONFIG_PRINTK=n +CONFIG_THREAD_NAME=n diff --git a/examples/window-app/nrfconnect/third_party/connectedhomeip b/examples/window-app/nrfconnect/third_party/connectedhomeip new file mode 120000 index 00000000000000..c866b86874994d --- /dev/null +++ b/examples/window-app/nrfconnect/third_party/connectedhomeip @@ -0,0 +1 @@ +../../../.. \ No newline at end of file diff --git a/src/app/clusters/window-covering-server/window-covering-server.cpp b/src/app/clusters/window-covering-server/window-covering-server.cpp index 33fec58d9b17cc..a4d5d4b7a92f78 100644 --- a/src/app/clusters/window-covering-server/window-covering-server.cpp +++ b/src/app/clusters/window-covering-server/window-covering-server.cpp @@ -93,11 +93,6 @@ static Percent100ths ValueToPercent100ths(AbsoluteLimits limits, uint16_t absolu return ConvertValue(limits.open, limits.closed, WC_PERCENT100THS_MIN_OPEN, WC_PERCENT100THS_MAX_CLOSED, absolute); } -static uint16_t Percent100thsToValue(AbsoluteLimits limits, Percent100ths relative) -{ - return ConvertValue(WC_PERCENT100THS_MIN_OPEN, WC_PERCENT100THS_MAX_CLOSED, limits.open, limits.closed, relative); -} - static OperationalState ValueToOperationalState(uint8_t value) { switch (value) @@ -378,6 +373,11 @@ bool IsPercent100thsValid(NPercent100ths percent100ths) return true; } +uint16_t Percent100thsToValue(AbsoluteLimits limits, Percent100ths relative) +{ + return ConvertValue(WC_PERCENT100THS_MIN_OPEN, WC_PERCENT100THS_MAX_CLOSED, limits.open, limits.closed, relative); +} + uint16_t LiftToPercent100ths(chip::EndpointId endpoint, uint16_t lift) { uint16_t openLimit = 0; diff --git a/src/app/clusters/window-covering-server/window-covering-server.h b/src/app/clusters/window-covering-server/window-covering-server.h index 5ec1e83a82fddc..cf785b61aa19e0 100644 --- a/src/app/clusters/window-covering-server/window-covering-server.h +++ b/src/app/clusters/window-covering-server/window-covering-server.h @@ -125,6 +125,8 @@ LimitStatus CheckLimitState(uint16_t position, AbsoluteLimits limits); bool IsPercent100thsValid(Percent100ths percent100ths); bool IsPercent100thsValid(NPercent100ths npercent100ths); +uint16_t Percent100thsToValue(AbsoluteLimits limits, Percent100ths relative); + uint16_t LiftToPercent100ths(chip::EndpointId endpoint, uint16_t lift); uint16_t Percent100thsToLift(chip::EndpointId endpoint, uint16_t percent100ths); void LiftPositionSet(chip::EndpointId endpoint, NPercent100ths position);