diff --git a/examples/light-switch-app/silabs/SiWx917/.gn b/examples/light-switch-app/silabs/SiWx917/.gn new file mode 100644 index 00000000000000..4c078f6acefdcc --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/.gn @@ -0,0 +1,29 @@ +# Copyright (c) 2020 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. + +import("//build_overrides/build.gni") + +# The location of the build configuration file. +buildconfig = "${build_root}/config/BUILDCONFIG.gn" + +# CHIP uses angle bracket includes. +check_system_includes = true + +default_args = { + target_cpu = "arm" + target_os = "freertos" + chip_openthread_ftd = true + + import("//args.gni") +} diff --git a/examples/light-switch-app/silabs/SiWx917/BUILD.gn b/examples/light-switch-app/silabs/SiWx917/BUILD.gn new file mode 100644 index 00000000000000..b7bf2c938faf45 --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/BUILD.gn @@ -0,0 +1,367 @@ +# Copyright (c) 2020 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") +import("//build_overrides/efr32_sdk.gni") +import("//build_overrides/pigweed.gni") + +import("${build_root}/config/defaults.gni") +import("${efr32_sdk_build_root}/SiWx917_sdk.gni") +import("${efr32_sdk_build_root}/efr32_executable.gni") + +import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") +import("${chip_root}/src/platform/device.gni") + +if (chip_enable_pw_rpc) { + import("//build_overrides/pigweed.gni") + import("$dir_pw_build/target_types.gni") +} + +assert(current_os == "freertos") + +efr32_project_dir = "${chip_root}/examples/light-switch-app/silabs/SiWx917" +examples_plat_dir = "${chip_root}/examples/platform/silabs/SiWx917" +examples_common_plat_dir = "${chip_root}/examples/platform/silabs" + +declare_args() { + # Dump memory usage at link time. + chip_print_memory_usage = false + + # Monitor & log memory usage at runtime. + enable_heap_monitoring = false + + # Enable Sleepy end device + enable_sleepy_device = false + + # OTA timeout in seconds + OTA_periodic_query_timeout = 86400 + + # Wifi related stuff - they are overridden by gn -args="use_wf200=true" + use_wf200 = false + use_rs911x = false + use_rs911x_sockets = false + sl_wfx_config_softap = false + sl_wfx_config_scan = true + + # Disable LCD on supported devices + disable_lcd = true + + # Argument to Disable IPv4 for wifi(rs911) + chip_enable_wifi_ipv4 = false + + # Argument to force enable WPA3 security + rs91x_wpa3_only = false + + #default WiFi SSID + chip_default_wifi_ssid = "" + + #default Wifi Password + chip_default_wifi_psk = "" +} + +declare_args() { + # Enables LCD Qr Code on supported devices + show_qr_code = !disable_lcd +} + +# qr code cannot be true if lcd is disabled +assert(!(disable_lcd && show_qr_code)) + +# Sanity check +assert(!(chip_enable_wifi && chip_enable_openthread)) +assert(!(use_rs911x && chip_enable_openthread)) +assert(!(use_wf200 && chip_enable_openthread)) +if (chip_enable_wifi) { + assert(use_rs911x || use_wf200) + enable_openthread_cli = false + import("${chip_root}/src/platform/silabs/SiWx917/wifi_args.gni") +} + +# ThunderBoards, Explorer Kit and MGM240L do not support LCD (No LCD) +if (silabs_board == "BRD4166A" || silabs_board == "BRD2601B" || + silabs_board == "BRD2703A" || silabs_board == "BRD4319A") { + show_qr_code = false + disable_lcd = true +} + +defines = [] + +# WiFi settings +if (chip_enable_wifi) { + if (chip_default_wifi_ssid != "") { + defines += [ + "CHIP_ONNETWORK_PAIRING=1", + "CHIP_WIFI_SSID=\"${chip_default_wifi_ssid}\"", + ] + } + if (chip_default_wifi_psk != "") { + assert(chip_default_wifi_ssid != "", + "ssid can't be null if psk is provided") + defines += [ "CHIP_WIFI_PSK=\"${chip_default_wifi_psk}\"" ] + } + wifi_sdk_dir = "${chip_root}/src/platform/silabs/SiWx917/wifi" + efr32_lwip_defs = [ "LWIP_NETIF_API=1" ] + if (lwip_ipv4) { + efr32_lwip_defs += [ + "LWIP_IPV4=1", + + # adds following options to provide + # them to .cpp source files + # flags ported from lwipopts file + # TODO: move lwipopts to one location + "LWIP_ARP=1", + "LWIP_ICMP=1", + "LWIP_IGMP=1", + "LWIP_DHCP=1", + "LWIP_DNS=0", + ] + } else { + efr32_lwip_defs += [ "LWIP_IPV4=0" ] + } + if (lwip_ipv6) { + efr32_lwip_defs += [ "LWIP_IPV6=1" ] + } else { + efr32_lwip_defs += [ "LWIP_IPV6=0" ] + } + + wiseconnect_sdk_root = "${chip_root}/third_party/silabs/wisemcu-wifi-bt-sdk" + import("${examples_plat_dir}/SiWx917/rs911x.gni") +} + +efr32_sdk("sdk") { + sources = [ + "${efr32_project_dir}/include/CHIPProjectConfig.h", + "${examples_plat_dir}/FreeRTOSConfig.h", + ] + + include_dirs = [ + "${chip_root}/src/platform/silabs/SiWx917", + "${efr32_project_dir}/include", + "${examples_plat_dir}", + "${chip_root}/src/lib", + "${examples_common_plat_dir}", + ] + + defines += [ + "BOARD_ID=${silabs_board}", + "OTA_PERIODIC_TIMEOUT=${OTA_periodic_query_timeout}", + ] + + if (chip_enable_pw_rpc) { + defines += [ + "HAL_VCOM_ENABLE=1", + "PW_RPC_ENABLED", + ] + } + + # WiFi Settings + if (chip_enable_wifi) { + if (use_rs911x) { + defines += rs911x_defs + include_dirs += rs911x_plat_incs + } else if (use_wf200) { + defines += wf200_defs + include_dirs += wf200_plat_incs + } + + if (use_rs911x_sockets) { + include_dirs += [ "${examples_plat_dir}/wifi/rsi-sockets" ] + defines += rs911x_sock_defs + } else { + # Using LWIP instead of the native TCP/IP stack + defines += efr32_lwip_defs + } + + if (sl_wfx_config_softap) { + defines += [ "SL_WFX_CONFIG_SOFTAP" ] + } + if (sl_wfx_config_scan) { + defines += [ "SL_WFX_CONFIG_SCAN" ] + } + } +} + +efr32_executable("light_switch_app") { + output_name = "chip-efr32-light-switch-example.out" + public_configs = [ "${efr32_sdk_build_root}:silabs_config" ] + include_dirs = [ "include" ] + defines = [] + + sources = [ + "${examples_common_plat_dir}/heap_4_silabs.c", + "${examples_plat_dir}/BaseApplication.cpp", + "${examples_plat_dir}/init_ccpPlatform.cpp", + "${examples_plat_dir}/matter_config.cpp", + "${examples_plat_dir}/siwx917_utils.cpp", + "src/AppTask.cpp", + "src/ZclCallbacks.cpp", + "src/binding-handler.cpp", + "src/main.cpp", + ] + + if (use_wstk_leds) { + #sources += [ "${examples_plat_dir}/LEDWidget.cpp" ] + } + + if (chip_enable_pw_rpc || chip_build_libshell || enable_openthread_cli || + use_wf200 || use_rs911x) { + #sources += [ "${examples_plat_dir}/uart.cpp" ] + } + + deps = [ + ":sdk", + "${chip_root}/examples/light-switch-app/light-switch-common", + "${chip_root}/examples/providers:device_info_provider", + "${chip_root}/src/lib", + "${chip_root}/src/setup_payload", + ] + + # Attestation Credentials + if (chip_build_platform_attestation_credentials_provider) { + deps += [ "${examples_plat_dir}:siwx917-attestation-credentials" ] + } + + # Factory Data Provider + if (use_efr32_factory_data_provider) { + deps += [ "${examples_plat_dir}:efr32-factory-data-provider" ] + } + + # WiFi Settings + if (chip_enable_wifi) { + if (use_rs911x) { + sources += rs911x_src_plat + + # All the stuff from wiseconnect + sources += rs911x_src_sapi + + # Apparently - the rsi library needs this (though we may not use use it) + sources += rs911x_src_sock + include_dirs += rs911x_inc_plat + + if (use_rs911x_sockets) { + # + # Using native sockets inside RS911x + # + include_dirs += rs911x_sock_inc + } else { + # + # We use LWIP - not built-in sockets + # + sources += rs911x_src_lwip + } + } else if (use_wf200) { + sources += wf200_plat_src + include_dirs += wf200_plat_incs + } + + if (chip_enable_wifi_ipv4) { + defines += [ "CHIP_DEVICE_CONFIG_ENABLE_IPV4" ] + } + + if (rs91x_wpa3_only) { + # TODO: Change this macro once WF200 support is provided + defines += [ "WIFI_ENABLE_SECURITY_WPA3=1" ] + } + } + + if (!disable_lcd) { + sources += [ + "${examples_plat_dir}/display/demo-ui.c", + "${examples_plat_dir}/display/lcd.cpp", + ] + include_dirs += [ "${examples_plat_dir}/display" ] + defines += [ + "DISPLAY_ENABLED", + "IS_DEMO_SWITCH=1", + ] + if (show_qr_code) { + defines += [ "QR_CODE_ENABLED" ] + deps += [ "${chip_root}/examples/common/QRCode" ] + } + } + + if (chip_enable_pw_rpc) { + defines += [ + "PW_RPC_ENABLED", + "PW_RPC_ATTRIBUTE_SERVICE=1", + "PW_RPC_BUTTON_SERVICE=1", + "PW_RPC_DESCRIPTOR_SERVICE=1", + "PW_RPC_DEVICE_SERVICE=1", + "PW_RPC_LIGHTING_SERVICE=1", + ] + + sources += [ + "${chip_root}/examples/common/pigweed/RpcService.cpp", + "${chip_root}/examples/common/pigweed/efr32/PigweedLoggerMutex.cpp", + "${examples_common_plat_dir}/PigweedLogger.cpp", + "${examples_common_plat_dir}/Rpc.cpp", + ] + + deps += [ + "$dir_pw_hdlc:rpc_channel_output", + "$dir_pw_stream:sys_io_stream", + "${chip_root}/config/efr32/lib/pw_rpc:pw_rpc", + "${chip_root}/examples/common/pigweed:attributes_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:button_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:descriptor_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:device_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:lighting_service.nanopb_rpc", + "${examples_plat_dir}/pw_sys_io:pw_sys_io_siwx917", + ] + + deps += pw_build_LINK_DEPS + + include_dirs += [ + "${chip_root}/examples/common", + "${chip_root}/examples/common/pigweed/efr32", + ] + } + + if (enable_heap_monitoring) { + sources += [ "${examples_common_plat_dir}/MemMonitoring.cpp" ] + defines += [ "HEAP_MONITORING" ] + } + + ldscript = "${examples_plat_dir}/ldscripts/${silabs_family}.ld" + + inputs = [ ldscript ] + + ldflags = [ "-T" + rebase_path(ldscript, root_build_dir) ] + + if (chip_print_memory_usage) { + ldflags += [ + "-Wl,--print-memory-usage", + "-fstack-usage", + ] + } + + # WiFi Settings + if (chip_enable_wifi) { + ldflags += [ + "-Wl,--defsym", + "-Wl,SILABS_WIFI=1", + ] + } + + output_dir = root_out_dir +} + +group("efr32") { + deps = [ ":light_switch_app" ] +} + +group("default") { + deps = [ ":efr32" ] +} diff --git a/examples/light-switch-app/silabs/SiWx917/README.md b/examples/light-switch-app/silabs/SiWx917/README.md new file mode 100644 index 00000000000000..3a72765e663796 --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/README.md @@ -0,0 +1,468 @@ +# Matter EFR32 Light Switch Example + +An example showing the use of CHIP on the Silicon Labs EFR32 MG12 and MG24. + +
+ +- [Matter EFR32 Light Switch Example](#matter-efr32-light-switch-example) + - [Introduction](#introduction) + - [Building](#building) + - [Linux](#linux) + - [Mac OS X](#mac-os-x) + - [Flashing the Application](#flashing-the-application) + - [Viewing Logging Output](#viewing-logging-output) + - [Running the Complete Example](#running-the-complete-example) + - [Notes](#notes) + - [On Border Router:](#on-border-router) + - [On PC(Linux):](#on-pclinux) + - [Running RPC console](#running-rpc-console) + - [Memory settings](#memory-settings) + - [OTA Software Update](#ota-software-update) + - [Building options](#building-options) + - [Disabling logging](#disabling-logging) + - [Debug build / release build](#debug-build--release-build) + - [Disabling LCD](#disabling-lcd) + - [KVS maximum entry count](#kvs-maximum-entry-count) + +
+ +> **NOTE:** Silicon Laboratories now maintains a public matter GitHub repo with +> frequent releases thoroughly tested and validated. Developers looking to +> develop matter products with silabs hardware are encouraged to use our latest +> release with added tools and documentation. +> [Silabs Matter Github](https://github.com/SiliconLabs/matter/releases) + + + +## Introduction + +The EFR32 light switch example provides a baseline demonstration of a on-off +light switch device, built using Matter and the Silicon Labs gecko SDK. It can +be controlled by a Chip controller over an Openthread or Wifi network. + +The EFR32 device can be commissioned over Bluetooth Low Energy where the device +and the Chip controller will exchange security information with the Rendez-vous +procedure. If using Thread, Thread Network credentials are then provided to the +EFR32 device which will then join the network. + +If the LCD is enabled, the LCD on the Silabs WSTK shows a QR Code containing the +needed commissioning information for the BLE connection and starting the +Rendez-vous procedure. + +The light switch example is intended to serve both as a means to explore the +workings of Matter as well as a template for creating real products based on the +Silicon Labs platform. + + + +## Building + +- Download the + [Simplicity Commander](https://www.silabs.com/mcu/programming-options) + command line tool, and ensure that `commander` is your shell search path. + (For Mac OS X, `commander` is located inside + `Commander.app/Contents/MacOS/`.) + +- Download and install a suitable ARM gcc tool chain: + [GNU Arm Embedded Toolchain 9-2019-q4-major](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads) + +- Install some additional tools(likely already present for CHIP developers): + +#### Linux + + $ sudo apt-get install git ninja-build + +#### Mac OS X + + $ brew install ninja + +- Supported hardware: + + - > For the latest supported hardware please refer to the + > [Hardware Requirements](https://github.com/SiliconLabs/matter/blob/latest/docs/silabs/general/HARDWARE_REQUIREMENTS.md) + > in the Silicon Labs Matter Github Repo + + MG12 boards: + + - BRD4161A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@19dBm + - BRD4162A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4163A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm, + 868MHz@19dBm + - BRD4164A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@19dBm + - BRD4166A / SLTB004A / Thunderboard Sense 2 / 2.4GHz@10dBm + - BRD4170A / SLWSTK6000B / Multiband Wireless Starter Kit / 2.4GHz@19dBm, + 915MHz@19dBm + - BRD4304A / SLWSTK6000B / MGM12P Module / 2.4GHz@19dBm + + MG21 boards: Currently not supported due to RAM limitation. + + - BRD4180A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + + MG24 boards : + + - BRD2601B / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD2703A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4186A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4186C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4187A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + - BRD4187C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + + MG12 boards: + + - BRD4161A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@19dBm + - BRD4162A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4163A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm, + 868MHz@19dBm + - BRD4164A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@19dBm + - BRD4166A / SLTB004A / Thunderboard Sense 2 / 2.4GHz@10dBm + - BRD4170A / SLWSTK6000B / Multiband Wireless Starter Kit / 2.4GHz@19dBm, + 915MHz@19dBm + - BRD4304A / SLWSTK6000B / MGM12P Module / 2.4GHz@19dBm + + MG21 boards: Currently not supported due to RAM limitation. + + - BRD4180A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + + MG24 boards : + + - BRD2601B / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD2703A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4186A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4186C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4187A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + - BRD4187C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + +* Build the example application: + + cd ~/connectedhomeip + ./scripts/examples/gn_efr32_example.sh ./examples/light-switch-app/efr32/ ./out/light-switch-app BRD4161A + +- To delete generated executable, libraries and object files use: + + $ cd ~/connectedhomeip + $ rm -rf ./out/ + + OR use GN/Ninja directly + + $ cd ~/connectedhomeip/examples/light-switch-app/efr32 + $ git submodule update --init + $ source third_party/connectedhomeip/scripts/activate.sh + $ export EFR32_BOARD=BRD4161A + $ gn gen out/debug + $ ninja -C out/debug + +- To delete generated executable, libraries and object files use: + + $ cd ~/connectedhomeip/examples/light-switch-app/efr32 + $ rm -rf out/ + +* Build the example with Matter shell + + ./scripts/examples/gn_efr32_example.sh examples/light-switch-app/efr32/ out/light-switch-app BRD4161A chip_build_libshell=true + +* Build the example as Sleepy End Device (SED) + + $ ./scripts/examples/gn_efr32_example.sh ./examples/light-switch-app/efr32/ ./out/light-switch-app_SED BRD4161A --sed + + or use gn as previously mentioned but adding the following arguments: + + $ gn gen out/debug '--args=silabs_board="BRD4161A" enable_sleepy_device=true chip_openthread_ftd=false chip_build_libshell=true' + +* Build the example with pigweed RCP + + $ ./scripts/examples/gn_efr32_example.sh examples/light-switch-app/efr32/ out/light-switch-app_rpc BRD4161A 'import("//with_pw_rpc.gni")' + + or use GN/Ninja Directly + + $ cd ~/connectedhomeip/examples/light-switch-app/efr32 + $ git submodule update --init + $ source third_party/connectedhomeip/scripts/activate.sh + $ export EFR32_BOARD=BRD4161A + $ gn gen out/debug --args='import("//with_pw_rpc.gni")' + $ ninja -C out/debug + + [Running Pigweed RPC console](#running-pigweed-rpc-console) + +For more build options, help is provided when running the build script without +arguments + + ./scripts/examples/gn_efr32_example.sh + + + +## Flashing the Application + +- On the command line: + + $ cd ~/connectedhomeip/examples/lighting-app/efr32 + $ python3 out/debug/chip-efr32-light-switch-example.flash.py + +- Or with the Ozone debugger, just load the .out file. + + + +## Viewing Logging Output + +The example application is built to use the SEGGER Real Time Transfer (RTT) +facility for log output. RTT is a feature built-in to the J-Link Interface MCU +on the WSTK development board. It allows bi-directional communication with an +embedded application without the need for a dedicated UART. + +Using the RTT facility requires downloading and installing the _SEGGER J-Link +Software and Documentation Pack_ +([web site](https://www.segger.com/downloads/jlink#J-LinkSoftwareAndDocumentationPack)). + +Alternatively, SEGGER Ozone J-Link debugger can be used to view RTT logs too +after flashing the .out file. + +- Download the J-Link installer by navigating to the appropriate URL and + agreeing to the license agreement. + +- [JLink_Linux_x86_64.deb](https://www.segger.com/downloads/jlink/JLink_Linux_x86_64.deb) +- [JLink_MacOSX.pkg](https://www.segger.com/downloads/jlink/JLink_MacOSX.pkg) + +* Install the J-Link software + + $ cd ~/Downloads + $ sudo dpkg -i JLink_Linux_V*_x86_64.deb + +* In Linux, grant the logged in user the ability to talk to the development + hardware via the linux tty device (/dev/ttyACMx) by adding them to the + dialout group. + + $ sudo usermod -a -G dialout ${USER} + +Once the above is complete, log output can be viewed using the JLinkExe tool in +combination with JLinkRTTClient as follows: + +- Run the JLinkExe tool with arguments to autoconnect to the WSTK board: + + For MG12 use: + + $ JLinkExe -device EFR32MG12PXXXF1024 -if JTAG -speed 4000 -autoconnect 1 + + For MG21 use: + + $ JLinkExe -device EFR32MG21AXXXF1024 -if SWD -speed 4000 -autoconnect 1 + +- In a second terminal, run the JLinkRTTClient to view logs: + + $ JLinkRTTClient + + + +## Running the Complete Example + +- It is assumed here that you already have an OpenThread border router + configured and running. If not see the following guide + [Openthread_border_router](https://github.com/project-chip/connectedhomeip/blob/master/docs/guides/openthread_border_router_pi.md) + for more information on how to setup a border router on a raspberryPi. + + Take note that the RCP code is available directly through + [Simplicity Studio 5](https://www.silabs.com/products/development-tools/software/simplicity-studio/simplicity-studio-5) + under File->New->Project Wizard->Examples->Thread : ot-rcp + +- For this example to work, it is necessary to have a second efr32 device + running the + [lighting app example](https://github.com/project-chip/connectedhomeip/blob/master/examples/lighting-app/efr32/README.md) + commissioned on the same openthread network + +- User interface : **LCD** The LCD on Silabs WSTK shows a QR Code. This QR + Code is be scanned by the CHIP Tool app For the Rendez-vous procedure over + BLE + + * On devices that do not have or support the LCD Display like the BRD4166A Thunderboard Sense 2, + a URL can be found in the RTT logs. + + [SVR] Copy/paste the below URL in a browser to see the QR Code: + [SVR] https://project-chip.github.io/connectedhomeip/qrcode.html?data=CH%3AI34NM%20-00%200C9SS0 + + **LED 0** 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. + + **Push Button 0** + + - _Press and Release_ : Start, or restart, BLE advertisement in fast mode. It will advertise in this mode + for 30 seconds. The device will then switch to a slower interval advertisement. + After 15 minutes, the advertisement stops. + + - _Pressed and hold for 6 s_ : Initiates the factory reset of the device. + Releasing the button within the 6-second window cancels the factory reset + procedure. **LEDs** blink in unison when the factory reset procedure is + initiated. + + **Push Button 1** + + - Sends a Toggle command to bound light app + + **Matter shell** + + **_OnOff Cluster_** + + - 'switch onoff on' : Sends unicast On command to bound device + - 'switch onoff off' : Sends unicast Off command to bound device + - 'switch onoff toggle' : Sends unicast Toggle command to bound device + + - 'switch groups onoff on' : Sends On group command to bound group + - 'switch groups onoff off' : Sends On group command to bound group + - 'switch groups onoff toggle' : Sends On group command to bound group + + **_Binding Cluster_** + + - 'switch binding unicast ' : Creates a unicast binding + - 'switch binding group ' : Creates a group binding + +* You can provision and control the Chip device using the python controller, + [CHIPTool](https://github.com/project-chip/connectedhomeip/blob/master/examples/chip-tool/README.md) + standalone, Android or iOS app + + Here is an example with the CHIPTool for unicast commands only: + + ``` + chip-tool pairing ble-thread 1 hex: 20202021 3840 + + chip-tool accesscontrol write acl '[{"fabricIndex": 1, "privilege": 5, "authMode": 2, "subjects": [], "targets": null }{"fabricIndex": 1, "privilege": 3, "authMode": 2, "subjects": [1], "targets": null }]' 0 + + chip-tool binding write binding '[{"fabricIndex": 1, "node": , "endpoint": 1, "cluster":6}]' 1 1 + ``` + + Here is an example with the CHIPTool for groups commands only: + + ``` + chip-tool pairing ble-thread 1 hex: 20202021 3840 + + chip-tool tests TestGroupDemoConfig --nodeId 1 + + chip-tool tests TestGroupDemoConfig --nodeId + + chip-tool binding write binding '[{"fabricIndex": 1, "group": 257}]' 1 1 + ``` + + To run the example with unicast and groups commands, run the group + configuration commands and replace the last one with binding this command + + ``` + chip-tool binding write binding '[{"fabricIndex": 1, "group": 257},{"fabricIndex": 1, "node": , "endpoint": 1, "cluster":6} ]' 1 1 + ``` + + To acquire the chip-tool node id, read the acl table right after + commissioning + + ``` + ./connectedhomeip/out/chip-tool/chip-tool accesscontrol read acl 0 + ``` + +### Notes + +- Depending on your network settings your router might not provide native ipv6 + addresses to your devices (Border router / PC). If this is the case, you + need to add a static ipv6 addresses on both device and then an ipv6 route to + the border router on your PC + +#### On Border Router: + +`$ sudo ip addr add dev 2002::2/64` + +#### On PC(Linux): + +`$ sudo ip addr add dev 2002::1/64` + +#Add Ipv6 route on PC(Linux) \$ sudo ip route add /64 +via 2002::2 + + + +## Running RPC console + +- As part of building the example with RPCs enabled the chip_rpc python + interactive console is installed into your venv. The python wheel files are + also created in the output folder: out/debug/chip_rpc_console_wheels. To + install the wheel files without rebuilding: + + `pip3 install out/debug/chip_rpc_console_wheels/*.whl` + +- To use the chip-rpc console after it has been installed run: + + `chip-console --device /dev/tty. -b 115200 -o //pw_log.out` + +- Then you can simulate a button press or release using the following command + where : idx = 0 or 1 for Button PB0 or PB1 action = 0 for PRESSED, 1 for + RELEASE Test toggling the LED with + + `rpcs.chip.rpc.Button.Event(idx=1, pushed=True)` + +- You can also Get and Set the light directly using the RPCs: + + `rpcs.chip.rpc.Lighting.Get()` + + `rpcs.chip.rpc.Lighting.Set(on=True, level=128, color=protos.chip.rpc.LightingColor(hue=5, saturation=5))` + +## Memory settings + +While most of the RAM usage in CHIP is static, allowing easier debugging and +optimization with symbols analysis, we still need some HEAP for the crypto and +OpenThread. Size of the HEAP can be modified by changing the value of the +`configTOTAL_HEAP_SIZE` define inside of the FreeRTOSConfig.h file of this +example. Please take note that a HEAP size smaller than 13k can and will cause a +Mbedtls failure during the BLE rendez-vous or CASE session + +To track memory usage you can set `enable_heap_monitoring = true` either in the +BUILD.gn file or pass it as a build argument to gn. This will print on the RTT +console the RAM usage of each individual task and the number of Memory +allocation and Free. While this is not extensive monitoring you're welcome to +modify `examples/platform/efr32/MemMonitoring.cpp` to add your own memory +tracking code inside the `trackAlloc` and `trackFree` function + +## OTA Software Update + +For the description of Software Update process with EFR32 example applications +see +[EFR32 OTA Software Update](../../../docs/guides/silabs_efr32_software_update.md) + +## Building options + +All of Silabs's examples within the Matter repo have all the features enabled by +default, as to provide the best end user experience. However some of those +features can easily be toggled on or off. Here is a short list of options : + +### Disabling logging + +chip_progress_logging, chip_detail_logging, chip_automation_logging + + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32 ./out/lighting-app BRD4164A "chip_detail_logging=false chip_automation_logging=false chip_progress_logging=false" + +### Debug build / release build + +is_debug + + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32 ./out/lighting-app BRD4164A "is_debug=false" + +### Disabling LCD + +show_qr_code + + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32 ./out/lighting-app BRD4164A "show_qr_code=false" + +### KVS maximum entry count + +kvs_max_entries + + Set the maximum Kvs entries that can be stored in NVM (Default 75) + Thresholds: 30 <= kvs_max_entries <= 255 + + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32 ./out/lighting-app BRD4164A kvs_max_entries=50 diff --git a/examples/light-switch-app/silabs/SiWx917/args.gni b/examples/light-switch-app/silabs/SiWx917/args.gni new file mode 100644 index 00000000000000..421090dbc97aae --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/args.gni @@ -0,0 +1,24 @@ +# Copyright (c) 2020 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. + +import("//build_overrides/chip.gni") +import("${chip_root}/config/standalone/args.gni") +import("${chip_root}/src/platform/silabs/SiWx917/args.gni") + +efr32_sdk_target = get_label_info(":sdk", "label_no_toolchain") + +chip_enable_ota_requestor = true +chip_enable_openthread = true +openthread_external_platform = + "${chip_root}/third_party/openthread/platforms/efr32:libopenthread-efr32" diff --git a/examples/light-switch-app/silabs/SiWx917/build_for_wifi_args.gni b/examples/light-switch-app/silabs/SiWx917/build_for_wifi_args.gni new file mode 100644 index 00000000000000..b1a0be42ca9351 --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/build_for_wifi_args.gni @@ -0,0 +1,21 @@ +# Copyright (c) 2020 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. +import("//build_overrides/chip.gni") +import("${chip_root}/config/standalone/args.gni") + +efr32_sdk_target = get_label_info(":sdk", "label_no_toolchain") +chip_enable_openthread = false +import("${chip_root}/src/platform/silabs/SiWx917/wifi_args.gni") + +chip_enable_ota_requestor = true diff --git a/examples/light-switch-app/silabs/SiWx917/build_for_wifi_gnfile.gn b/examples/light-switch-app/silabs/SiWx917/build_for_wifi_gnfile.gn new file mode 100644 index 00000000000000..d391814190d09f --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/build_for_wifi_gnfile.gn @@ -0,0 +1,28 @@ +# Copyright (c) 2020 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. + +import("//build_overrides/build.gni") + +# The location of the build configuration file. +buildconfig = "${build_root}/config/BUILDCONFIG.gn" + +# CHIP uses angle bracket includes. +check_system_includes = true + +default_args = { + target_cpu = "arm" + target_os = "freertos" + chip_enable_wifi = true + import("//build_for_wifi_args.gni") +} diff --git a/examples/light-switch-app/silabs/SiWx917/build_overrides b/examples/light-switch-app/silabs/SiWx917/build_overrides new file mode 120000 index 00000000000000..995884e6163eb5 --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/build_overrides @@ -0,0 +1 @@ +../../../build_overrides \ No newline at end of file diff --git a/examples/light-switch-app/silabs/SiWx917/include/AppConfig.h b/examples/light-switch-app/silabs/SiWx917/include/AppConfig.h new file mode 100644 index 00000000000000..a936fe1d8abaeb --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/include/AppConfig.h @@ -0,0 +1,46 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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 + +// ---- Lighting Example App Config ---- + +#define APP_TASK_NAME "Lit" + +// Time it takes in ms for the simulated actuator to move from one +// state to another. +#define ACTUATOR_MOVEMENT_PERIOS_MS 10 + +// EFR Logging +#ifdef __cplusplus +extern "C" { +#endif + +void silabsInitLog(void); + +void efr32Log(const char * aFormat, ...); +#define SILABS_LOG(...) efr32Log(__VA_ARGS__); +void appError(int err); + +#ifdef __cplusplus +} + +#include +void appError(CHIP_ERROR error); +#endif diff --git a/examples/light-switch-app/silabs/SiWx917/include/AppEvent.h b/examples/light-switch-app/silabs/SiWx917/include/AppEvent.h new file mode 100644 index 00000000000000..7a19b719edad25 --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/include/AppEvent.h @@ -0,0 +1,55 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2018 Nest Labs, Inc. + * 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 + +struct AppEvent; +typedef void (*EventHandler)(AppEvent *); + +struct AppEvent +{ + enum AppEventTypes + { + kEventType_Button = 0, + kEventType_Timer, + kEventType_Light, + kEventType_Install, + }; + + uint16_t Type; + + union + { + struct + { + uint8_t Action; + } ButtonEvent; + struct + { + void * Context; + } TimerEvent; + struct + { + uint8_t Action; + int32_t Actor; + } LightEvent; + }; + + EventHandler Handler; +}; diff --git a/examples/light-switch-app/silabs/SiWx917/include/AppTask.h b/examples/light-switch-app/silabs/SiWx917/include/AppTask.h new file mode 100644 index 00000000000000..29a0078d1472f3 --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/include/AppTask.h @@ -0,0 +1,164 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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 + +/********************************************************** + * Includes + *********************************************************/ + +#include +#include + +#include "AppEvent.h" +#include "BaseApplication.h" +#include "FreeRTOS.h" +#include "timers.h" // provides FreeRTOS timer support +#include +#include +#include +#include + +/********************************************************** + * Defines + *********************************************************/ + +#define SL_SIMPLE_BUTTON_MODE_POLL 0U ///< BUTTON input capture using polling +#define SL_SIMPLE_BUTTON_MODE_POLL_AND_DEBOUNCE 1U ///< BUTTON input capture using polling and debouncing +#define SL_SIMPLE_BUTTON_MODE_INTERRUPT 2U ///< BUTTON input capture using interrupt + +#define SL_SIMPLE_BUTTON_DISABLED 2U ///< BUTTON state is disabled +#define SL_SIMPLE_BUTTON_PRESSED 1U ///< BUTTON state is pressed +#define SL_SIMPLE_BUTTON_RELEASED 0U ///< BUTTON state is released + +typedef uint8_t sl_button_mode_t; ///< BUTTON mode +typedef uint8_t sl_button_state_t; ///< BUTTON state +typedef struct sl_button sl_button_t; + +/// A BUTTON instance +typedef struct sl_button +{ + void * context; ///< The context for this BUTTON instance + void (*init)(const sl_button_t * handle); ///< Member function to initialize BUTTON instance + void (*poll)(const sl_button_t * handle); ///< Member function to poll BUTTON + void (*enable)(const sl_button_t * handle); ///< Member function to enable BUTTON + void (*disable)(const sl_button_t * handle); ///< Member function to disable BUTTON + sl_button_state_t (*get_state)(const sl_button_t * handle); ///< Member function to retrieve BUTTON state +} sl_button; + +const sl_button_t sl_button_btn0 = { + .context = NULL, + .init = NULL, + .poll = NULL, + .enable = NULL, + .disable = NULL, + .get_state = NULL, +}; +#define APP_FUNCTION_BUTTON &sl_button_btn0 + +const sl_button_t sl_button_btn1 = { + .context = NULL, + .init = NULL, + .poll = NULL, + .enable = NULL, + .disable = NULL, + .get_state = NULL, +}; +#define APP_LIGHT_SWITCH &sl_button_btn1 + +// Application-defined error codes in the CHIP_ERROR space. +#define APP_ERROR_EVENT_QUEUE_FAILED CHIP_APPLICATION_ERROR(0x01) +#define APP_ERROR_CREATE_TASK_FAILED CHIP_APPLICATION_ERROR(0x02) +#define APP_ERROR_UNHANDLED_EVENT CHIP_APPLICATION_ERROR(0x03) +#define APP_ERROR_CREATE_TIMER_FAILED CHIP_APPLICATION_ERROR(0x04) +#define APP_ERROR_START_TIMER_FAILED CHIP_APPLICATION_ERROR(0x05) +#define APP_ERROR_STOP_TIMER_FAILED CHIP_APPLICATION_ERROR(0x06) + +/********************************************************** + * AppTask Declaration + *********************************************************/ + +class AppTask : public BaseApplication +{ + +public: + AppTask() = default; + + static AppTask & GetAppTask() { return sAppTask; } + + /** + * @brief AppTask task main loop function + * + * @param pvParameter FreeRTOS task parameter + */ + static void AppTaskMain(void * pvParameter); + + CHIP_ERROR StartAppTask(); + + /** + * @brief Event handler when a button is pressed + * Function posts an event for button processing + * + * @param buttonHandle APP_LIGHT_SWITCH or APP_FUNCTION_BUTTON + * @param btnAction button action - SL_SIMPLE_BUTTON_PRESSED, + * SL_SIMPLE_BUTTON_RELEASED or SL_SIMPLE_BUTTON_DISABLED + */ + void ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAction); + + /** + * @brief Callback called by the identify-server when an identify command is received + * + * @param identify identify structure the command applies on + */ + static void OnIdentifyStart(Identify * identify); + + /** + * @brief Callback called by the identify-server when an identify command is stopped or finished + * + * @param identify identify structure the command applies on + */ + static void OnIdentifyStop(Identify * identify); + +private: + static AppTask sAppTask; + + /** + * @brief AppTask initialisation function + * + * @return CHIP_ERROR + */ + CHIP_ERROR Init(); + + /** + * @brief PB0 Button event processing function + * Press and hold will trigger a factory reset timer start + * Press and release will restart BLEAdvertising if not commisionned + * + * @param aEvent button event being processed + */ + static void ButtonHandler(AppEvent * aEvent); + + /** + * @brief PB1 Button event processing function + * Function triggers a switch action sent to the CHIP task + * + * @param aEvent button event being processed + */ + static void SwitchActionEventHandler(AppEvent * aEvent); +}; diff --git a/examples/light-switch-app/silabs/SiWx917/include/CHIPProjectConfig.h b/examples/light-switch-app/silabs/SiWx917/include/CHIPProjectConfig.h new file mode 100644 index 00000000000000..1b74c802fd4f09 --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/include/CHIPProjectConfig.h @@ -0,0 +1,124 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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. +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE 20202021 +#endif + +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR 0xF00 +#endif + +// For convenience, Chip Security Test Mode can be enabled and the +// requirement for authentication in various protocols can be disabled. +// +// WARNING: These options make it possible to circumvent basic Chip security functionality, +// including message encryption. Because of this they MUST NEVER BE ENABLED IN PRODUCTION BUILDS. +// +#define CHIP_CONFIG_SECURITY_TEST_MODE 0 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID + * + * 0xFFF1: Test vendor + */ +#define CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID 0xFFF1 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID + * + * 0x8005: example lighting app + */ +#define CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID 0x8004 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_HARDWARE_VERSION + * + * The hardware version number assigned to device or product by the device vendor. This + * number is scoped to the device product id, and typically corresponds to a revision of the + * physical device, a change to its packaging, and/or a change to its marketing presentation. + * This value is generally *not* incremented for device software versions. + */ +#define CHIP_DEVICE_CONFIG_DEVICE_HARDWARE_VERSION 1 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION + * + * A uint32_t identifying the software version running on the device. + */ +/* The SoftwareVersion attribute of the Basic cluster. */ +#ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x0001 +#endif + +/** + * CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE + * + * Enable support for Chip-over-BLE (CHIPoBLE). + */ +#define CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE 1 + +/** + * CHIP_DEVICE_CONFIG_TEST_SERIAL_NUMBER + * + * Enables the use of a hard-coded default serial number if none + * is found in Chip NV storage. + */ +#define CHIP_DEVICE_CONFIG_TEST_SERIAL_NUMBER "TEST_SN" + +/** + * CHIP_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS + * + * Enable recording UTC timestamps. + */ +#define CHIP_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS 1 + +/** + * CHIP_DEVICE_CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE + * + * A size, in bytes, of the individual debug event logging buffer. + */ +#define CHIP_DEVICE_CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE (512) + +/** + * @def CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL + * + * @brief + * Active retransmit interval, or time to wait before retransmission after + * subsequent failures in milliseconds. + * + * This is the default value, that might be adjusted by end device depending on its + * needs (e.g. sleeping period) using Service Discovery TXT record CRA key. + * + */ +#define CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL (2000_ms32) + +#define CHIP_DEVICE_CONFIG_ENABLE_EXTENDED_DISCOVERY 1 diff --git a/examples/light-switch-app/silabs/SiWx917/include/binding-handler.h b/examples/light-switch-app/silabs/SiWx917/include/binding-handler.h new file mode 100644 index 00000000000000..aed08be25eb5bc --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/include/binding-handler.h @@ -0,0 +1,33 @@ +/* + * + * Copyright (c) 2020 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. + */ +#pragma once + +#include "app-common/zap-generated/ids/Clusters.h" +#include "app-common/zap-generated/ids/Commands.h" +#include "lib/core/CHIPError.h" + +CHIP_ERROR InitBindingHandler(); +void SwitchWorkerFunction(intptr_t context); +void BindingWorkerFunction(intptr_t context); + +struct BindingCommandData +{ + chip::EndpointId localEndpointId = 1; + chip::CommandId commandId; + chip::ClusterId clusterId; + bool isGroup = false; +}; diff --git a/examples/light-switch-app/silabs/SiWx917/src/AppTask.cpp b/examples/light-switch-app/silabs/SiWx917/src/AppTask.cpp new file mode 100644 index 00000000000000..9cba97f27dafc2 --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/src/AppTask.cpp @@ -0,0 +1,263 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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. + */ + +/********************************************************** + * Includes + *********************************************************/ + +#include "AppTask.h" +#include "AppConfig.h" +#include "AppEvent.h" +#include "binding-handler.h" + +#ifdef ENABLE_WSTK_LEDS +#include "LEDWidget.h" +#endif // ENABLE_WSTK_LEDS + +#ifdef DISPLAY_ENABLED +#include "lcd.h" +#ifdef QR_CODE_ENABLED +#include "qrcodegen.h" +#endif // QR_CODE_ENABLED +#endif // DISPLAY_ENABLED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/********************************************************** + * Defines and Constants + *********************************************************/ + +#define SYSTEM_STATE_LED &sl_led_led0 + +#define APP_FUNCTION_BUTTON &sl_button_btn0 +#define APP_LIGHT_SWITCH &sl_button_btn1 + +using namespace chip; +using namespace ::chip::DeviceLayer; + +namespace { + +/********************************************************** + * Variable declarations + *********************************************************/ + +EmberAfIdentifyEffectIdentifier sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; + +bool mCurrentButtonState = false; + +/********************************************************** + * Identify Callbacks + *********************************************************/ + +namespace { +void OnTriggerIdentifyEffectCompleted(chip::System::Layer * systemLayer, void * appState) +{ + ChipLogProgress(Zcl, "Trigger Identify Complete"); + sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; + +#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 + AppTask::GetAppTask().StopStatusLEDTimer(); +#endif +} +} // namespace + +void OnTriggerIdentifyEffect(Identify * identify) +{ + ChipLogProgress(Zcl, "Trigger Identify Effect"); + sIdentifyEffect = identify->mCurrentEffectIdentifier; + + if (identify->mCurrentEffectIdentifier == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_CHANNEL_CHANGE) + { + ChipLogProgress(Zcl, "IDENTIFY_EFFECT_IDENTIFIER_CHANNEL_CHANGE - Not supported, use effect varriant %d", + identify->mEffectVariant); + sIdentifyEffect = static_cast(identify->mEffectVariant); + } + +#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 + AppTask::GetAppTask().StartStatusLEDTimer(); +#endif + + switch (sIdentifyEffect) + { + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BLINK: + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BREATHE: + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_OKAY: + (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(5), OnTriggerIdentifyEffectCompleted, + identify); + break; + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_FINISH_EFFECT: + (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify); + (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(1), OnTriggerIdentifyEffectCompleted, + identify); + break; + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT: + (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify); + sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; + break; + default: + ChipLogProgress(Zcl, "No identifier effect"); + } +} + +Identify gIdentify = { + chip::EndpointId{ 1 }, + AppTask::GetAppTask().OnIdentifyStart, + AppTask::GetAppTask().OnIdentifyStop, + EMBER_ZCL_IDENTIFY_IDENTIFY_TYPE_VISIBLE_LED, + OnTriggerIdentifyEffect, +}; + +} // namespace + +using namespace chip::TLV; +using namespace ::chip::DeviceLayer; + +/********************************************************** + * AppTask Definitions + *********************************************************/ + +AppTask AppTask::sAppTask; + +CHIP_ERROR AppTask::Init() +{ + CHIP_ERROR err = CHIP_NO_ERROR; +#ifdef DISPLAY_ENABLED + GetLCD().Init((uint8_t *) "Light Switch"); +#endif + + err = BaseApplication::Init(&gIdentify); + if (err != CHIP_NO_ERROR) + { + SILABS_LOG("BaseApplication::Init() failed"); + appError(err); + } + + // Configure Bindings - TODO ERROR PROCESSING + err = InitBindingHandler(); + if (err != CHIP_NO_ERROR) + { + SILABS_LOG("InitBindingHandler() failed"); + appError(err); + } + + return err; +} + +CHIP_ERROR AppTask::StartAppTask() +{ + return BaseApplication::StartAppTask(AppTaskMain); +} + +void AppTask::AppTaskMain(void * pvParameter) +{ + AppEvent event; + QueueHandle_t sAppEventQueue = *(static_cast(pvParameter)); + + CHIP_ERROR err = sAppTask.Init(); + if (err != CHIP_NO_ERROR) + { + SILABS_LOG("AppTask.Init() failed"); + appError(err); + } + +#if !(defined(CHIP_DEVICE_CONFIG_ENABLE_SED) && CHIP_DEVICE_CONFIG_ENABLE_SED) + sAppTask.StartStatusLEDTimer(); +#endif + + SILABS_LOG("App Task started"); + while (true) + { + BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, portMAX_DELAY); + while (eventReceived == pdTRUE) + { + sAppTask.DispatchEvent(&event); + eventReceived = xQueueReceive(sAppEventQueue, &event, 0); + } + } +} + +void AppTask::OnIdentifyStart(Identify * identify) +{ + ChipLogProgress(Zcl, "onIdentifyStart"); + +#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 + sAppTask.StartStatusLEDTimer(); +#endif +} + +void AppTask::OnIdentifyStop(Identify * identify) +{ + ChipLogProgress(Zcl, "onIdentifyStop"); + +#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 + sAppTask.StopStatusLEDTimer(); +#endif +} + +void AppTask::SwitchActionEventHandler(AppEvent * aEvent) +{ + if (aEvent->Type == AppEvent::kEventType_Button) + { + BindingCommandData * data = Platform::New(); + data->clusterId = chip::app::Clusters::OnOff::Id; + + if (mCurrentButtonState) + { + mCurrentButtonState = false; + data->commandId = chip::app::Clusters::OnOff::Commands::Off::Id; + } + else + { + data->commandId = chip::app::Clusters::OnOff::Commands::On::Id; + mCurrentButtonState = true; + } + +#ifdef DISPLAY_ENABLED + sAppTask.GetLCD().WriteDemoUI(mCurrentButtonState); +#endif + + DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); + } +} + +void AppTask::ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAction) +{ + if (buttonHandle == NULL) + { + return; + } + + AppEvent button_event = {}; + button_event.Type = AppEvent::kEventType_Button; + button_event.ButtonEvent.Action = btnAction; + + button_event.Handler = SwitchActionEventHandler; + sAppTask.PostEvent(&button_event); +} diff --git a/examples/light-switch-app/silabs/SiWx917/src/ZclCallbacks.cpp b/examples/light-switch-app/silabs/SiWx917/src/ZclCallbacks.cpp new file mode 100644 index 00000000000000..52542ca88fb390 --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/src/ZclCallbacks.cpp @@ -0,0 +1,72 @@ +/* + * + * Copyright (c) 2020 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 "AppConfig.h" + +#include +#include +#include +#include + +using namespace ::chip; +using namespace ::chip::app::Clusters; + +void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size, + uint8_t * value) +{ + ClusterId clusterId = attributePath.mClusterId; + AttributeId attributeId = attributePath.mAttributeId; + ChipLogProgress(Zcl, "Cluster callback: " ChipLogFormatMEI, ChipLogValueMEI(clusterId)); + + if (clusterId == OnOffSwitchConfiguration::Id) + { + ChipLogProgress(Zcl, "OnOff Switch Configuration attribute ID: " ChipLogFormatMEI " Type: %u Value: %u, length %u", + ChipLogValueMEI(attributeId), type, *value, size); + + // WIP Apply attribute change to Light + } + else if (clusterId == Identify::Id) + { + ChipLogProgress(Zcl, "Identify attribute ID: " ChipLogFormatMEI " Type: %u Value: %u, length %u", + ChipLogValueMEI(attributeId), type, *value, size); + } +} + +/** @brief OnOff Cluster Init + * + * This function is called when a specific cluster is initialized. It gives the + * application an opportunity to take care of cluster initialization procedures. + * It is called exactly once for each endpoint where cluster is present. + * + * @param endpoint Ver.: always + * + * TODO Issue #3841 + * emberAfOnOffClusterInitCallback happens before the stack initialize the cluster + * attributes to the default value. + * The logic here expects something similar to the deprecated Plugins callback + * emberAfPluginOnOffClusterServerPostInitCallback. + * + */ +void emberAfOnOffClusterInitCallback(EndpointId endpoint) +{ + // TODO: implement any additional Cluster Server init actions +} diff --git a/examples/light-switch-app/silabs/SiWx917/src/binding-handler.cpp b/examples/light-switch-app/silabs/SiWx917/src/binding-handler.cpp new file mode 100644 index 00000000000000..90a2463a088e33 --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/src/binding-handler.cpp @@ -0,0 +1,431 @@ +/* + * + * Copyright (c) 2020 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. + */ + +#include "binding-handler.h" + +#include "AppConfig.h" +#include "app/CommandSender.h" +#include "app/clusters/bindings/BindingManager.h" +#include "app/server/Server.h" +#include "controller/InvokeInteraction.h" +#include "platform/CHIPDeviceLayer.h" +#include +#include + +#if defined(ENABLE_CHIP_SHELL) +#include "lib/shell/Engine.h" +#include "lib/shell/commands/Help.h" +#endif // ENABLE_CHIP_SHELL + +using namespace chip; +using namespace chip::app; + +#if defined(ENABLE_CHIP_SHELL) +using Shell::Engine; +using Shell::shell_command_t; +using Shell::streamer_get; +using Shell::streamer_printf; + +Engine sShellSwitchSubCommands; +Engine sShellSwitchOnOffSubCommands; + +Engine sShellSwitchGroupsSubCommands; +Engine sShellSwitchGroupsOnOffSubCommands; + +Engine sShellSwitchBindingSubCommands; +#endif // defined(ENABLE_CHIP_SHELL) + +namespace { + +void ProcessOnOffUnicastBindingCommand(CommandId commandId, const EmberBindingTableEntry & binding, + Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle) +{ + auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) { + ChipLogProgress(NotSpecified, "OnOff command succeeds"); + }; + + auto onFailure = [](CHIP_ERROR error) { + ChipLogError(NotSpecified, "OnOff command failed: %" CHIP_ERROR_FORMAT, error.Format()); + }; + + switch (commandId) + { + case Clusters::OnOff::Commands::Toggle::Id: + Clusters::OnOff::Commands::Toggle::Type toggleCommand; + Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, toggleCommand, onSuccess, onFailure); + break; + + case Clusters::OnOff::Commands::On::Id: + Clusters::OnOff::Commands::On::Type onCommand; + Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, onCommand, onSuccess, onFailure); + break; + + case Clusters::OnOff::Commands::Off::Id: + Clusters::OnOff::Commands::Off::Type offCommand; + Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, offCommand, onSuccess, onFailure); + break; + } +} + +void ProcessOnOffGroupBindingCommand(CommandId commandId, const EmberBindingTableEntry & binding) +{ + Messaging::ExchangeManager & exchangeMgr = Server::GetInstance().GetExchangeManager(); + + switch (commandId) + { + case Clusters::OnOff::Commands::Toggle::Id: + Clusters::OnOff::Commands::Toggle::Type toggleCommand; + Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, toggleCommand); + break; + + case Clusters::OnOff::Commands::On::Id: + Clusters::OnOff::Commands::On::Type onCommand; + Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, onCommand); + + break; + + case Clusters::OnOff::Commands::Off::Id: + Clusters::OnOff::Commands::Off::Type offCommand; + Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, offCommand); + break; + } +} + +void LightSwitchChangedHandler(const EmberBindingTableEntry & binding, OperationalDeviceProxy * peer_device, void * context) +{ + VerifyOrReturn(context != nullptr, ChipLogError(NotSpecified, "OnDeviceConnectedFn: context is null")); + BindingCommandData * data = static_cast(context); + + if (binding.type == EMBER_MULTICAST_BINDING && data->isGroup) + { + switch (data->clusterId) + { + case Clusters::OnOff::Id: + ProcessOnOffGroupBindingCommand(data->commandId, binding); + break; + } + } + else if (binding.type == EMBER_UNICAST_BINDING && !data->isGroup) + { + switch (data->clusterId) + { + case Clusters::OnOff::Id: + VerifyOrDie(peer_device != nullptr && peer_device->ConnectionReady()); + ProcessOnOffUnicastBindingCommand(data->commandId, binding, peer_device->GetExchangeManager(), + peer_device->GetSecureSession().Value()); + break; + } + } +} + +void LightSwitchContextReleaseHandler(void * context) +{ + VerifyOrReturn(context != nullptr, ChipLogError(NotSpecified, "LightSwitchContextReleaseHandler: context is null")); + + Platform::Delete(static_cast(context)); +} + +#ifdef ENABLE_CHIP_SHELL + +/******************************************************** + * Switch shell functions + *********************************************************/ + +CHIP_ERROR SwitchHelpHandler(int argc, char ** argv) +{ + sShellSwitchSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); + return CHIP_NO_ERROR; +} + +CHIP_ERROR SwitchCommandHandler(int argc, char ** argv) +{ + if (argc == 0) + { + return SwitchHelpHandler(argc, argv); + } + + return sShellSwitchSubCommands.ExecCommand(argc, argv); +} + +/******************************************************** + * OnOff switch shell functions + *********************************************************/ + +CHIP_ERROR OnOffHelpHandler(int argc, char ** argv) +{ + sShellSwitchOnOffSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OnOffSwitchCommandHandler(int argc, char ** argv) +{ + if (argc == 0) + { + return OnOffHelpHandler(argc, argv); + } + + return sShellSwitchOnOffSubCommands.ExecCommand(argc, argv); +} + +CHIP_ERROR OnSwitchCommandHandler(int argc, char ** argv) +{ + BindingCommandData * data = Platform::New(); + data->commandId = Clusters::OnOff::Commands::On::Id; + data->clusterId = Clusters::OnOff::Id; + + DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OffSwitchCommandHandler(int argc, char ** argv) +{ + BindingCommandData * data = Platform::New(); + data->commandId = Clusters::OnOff::Commands::Off::Id; + data->clusterId = Clusters::OnOff::Id; + + DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR ToggleSwitchCommandHandler(int argc, char ** argv) +{ + BindingCommandData * data = Platform::New(); + data->commandId = Clusters::OnOff::Commands::Toggle::Id; + data->clusterId = Clusters::OnOff::Id; + + DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); + return CHIP_NO_ERROR; +} + +/******************************************************** + * bind switch shell functions + *********************************************************/ + +CHIP_ERROR BindingHelpHandler(int argc, char ** argv) +{ + sShellSwitchBindingSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); + return CHIP_NO_ERROR; +} + +CHIP_ERROR BindingSwitchCommandHandler(int argc, char ** argv) +{ + if (argc == 0) + { + return BindingHelpHandler(argc, argv); + } + + return sShellSwitchBindingSubCommands.ExecCommand(argc, argv); +} + +CHIP_ERROR BindingGroupBindCommandHandler(int argc, char ** argv) +{ + VerifyOrReturnError(argc == 2, CHIP_ERROR_INVALID_ARGUMENT); + + EmberBindingTableEntry * entry = Platform::New(); + entry->type = EMBER_MULTICAST_BINDING; + entry->fabricIndex = atoi(argv[0]); + entry->groupId = atoi(argv[1]); + entry->local = 1; // Hardcoded to endpoint 1 for now + entry->clusterId.SetValue(6); // Hardcoded to OnOff cluster for now + + DeviceLayer::PlatformMgr().ScheduleWork(BindingWorkerFunction, reinterpret_cast(entry)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR BindingUnicastBindCommandHandler(int argc, char ** argv) +{ + VerifyOrReturnError(argc == 3, CHIP_ERROR_INVALID_ARGUMENT); + + EmberBindingTableEntry * entry = Platform::New(); + entry->type = EMBER_UNICAST_BINDING; + entry->fabricIndex = atoi(argv[0]); + entry->nodeId = atoi(argv[1]); + entry->local = 1; // Hardcoded to endpoint 1 for now + entry->remote = atoi(argv[2]); + entry->clusterId.SetValue(6); // Hardcode to OnOff cluster for now + + DeviceLayer::PlatformMgr().ScheduleWork(BindingWorkerFunction, reinterpret_cast(entry)); + return CHIP_NO_ERROR; +} + +/******************************************************** + * Groups switch shell functions + *********************************************************/ + +CHIP_ERROR GroupsHelpHandler(int argc, char ** argv) +{ + sShellSwitchGroupsSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); + return CHIP_NO_ERROR; +} + +CHIP_ERROR GroupsSwitchCommandHandler(int argc, char ** argv) +{ + if (argc == 0) + { + return GroupsHelpHandler(argc, argv); + } + + return sShellSwitchGroupsSubCommands.ExecCommand(argc, argv); +} + +/******************************************************** + * Groups OnOff switch shell functions + *********************************************************/ + +CHIP_ERROR GroupsOnOffHelpHandler(int argc, char ** argv) +{ + sShellSwitchGroupsOnOffSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); + return CHIP_NO_ERROR; +} + +CHIP_ERROR GroupsOnOffSwitchCommandHandler(int argc, char ** argv) +{ + if (argc == 0) + { + return GroupsOnOffHelpHandler(argc, argv); + } + + return sShellSwitchGroupsOnOffSubCommands.ExecCommand(argc, argv); +} + +CHIP_ERROR GroupOnSwitchCommandHandler(int argc, char ** argv) +{ + BindingCommandData * data = Platform::New(); + data->commandId = Clusters::OnOff::Commands::On::Id; + data->clusterId = Clusters::OnOff::Id; + data->isGroup = true; + + DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR GroupOffSwitchCommandHandler(int argc, char ** argv) +{ + BindingCommandData * data = Platform::New(); + data->commandId = Clusters::OnOff::Commands::Off::Id; + data->clusterId = Clusters::OnOff::Id; + data->isGroup = true; + + DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR GroupToggleSwitchCommandHandler(int argc, char ** argv) +{ + BindingCommandData * data = Platform::New(); + data->commandId = Clusters::OnOff::Commands::Toggle::Id; + data->clusterId = Clusters::OnOff::Id; + data->isGroup = true; + + DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); + return CHIP_NO_ERROR; +} + +/** + * @brief configures switch matter shell + * + */ +static void RegisterSwitchCommands() +{ + static const shell_command_t sSwitchSubCommands[] = { + { &SwitchHelpHandler, "help", "Usage: switch " }, + { &OnOffSwitchCommandHandler, "onoff", " Usage: switch onoff " }, + { &GroupsSwitchCommandHandler, "groups", "Usage: switch groups " }, + { &BindingSwitchCommandHandler, "binding", "Usage: switch binding " } + }; + + static const shell_command_t sSwitchOnOffSubCommands[] = { + { &OnOffHelpHandler, "help", "Usage : switch ononff " }, + { &OnSwitchCommandHandler, "on", "Sends on command to bound lighting app" }, + { &OffSwitchCommandHandler, "off", "Sends off command to bound lighting app" }, + { &ToggleSwitchCommandHandler, "toggle", "Sends toggle command to bound lighting app" } + }; + + static const shell_command_t sSwitchGroupsSubCommands[] = { { &GroupsHelpHandler, "help", "Usage: switch groups " }, + { &GroupsOnOffSwitchCommandHandler, "onoff", + "Usage: switch groups onoff " } }; + + static const shell_command_t sSwitchGroupsOnOffSubCommands[] = { + { &GroupsOnOffHelpHandler, "help", "Usage: switch groups onoff " }, + { &GroupOnSwitchCommandHandler, "on", "Sends on command to bound group" }, + { &GroupOffSwitchCommandHandler, "off", "Sends off command to bound group" }, + { &GroupToggleSwitchCommandHandler, "toggle", "Sends toggle command to group" } + }; + + static const shell_command_t sSwitchBindingSubCommands[] = { + { &BindingHelpHandler, "help", "Usage: switch binding " }, + { &BindingGroupBindCommandHandler, "group", "Usage: switch binding group " }, + { &BindingUnicastBindCommandHandler, "unicast", "Usage: switch binding group " } + }; + + static const shell_command_t sSwitchCommand = { &SwitchCommandHandler, "switch", + "Light-switch commands. Usage: switch " }; + + sShellSwitchGroupsOnOffSubCommands.RegisterCommands(sSwitchGroupsOnOffSubCommands, ArraySize(sSwitchGroupsOnOffSubCommands)); + sShellSwitchOnOffSubCommands.RegisterCommands(sSwitchOnOffSubCommands, ArraySize(sSwitchOnOffSubCommands)); + sShellSwitchGroupsSubCommands.RegisterCommands(sSwitchGroupsSubCommands, ArraySize(sSwitchGroupsSubCommands)); + sShellSwitchBindingSubCommands.RegisterCommands(sSwitchBindingSubCommands, ArraySize(sSwitchBindingSubCommands)); + sShellSwitchSubCommands.RegisterCommands(sSwitchSubCommands, ArraySize(sSwitchSubCommands)); + + Engine::Root().RegisterCommands(&sSwitchCommand, 1); +} +#endif // ENABLE_CHIP_SHELL + +void InitBindingHandlerInternal(intptr_t arg) +{ + auto & server = chip::Server::GetInstance(); + chip::BindingManager::GetInstance().Init( + { &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() }); + chip::BindingManager::GetInstance().RegisterBoundDeviceChangedHandler(LightSwitchChangedHandler); + chip::BindingManager::GetInstance().RegisterBoundDeviceContextReleaseHandler(LightSwitchContextReleaseHandler); +} + +} // namespace + +/******************************************************** + * Switch functions + *********************************************************/ + +void SwitchWorkerFunction(intptr_t context) +{ + VerifyOrReturn(context != 0, ChipLogError(NotSpecified, "SwitchWorkerFunction - Invalid work data")); + + BindingCommandData * data = reinterpret_cast(context); + BindingManager::GetInstance().NotifyBoundClusterChanged(data->localEndpointId, data->clusterId, static_cast(data)); +} + +void BindingWorkerFunction(intptr_t context) +{ + VerifyOrReturn(context != 0, ChipLogError(NotSpecified, "BindingWorkerFunction - Invalid work data")); + + EmberBindingTableEntry * entry = reinterpret_cast(context); + AddBindingEntry(*entry); + + Platform::Delete(entry); +} + +CHIP_ERROR InitBindingHandler() +{ + // The initialization of binding manager will try establishing connection with unicast peers + // so it requires the Server instance to be correctly initialized. Post the init function to + // the event queue so that everything is ready when initialization is conducted. + chip::DeviceLayer::PlatformMgr().ScheduleWork(InitBindingHandlerInternal); +#if defined(ENABLE_CHIP_SHELL) + RegisterSwitchCommands(); +#endif + return CHIP_NO_ERROR; +} diff --git a/examples/light-switch-app/silabs/SiWx917/src/main.cpp b/examples/light-switch-app/silabs/SiWx917/src/main.cpp new file mode 100644 index 00000000000000..c67851a2ca78cc --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/src/main.cpp @@ -0,0 +1,87 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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 + +#include "AppConfig.h" +#include "init_ccpPlatform.h" +#include +#include +#include +#include +#ifdef SI917_ATTESTATION_CREDENTIALS +#include +#else +#include +#endif + +#define BLE_DEV_NAME "SiLabs-Light-Switch" + +extern "C" void sl_button_on_change(); + +using namespace ::chip; +using namespace ::chip::Inet; +using namespace ::chip::DeviceLayer; +using namespace ::chip::Credentials; + +#define UNUSED_PARAMETER(a) (a = a) + +volatile int apperror_cnt; +static chip::DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider; + +// ================================================================================ +// Main Code +// ================================================================================ +int main(void) +{ + init_ccpPlatform(); + if (SI917MatterConfig::InitMatter(BLE_DEV_NAME) != CHIP_NO_ERROR) + { + appError(CHIP_ERROR_INTERNAL); + } + + gExampleDeviceInfoProvider.SetStorageDelegate(&Server::GetInstance().GetPersistentStorage()); + chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); + + chip::DeviceLayer::PlatformMgr().LockChipStack(); + // Initialize device attestation config +#ifdef SI917_ATTESTATION_CREDENTIALS + SetDeviceAttestationCredentialsProvider(SIWx917::GetSIWx917DacProvider()); +#else + SetDeviceAttestationCredentialsProvider(Examples::GetExampleDACProvider()); +#endif + chip::DeviceLayer::PlatformMgr().UnlockChipStack(); + + SILABS_LOG("Starting App Task"); + if (AppTask::GetAppTask().StartAppTask() != CHIP_NO_ERROR) + appError(CHIP_ERROR_INTERNAL); + + SILABS_LOG("Starting FreeRTOS scheduler"); + vTaskStartScheduler(); + + // Should never get here. + chip::Platform::MemoryShutdown(); + SILABS_LOG("vTaskStartScheduler() failed"); + appError(CHIP_ERROR_INTERNAL); +} + +void sl_button_on_change() +{ + AppTask::GetAppTask().ButtonEventHandler(APP_LIGHT_SWITCH, SL_SIMPLE_BUTTON_PRESSED); +} diff --git a/examples/light-switch-app/silabs/SiWx917/third_party/connectedhomeip b/examples/light-switch-app/silabs/SiWx917/third_party/connectedhomeip new file mode 120000 index 00000000000000..59307833b4fee9 --- /dev/null +++ b/examples/light-switch-app/silabs/SiWx917/third_party/connectedhomeip @@ -0,0 +1 @@ +../../../../.. \ No newline at end of file diff --git a/examples/lock-app/silabs/SiWx917/.gn b/examples/lock-app/silabs/SiWx917/.gn new file mode 100644 index 00000000000000..4c078f6acefdcc --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/.gn @@ -0,0 +1,29 @@ +# Copyright (c) 2020 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. + +import("//build_overrides/build.gni") + +# The location of the build configuration file. +buildconfig = "${build_root}/config/BUILDCONFIG.gn" + +# CHIP uses angle bracket includes. +check_system_includes = true + +default_args = { + target_cpu = "arm" + target_os = "freertos" + chip_openthread_ftd = true + + import("//args.gni") +} diff --git a/examples/lock-app/silabs/SiWx917/BUILD.gn b/examples/lock-app/silabs/SiWx917/BUILD.gn new file mode 100644 index 00000000000000..dd196fa421303b --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/BUILD.gn @@ -0,0 +1,390 @@ +# Copyright (c) 2020 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") +import("//build_overrides/efr32_sdk.gni") +import("//build_overrides/pigweed.gni") + +import("${build_root}/config/defaults.gni") +import("${efr32_sdk_build_root}/SiWx917_sdk.gni") +import("${efr32_sdk_build_root}/efr32_executable.gni") + +import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") +import("${chip_root}/src/platform/device.gni") + +if (chip_enable_pw_rpc) { + import("//build_overrides/pigweed.gni") + import("$dir_pw_build/target_types.gni") +} + +assert(current_os == "freertos") + +efr32_project_dir = "${chip_root}/examples/lock-app/silabs/SiWx917" +examples_plat_dir = "${chip_root}/examples/platform/silabs/SiWx917" +examples_common_plat_dir = "${chip_root}/examples/platform/silabs" + +declare_args() { + # Dump memory usage at link time. + chip_print_memory_usage = false + + # Monitor & log memory usage at runtime. + enable_heap_monitoring = false + + # Enable Sleepy end device + enable_sleepy_device = false + + # OTA timeout in seconds + OTA_periodic_query_timeout = 86400 + + # Wifi related stuff - they are overridden by gn -args="use_wf200=true" + use_wf200 = false + use_rs911x = false + use_rs911x_sockets = false + sl_wfx_config_softap = false + sl_wfx_config_scan = true + + # Disable LCD on supported devices + disable_lcd = true + + # Argument to Disable IPv4 for wifi(rs911) + chip_enable_wifi_ipv4 = false + + # Argument to force enable WPA3 security + rs91x_wpa3_only = false + + #default WiFi SSID + chip_default_wifi_ssid = "" + + #default Wifi Password + chip_default_wifi_psk = "" +} + +declare_args() { + # Enables LCD Qr Code on supported devices + show_qr_code = !disable_lcd +} + +# qr code cannot be true if lcd is disabled +assert(!(disable_lcd && show_qr_code)) + +# Sanity check +assert(!(chip_enable_wifi && chip_enable_openthread)) +assert(!(use_rs911x && chip_enable_openthread)) +assert(!(use_wf200 && chip_enable_openthread)) +if (chip_enable_wifi) { + assert(use_rs911x || use_wf200) + enable_openthread_cli = false + import("${chip_root}/src/platform/silabs/SiWx917/wifi_args.gni") +} + +# ThunderBoards, Explorer Kit and MGM240L do not support LCD (No LCD) +if (silabs_board == "BRD4166A" || silabs_board == "BRD2601B" || + silabs_board == "BRD2703A" || silabs_board == "BRD4319A") { + show_qr_code = false + disable_lcd = true +} + +defines = [] + +# WiFi settings +if (chip_enable_wifi) { + if (chip_default_wifi_ssid != "") { + defines += [ + "CHIP_ONNETWORK_PAIRING=1", + "CHIP_WIFI_SSID=\"${chip_default_wifi_ssid}\"", + ] + } + if (chip_default_wifi_psk != "") { + assert(chip_default_wifi_ssid != "", + "ssid can't be null if psk is provided") + defines += [ "CHIP_WIFI_PSK=\"${chip_default_wifi_psk}\"" ] + } + wifi_sdk_dir = "${chip_root}/src/platform/silabs/SiWx917/wifi" + efr32_lwip_defs = [ "LWIP_NETIF_API=1" ] + if (lwip_ipv4) { + efr32_lwip_defs += [ + "LWIP_IPV4=1", + + # adds following options to provide + # them to .cpp source files + # flags ported from lwipopts file + # TODO: move lwipopts to one location + "LWIP_ARP=1", + "LWIP_ICMP=1", + "LWIP_IGMP=1", + "LWIP_DHCP=1", + "LWIP_DNS=0", + ] + } else { + efr32_lwip_defs += [ "LWIP_IPV4=0" ] + } + if (lwip_ipv6) { + efr32_lwip_defs += [ "LWIP_IPV6=1" ] + } else { + efr32_lwip_defs += [ "LWIP_IPV6=0" ] + } + + wiseconnect_sdk_root = "${chip_root}/third_party/silabs/wisemcu-wifi-bt-sdk" + import("${examples_plat_dir}/SiWx917/rs911x.gni") +} + +efr32_sdk("sdk") { + sources = [ + "${efr32_project_dir}/include/CHIPProjectConfig.h", + "${examples_plat_dir}/FreeRTOSConfig.h", + ] + + include_dirs = [ + "${chip_root}/src/platform/silabs/SiWx917", + "${efr32_project_dir}/include", + "${examples_plat_dir}", + "${chip_root}/src/lib", + "${examples_common_plat_dir}", + ] + + defines += [ + "BOARD_ID=${silabs_board}", + "OTA_PERIODIC_TIMEOUT=${OTA_periodic_query_timeout}", + ] + + if (chip_enable_pw_rpc) { + defines += [ + "HAL_VCOM_ENABLE=1", + "PW_RPC_ENABLED", + ] + } + + # WiFi Settings + if (chip_enable_wifi) { + if (use_rs911x) { + defines += rs911x_defs + include_dirs += rs911x_plat_incs + } else if (use_wf200) { + defines += wf200_defs + include_dirs += wf200_plat_incs + } + + if (use_rs911x_sockets) { + include_dirs += [ "${examples_plat_dir}/wifi/rsi-sockets" ] + defines += rs911x_sock_defs + } else { + # Using LWIP instead of the native TCP/IP stack + defines += efr32_lwip_defs + } + if (sl_wfx_config_softap) { + defines += [ "SL_WFX_CONFIG_SOFTAP" ] + } + if (sl_wfx_config_scan) { + defines += [ "SL_WFX_CONFIG_SCAN" ] + } + } +} + +efr32_executable("lock_app") { + output_name = "chip-efr32-lock-example.out" + public_configs = [ "${efr32_sdk_build_root}:silabs_config" ] + include_dirs = [ "include" ] + defines = [] + + sources = [ + "${examples_common_plat_dir}/heap_4_silabs.c", + "${examples_plat_dir}/BaseApplication.cpp", + "${examples_plat_dir}/init_ccpPlatform.cpp", + "${examples_plat_dir}/matter_config.cpp", + "${examples_plat_dir}/siwx917_utils.cpp", + "src/AppTask.cpp", + "src/LockManager.cpp", + "src/ZclCallbacks.cpp", + "src/main.cpp", + ] + + if (use_wstk_leds) { + #sources += [ "${examples_plat_dir}/LEDWidget.cpp" ] + } + + if (chip_enable_pw_rpc || chip_build_libshell || enable_openthread_cli || + use_wf200 || use_rs911x) { + #sources += [ "${examples_plat_dir}/uart.cpp" ] + } + + if (chip_build_libshell) { + sources += [ "src/EventHandlerLibShell.cpp" ] + } + + deps = [ + ":sdk", + "${chip_root}/examples/lock-app/lock-common", + "${chip_root}/examples/providers:device_info_provider", + "${chip_root}/src/lib", + "${chip_root}/src/setup_payload", + "${chip_root}/third_party/openthread/platforms:libopenthread-platform", + "${chip_root}/third_party/openthread/platforms:libopenthread-platform-utils", + "${examples_plat_dir}:efr-matter-shell", + ] + + # OpenThread Settings + if (chip_enable_openthread) { + deps += [ + "${chip_root}/third_party/openthread:openthread", + "${chip_root}/third_party/openthread:openthread-platform", + "${examples_plat_dir}:efr-matter-shell", + ] + } + + if (chip_enable_ota_requestor) { + defines += [ "EFR32_OTA_ENABLED" ] + #sources += [ "${examples_plat_dir}/OTAConfig.cpp" ] + } + + # WiFi Settings + if (chip_enable_wifi) { + if (use_rs911x) { + sources += rs911x_src_plat + + # All the stuff from wiseconnect + sources += rs911x_src_sapi + + # Apparently - the rsi library needs this (though we may not use use it) + sources += rs911x_src_sock + include_dirs += rs911x_inc_plat + + if (use_rs911x_sockets) { + # + # Using native sockets inside RS911x + # + include_dirs += rs911x_sock_inc + } else { + # + # We use LWIP - not built-in sockets + # + sources += rs911x_src_lwip + } + } else if (use_wf200) { + sources += wf200_plat_src + include_dirs += wf200_plat_incs + } + + if (chip_enable_wifi_ipv4) { + defines += [ "CHIP_DEVICE_CONFIG_ENABLE_IPV4" ] + } + + if (rs91x_wpa3_only) { + # TODO: Change this macro once WF200 support is provided + defines += [ "WIFI_ENABLE_SECURITY_WPA3=1" ] + } + } + + if (!disable_lcd) { + sources += [ + "${examples_plat_dir}/display/demo-ui.c", + "${examples_plat_dir}/display/lcd.cpp", + ] + include_dirs += [ "${examples_plat_dir}/display" ] + defines += [ + "DISPLAY_ENABLED", + "IS_DEMO_LOCK=1", + ] + if (show_qr_code) { + defines += [ "QR_CODE_ENABLED" ] + deps += [ "${chip_root}/examples/common/QRCode" ] + } + } + + if (chip_enable_pw_rpc) { + defines += [ + "PW_RPC_ENABLED", + "PW_RPC_ATTRIBUTE_SERVICE=1", + "PW_RPC_BUTTON_SERVICE=1", + "PW_RPC_DESCRIPTOR_SERVICE=1", + "PW_RPC_DEVICE_SERVICE=1", + "PW_RPC_LOCKING_SERVICE=1", + "PW_RPC_OTCLI_SERVICE=1", + "PW_RPC_THREAD_SERVICE=1", + ] + + sources += [ + "${chip_root}/examples/common/pigweed/RpcService.cpp", + "${chip_root}/examples/common/pigweed/efr32/PigweedLoggerMutex.cpp", + "${examples_common_plat_dir}/PigweedLogger.cpp", + "${examples_common_plat_dir}/Rpc.cpp", + ] + + deps += [ + "$dir_pw_hdlc:rpc_channel_output", + "$dir_pw_stream:sys_io_stream", + "${chip_root}/config/efr32/lib/pw_rpc:pw_rpc", + "${chip_root}/examples/common/pigweed:attributes_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:button_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:descriptor_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:device_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:locking_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:ot_cli_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:thread_service.nanopb_rpc", + "${examples_plat_dir}/pw_sys_io:pw_sys_io_efr32", + ] + + deps += pw_build_LINK_DEPS + + include_dirs += [ + "${chip_root}/examples/common", + "${chip_root}/examples/common/pigweed/efr32", + ] + } + + if (enable_heap_monitoring) { + sources += [ "${examples_common_plat_dir}/MemMonitoring.cpp" ] + defines += [ "HEAP_MONITORING" ] + } + + ldscript = "${examples_plat_dir}/ldscripts/${silabs_family}.ld" + + inputs = [ ldscript ] + + ldflags = [ "-T" + rebase_path(ldscript, root_build_dir) ] + + if (chip_print_memory_usage) { + ldflags += [ + "-Wl,--print-memory-usage", + "-fstack-usage", + ] + } + + # WiFi Settings + if (chip_enable_wifi) { + ldflags += [ + "-Wl,--defsym", + "-Wl,SILABS_WIFI=1", + ] + } + + # Attestation Credentials + if (chip_build_platform_attestation_credentials_provider) { + deps += [ "${examples_plat_dir}:efr32-attestation-credentials" ] + } + + # Factory Data Provider + if (use_efr32_factory_data_provider) { + deps += [ "${examples_plat_dir}:efr32-factory-data-provider" ] + } + + output_dir = root_out_dir +} +group("efr32") { + deps = [ ":lock_app" ] +} + +group("default") { + deps = [ ":efr32" ] +} diff --git a/examples/lock-app/silabs/SiWx917/README.md b/examples/lock-app/silabs/SiWx917/README.md new file mode 100644 index 00000000000000..a681df76cc6d45 --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/README.md @@ -0,0 +1,457 @@ +# Matter EFR32 Lock Example + +An example showing the use of CHIP on the Silicon Labs EFR32 MG12 and MG24. + +
+ +- [Matter EFR32 Lock Example](#matter-efr32-lock-example) + - [Introduction](#introduction) + - [Building](#building) + - [Flashing the Application](#flashing-the-application) + - [Viewing Logging Output](#viewing-logging-output) + - [Running the Complete Example](#running-the-complete-example) + - [Notes](#notes) + - [Memory settings](#memory-settings) + - [OTA Software Update](#ota-software-update) + - [Building options](#building-options) + - [Disabling logging](#disabling-logging) + - [Debug build / release build](#debug-build--release-build) + - [Disabling LCD](#disabling-lcd) + - [KVS maximum entry count](#kvs-maximum-entry-count) + +
+ +> **NOTE:** Silicon Laboratories now maintains a public matter GitHub repo with +> frequent releases thoroughly tested and validated. Developers looking to +> develop matter products with silabs hardware are encouraged to use our latest +> release with added tools and documentation. +> [Silabs Matter Github](https://github.com/SiliconLabs/matter/releases) + + + +## Introduction + +The EFR32 lock example provides a baseline demonstration of a door lock control +device, built using Matter and the Silicon Labs gecko SDK. It can be controlled +by a Chip controller over an Openthread or Wifi network.. + +The EFR32 device can be commissioned over Bluetooth Low Energy where the device +and the Chip controller will exchange security information with the Rendez-vous +procedure. If using Thread, Thread Network credentials are then provided to the +EFR32 device which will then join the Thread network. + +If the LCD is enabled, the LCD on the Silabs WSTK shows a QR Code containing the +needed commissioning information for the BLE connection and starting the +Rendez-vous procedure. + +The lighting example is intended to serve both as a means to explore the +workings of Matter as well as a template for creating real products based on the +Silicon Labs platform. + + + +## Building + +- Download the + [Simplicity Commander](https://www.silabs.com/mcu/programming-options) + command line tool, and ensure that `commander` is your shell search path. + (For Mac OS X, `commander` is located inside + `Commander.app/Contents/MacOS/`.) + +- Download and install a suitable ARM gcc tool chain: + [GNU Arm Embedded Toolchain 9-2019-q4-major](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads) + +- Install some additional tools(likely already present for CHIP developers): + +Linux + + sudo apt-get install git ninja-build + +Mac OS X + + brew install ninja + +- Supported hardware: + + - > For the latest supported hardware please refer to the + > [Hardware Requirements](https://github.com/SiliconLabs/matter/blob/latest/docs/silabs/general/HARDWARE_REQUIREMENTS.md) + > in the Silicon Labs Matter Github Repo + + MG12 boards: + + - BRD4161A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@19dBm + - BRD4162A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4163A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm, + 868MHz@19dBm + - BRD4164A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@19dBm + - BRD4166A / SLTB004A / Thunderboard Sense 2 / 2.4GHz@10dBm + - BRD4170A / SLWSTK6000B / Multiband Wireless Starter Kit / 2.4GHz@19dBm, + 915MHz@19dBm + - BRD4304A / SLWSTK6000B / MGM12P Module / 2.4GHz@19dBm + + MG21 boards: Currently not supported due to RAM limitation. + + - BRD4180A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + + MG24 boards : + + - BRD2601B / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD2703A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4186A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4186C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4187A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + - BRD4187C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + + MG12 boards: + + - BRD4161A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@19dBm + - BRD4162A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4163A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm, + 868MHz@19dBm + - BRD4164A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@19dBm + - BRD4166A / SLTB004A / Thunderboard Sense 2 / 2.4GHz@10dBm + - BRD4170A / SLWSTK6000B / Multiband Wireless Starter Kit / 2.4GHz@19dBm, + 915MHz@19dBm + - BRD4304A / SLWSTK6000B / MGM12P Module / 2.4GHz@19dBm + + MG21 boards: Currently not supported due to RAM limitation. + + - BRD4180A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + + MG24 boards : + + - BRD4162A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4163A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm, + 868MHz@19dBm + - BRD4186A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4186C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4187A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + - BRD4187C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + +* Build the example application: + + ``` + cd ~/connectedhomeip + ./scripts/examples/gn_efr32_example.sh ./examples/lock-app/efr32/ ./out/lock_app BRD4161A + ``` + +- To delete generated executable, libraries and object files use: + + ``` + $ cd ~/connectedhomeip + $ rm -rf ./out/ + ``` + + OR use GN/Ninja directly + + ``` + $ cd ~/connectedhomeip/examples/lock-app/efr32 + $ git submodule update --init + $ source third_party/connectedhomeip/scripts/activate.sh + $ export EFR32_BOARD=BRD4161A + $ gn gen out/debug --args="efr32_sdk_root=\"${EFR32_SDK_ROOT}\" silabs_board=\"${EFR32_BOARD}\"" + $ ninja -C out/debug + ``` + +- To delete generated executable, libraries and object files use: + + ``` + $ cd ~/connectedhomeip/examples/lock-app/efr32 + $ rm -rf out/ + ``` + +* Build the example as Sleepy End Device (SED) + + ``` + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32/ ./out/lighting-app_SED BRD4161A --sed + ``` + + or use gn as previously mentioned but adding the following arguments: + + ``` + $ gn gen out/debug '--args=silabs_board="BRD4161A" enable_sleepy_device=true chip_openthread_ftd=false' + ``` + +* Build the example with pigweed RCP + + ``` + $ ./scripts/examples/gn_efr32_example.sh examples/lock-app/efr32/ out/lock_app_rpc BRD4161A 'import("//with_pw_rpc.gni")' + ``` + + or use GN/Ninja Directly + + ``` + $ cd ~/connectedhomeip/examples/lock-app/efr32 + $ git submodule update --init + $ source third_party/connectedhomeip/scripts/activate.sh + $ export EFR32_BOARD=BRD4161A + $ gn gen out/debug --args='import("//with_pw_rpc.gni")' + $ ninja -C out/debug + ``` + + [Running Pigweed RPC console](#running-pigweed-rpc-console) + +For more build options, help is provided when running the build script without +arguments + + ``` + ./scripts/examples/gn_efr32_example.sh + ``` + + + +## Flashing the Application + +- On the command line: + + ``` + $ cd ~/connectedhomeip/examples/lock-app/efr32 + $ python3 out/debug/chip-efr32-lock-example.flash.py + ``` + +- Or with the Ozone debugger, just load the .out file. + + + +## Viewing Logging Output + +The example application is built to use the SEGGER Real Time Transfer (RTT) +facility for log output. RTT is a feature built-in to the J-Link Interface MCU +on the WSTK development board. It allows bi-directional communication with an +embedded application without the need for a dedicated UART. + +Using the RTT facility requires downloading and installing the _SEGGER J-Link +Software and Documentation Pack_ +([web site](https://www.segger.com/downloads/jlink#J-LinkSoftwareAndDocumentationPack)). + +Alternatively, SEGGER Ozone J-Link debugger can be used to view RTT logs too +after flashing the .out file. + +- Download the J-Link installer by navigating to the appropriate URL and + agreeing to the license agreement. + +- [JLink_Linux_x86_64.deb](https://www.segger.com/downloads/jlink/JLink_Linux_x86_64.deb) +- [JLink_MacOSX.pkg](https://www.segger.com/downloads/jlink/JLink_MacOSX.pkg) + +* Install the J-Link software + + ``` + $ cd ~/Downloads + $ sudo dpkg -i JLink_Linux_V*_x86_64.deb + ``` + +* In Linux, grant the logged in user the ability to talk to the development + hardware via the linux tty device (/dev/ttyACMx) by adding them to the + dialout group. + + ``` + $ sudo usermod -a -G dialout ${USER} + ``` + +Once the above is complete, log output can be viewed using the JLinkExe tool in +combination with JLinkRTTClient as follows: + +- Run the JLinkExe tool with arguments to autoconnect to the WSTK board: + + For MG12 use: + + ``` + $ JLinkExe -device EFR32MG12PXXXF1024 -if JTAG -speed 4000 -autoconnect 1 + ``` + + For MG21 use: + + ``` + $ JLinkExe -device EFR32MG21AXXXF1024 -if SWD -speed 4000 -autoconnect 1 + ``` + +- In a second terminal, run the JLinkRTTClient to view logs: + + ``` + $ JLinkRTTClient + ``` + + + +## Running the Complete Example + +- It is assumed here that you already have an OpenThread border router + configured and running. If not see the following guide + [Openthread_border_router](https://github.com/project-chip/connectedhomeip/blob/master/docs/guides/openthread_border_router_pi.md) + for more information on how to setup a border router on a raspberryPi. + + Take note that the RCP code is available directly through + [Simplicity Studio 5](https://www.silabs.com/products/development-tools/software/simplicity-studio/simplicity-studio-5) + under File->New->Project Wizard->Examples->Thread : ot-rcp + +- User interface : **LCD** The LCD on Silabs WSTK shows a QR Code. This QR + Code is be scanned by the CHIP Tool app For the Rendez-vous procedure over + BLE + + * On devices that do not have or support the LCD Display like the BRD4166A Thunderboard Sense 2, + a URL can be found in the RTT logs. + + [SVR] Copy/paste the below URL in a browser to see the QR Code: + [SVR] https://project-chip.github.io/connectedhomeip/qrcode.html?data=CH%3AI34NM%20-00%200C9SS0 + + **LED 0** 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 1** Simulates the Lock The following states are possible: + + - _Solid On_ ; Bolt is unlocked + - _Blinking_ ; Bolt is moving to the desired state + - _Off_ ; Bolt is locked + + **Push Button 0** + + - _Press and Release_ : Start, or restart, BLE advertisement in fast mode. It will advertise in this mode + for 30 seconds. The device will then switch to a slower interval advertisement. + After 15 minutes, the advertisement stops. + + - _Pressed and hold for 6 s_ : Initiates the factory reset of the device. + Releasing the button within the 6-second window cancels the factory reset + procedure. **LEDs** blink in unison when the factory reset procedure is + initiated. + + **Push Button 1** Toggles the bolt state On/Off + +- You can provision and control the Chip device using the python controller, + Chip tool standalone, Android or iOS app + + [CHIPTool](https://github.com/project-chip/connectedhomeip/blob/master/examples/chip-tool/README.md) + +Here is some CHIPTool examples: + + Pairing with chip-tool: + ``` + chip-tool pairing ble-thread 1 hex: 20202021 3840 + ``` + + Set a user: + ``` + ./out/chip-tool doorlock set-user OperationType UserIndex UserName UserUniqueId UserStatus UserType CredentialRule node-id/group-id + ./out/chip-tool doorlock set-user 0 1 "mike" 5 1 0 0 1 1 --timedInteractionTimeoutMs 1000 + ``` + + Set a credential: + ``` + ./out/chip-tool doorlock set-credential OperationType Credential CredentialData UserIndex UserStatus UserType node-id/group-id + ./out/chip-tool doorlock set-credential 0 '{ "credentialType": 1, "credentialIndex": 1 }' "123456" 1 null null 1 1 --timedInteractionTimeoutMs 1000 + ``` + + Changing a credential: + ``` + ./out/chip-tool doorlock set-credential OperationType Credential CredentialData UserIndex UserStatus UserType node-id/group-id + ./out/chip-tool doorlock set-credential 2 '{ "credentialType": 1, "credentialIndex": 1 }' "123457" 1 null null 1 1 --timedInteractionTimeoutMs 1000 + ``` + + Get a user: + ``` + ./out/chip-tool doorlock get-user UserIndex node-id/group-id + ./out/chip-tool doorlock get-user 1 1 1 + ``` + + Unlock door: + ``` + ./out/chip-tool doorlock unlock-door node-id/group-id + ./out/chip-tool doorlock unlock-door 1 1 --timedInteractionTimeoutMs 1000 + ``` + + Lock door: + ``` + ./out/chip-tool doorlock lock-door node-id/group-id + ./out/chip-tool doorlock lock-door 1 1 --timedInteractionTimeoutMs 1000 + ``` + +### Notes + +- Depending on your network settings your router might not provide native ipv6 + addresses to your devices (Border router / PC). If this is the case, you + need to add a static ipv6 addresses on both device and then an ipv6 route to + the border router on your PC + +#On Border Router: \$ sudo ip addr add dev 2002::2/64 + +#On PC(Linux): \$ sudo ip addr add dev 2002::1/64 + +#Add Ipv6 route on PC(Linux) \$ sudo ip route add /64 +via 2002::2 + +## Memory settings + +While most of the RAM usage in CHIP is static, allowing easier debugging and +optimization with symbols analysis, we still need some HEAP for the crypto and +OpenThread. Size of the HEAP can be modified by changing the value of the +`configTOTAL_HEAP_SIZE` define inside of the FreeRTOSConfig.h file of this +example. Please take note that a HEAP size smaller than 13k can and will cause a +Mbedtls failure during the BLE rendez-vous or CASE session + +To track memory usage you can set `enable_heap_monitoring = true` either in the +BUILD.gn file or pass it as a build argument to gn. This will print on the RTT +console the RAM usage of each individual task and the number of Memory +allocation and Free. While this is not extensive monitoring you're welcome to +modify `examples/platform/efr32/MemMonitoring.cpp` to add your own memory +tracking code inside the `trackAlloc` and `trackFree` function + +## OTA Software Update + +For the description of Software Update process with EFR32 example applications +see +[EFR32 OTA Software Update](../../../docs/guides/silabs_efr32_software_update.md) + +## Building options + +All of Silabs's examples within the Matter repo have all the features enabled by +default, as to provide the best end user experience. However some of those +features can easily be toggled on or off. Here is a short list of options : + +### Disabling logging + +`chip_progress_logging, chip_detail_logging, chip_automation_logging` + + ``` + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32 ./out/lighting-app BRD4164A "chip_detail_logging=false chip_automation_logging=false chip_progress_logging=false" + ``` + +### Debug build / release build + +`is_debug` + + ``` + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32 ./out/lighting-app BRD4164A "is_debug=false" + ``` + +### Disabling LCD + +`show_qr_code` + + ``` + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32 ./out/lighting-app BRD4164A "show_qr_code=false" + ``` + +### KVS maximum entry count + +`kvs_max_entries` + + ``` + Set the maximum Kvs entries that can be stored in NVM (Default 75) + Thresholds: 30 <= kvs_max_entries <= 255 + + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32 ./out/lighting-app BRD4164A kvs_max_entries=50 + ``` diff --git a/examples/lock-app/silabs/SiWx917/args.gni b/examples/lock-app/silabs/SiWx917/args.gni new file mode 100644 index 00000000000000..421090dbc97aae --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/args.gni @@ -0,0 +1,24 @@ +# Copyright (c) 2020 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. + +import("//build_overrides/chip.gni") +import("${chip_root}/config/standalone/args.gni") +import("${chip_root}/src/platform/silabs/SiWx917/args.gni") + +efr32_sdk_target = get_label_info(":sdk", "label_no_toolchain") + +chip_enable_ota_requestor = true +chip_enable_openthread = true +openthread_external_platform = + "${chip_root}/third_party/openthread/platforms/efr32:libopenthread-efr32" diff --git a/examples/lock-app/silabs/SiWx917/build_for_wifi_args.gni b/examples/lock-app/silabs/SiWx917/build_for_wifi_args.gni new file mode 100644 index 00000000000000..b1a0be42ca9351 --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/build_for_wifi_args.gni @@ -0,0 +1,21 @@ +# Copyright (c) 2020 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. +import("//build_overrides/chip.gni") +import("${chip_root}/config/standalone/args.gni") + +efr32_sdk_target = get_label_info(":sdk", "label_no_toolchain") +chip_enable_openthread = false +import("${chip_root}/src/platform/silabs/SiWx917/wifi_args.gni") + +chip_enable_ota_requestor = true diff --git a/examples/lock-app/silabs/SiWx917/build_for_wifi_gnfile.gn b/examples/lock-app/silabs/SiWx917/build_for_wifi_gnfile.gn new file mode 100644 index 00000000000000..d391814190d09f --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/build_for_wifi_gnfile.gn @@ -0,0 +1,28 @@ +# Copyright (c) 2020 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. + +import("//build_overrides/build.gni") + +# The location of the build configuration file. +buildconfig = "${build_root}/config/BUILDCONFIG.gn" + +# CHIP uses angle bracket includes. +check_system_includes = true + +default_args = { + target_cpu = "arm" + target_os = "freertos" + chip_enable_wifi = true + import("//build_for_wifi_args.gni") +} diff --git a/examples/lock-app/silabs/SiWx917/build_overrides b/examples/lock-app/silabs/SiWx917/build_overrides new file mode 120000 index 00000000000000..995884e6163eb5 --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/build_overrides @@ -0,0 +1 @@ +../../../build_overrides \ No newline at end of file diff --git a/examples/lock-app/silabs/SiWx917/include/AppConfig.h b/examples/lock-app/silabs/SiWx917/include/AppConfig.h new file mode 100644 index 00000000000000..3014c5be66479c --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/include/AppConfig.h @@ -0,0 +1,46 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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 + +// ---- Door lock Example App Config ---- + +#define APP_TASK_NAME "Lock" + +// Time it takes in ms for the simulated actuator to move from one +// state to another. +#define ACTUATOR_MOVEMENT_PERIOS_MS 10 + +// EFR Logging +#ifdef __cplusplus +extern "C" { +#endif + +void silabsInitLog(void); + +void efr32Log(const char * aFormat, ...); +#define SILABS_LOG(...) efr32Log(__VA_ARGS__); +void appError(int err); + +#ifdef __cplusplus +} + +#include +void appError(CHIP_ERROR error); +#endif diff --git a/examples/lock-app/silabs/SiWx917/include/AppEvent.h b/examples/lock-app/silabs/SiWx917/include/AppEvent.h new file mode 100644 index 00000000000000..7724687d3ef96c --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/include/AppEvent.h @@ -0,0 +1,55 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2018 Nest Labs, Inc. + * 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 + +struct AppEvent; +typedef void (*EventHandler)(AppEvent *); + +struct AppEvent +{ + enum AppEventTypes + { + kEventType_Button = 0, + kEventType_Timer, + kEventType_Lock, + kEventType_Install, + }; + + uint16_t Type; + + union + { + struct + { + uint8_t Action; + } ButtonEvent; + struct + { + void * Context; + } TimerEvent; + struct + { + uint8_t Action; + int32_t Actor; + } LockEvent; + }; + + EventHandler Handler; +}; diff --git a/examples/lock-app/silabs/SiWx917/include/AppTask.h b/examples/lock-app/silabs/SiWx917/include/AppTask.h new file mode 100644 index 00000000000000..e6b88fcf127766 --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/include/AppTask.h @@ -0,0 +1,187 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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 + +/********************************************************** + * Includes + *********************************************************/ + +#include +#include + +#include "AppEvent.h" +#include "BaseApplication.h" +#include "FreeRTOS.h" +#include "LockManager.h" +//#include "sl_simple_button_instances.h" +#include "timers.h" // provides FreeRTOS timer support +#include +#include +#include +#include + +/********************************************************** + * Defines + *********************************************************/ +#define SL_SIMPLE_BUTTON_MODE_POLL 0U ///< BUTTON input capture using polling +#define SL_SIMPLE_BUTTON_MODE_POLL_AND_DEBOUNCE 1U ///< BUTTON input capture using polling and debouncing +#define SL_SIMPLE_BUTTON_MODE_INTERRUPT 2U ///< BUTTON input capture using interrupt + +#define SL_SIMPLE_BUTTON_DISABLED 2U ///< BUTTON state is disabled +#define SL_SIMPLE_BUTTON_PRESSED 1U ///< BUTTON state is pressed +#define SL_SIMPLE_BUTTON_RELEASED 0U ///< BUTTON state is released + +typedef uint8_t sl_button_mode_t; ///< BUTTON mode +typedef uint8_t sl_button_state_t; ///< BUTTON state +typedef struct sl_button sl_button_t; + +/// A BUTTON instance +typedef struct sl_button +{ + void * context; ///< The context for this BUTTON instance + void (*init)(const sl_button_t * handle); ///< Member function to initialize BUTTON instance + void (*poll)(const sl_button_t * handle); ///< Member function to poll BUTTON + void (*enable)(const sl_button_t * handle); ///< Member function to enable BUTTON + void (*disable)(const sl_button_t * handle); ///< Member function to disable BUTTON + sl_button_state_t (*get_state)(const sl_button_t * handle); ///< Member function to retrieve BUTTON state +} sl_button; + +const sl_button_t sl_button_btn0 = { + .context = NULL, + .init = NULL, + .poll = NULL, + .enable = NULL, + .disable = NULL, + .get_state = NULL, +}; +#define APP_FUNCTION_BUTTON &sl_button_btn0 + +const sl_button_t sl_button_btn1 = { + .context = NULL, + .init = NULL, + .poll = NULL, + .enable = NULL, + .disable = NULL, + .get_state = NULL, +}; +#define APP_LIGHT_SWITCH &sl_button_btn1 + +// Application-defined error codes in the CHIP_ERROR space. +#define APP_ERROR_EVENT_QUEUE_FAILED CHIP_APPLICATION_ERROR(0x01) +#define APP_ERROR_CREATE_TASK_FAILED CHIP_APPLICATION_ERROR(0x02) +#define APP_ERROR_UNHANDLED_EVENT CHIP_APPLICATION_ERROR(0x03) +#define APP_ERROR_CREATE_TIMER_FAILED CHIP_APPLICATION_ERROR(0x04) +#define APP_ERROR_START_TIMER_FAILED CHIP_APPLICATION_ERROR(0x05) +#define APP_ERROR_STOP_TIMER_FAILED CHIP_APPLICATION_ERROR(0x06) +#define APP_ERROR_ALLOCATION_FAILED CHIP_APPLICATION_ERROR(0x07) +#if defined(ENABLE_CHIP_SHELL) +#define APP_ERROR_TOO_MANY_SHELL_ARGUMENTS CHIP_APPLICATION_ERROR(0x08) +#endif // ENABLE_CHIP_SHELL + +/********************************************************** + * AppTask Declaration + *********************************************************/ + +class AppTask : public BaseApplication +{ + +public: + AppTask() = default; + + static AppTask & GetAppTask() { return sAppTask; } + + /** + * @brief AppTask task main loop function + * + * @param pvParameter FreeRTOS task parameter + */ + static void AppTaskMain(void * pvParameter); + + CHIP_ERROR StartAppTask(); + + void ActionRequest(int32_t aActor, LockManager::Action_t aAction); + static void ActionInitiated(LockManager::Action_t aAction, int32_t aActor); + static void ActionCompleted(LockManager::Action_t aAction); + + /** + * @brief Event handler when a button is pressed + * Function posts an event for button processing + * + * @param buttonHandle APP_LIGHT_SWITCH or APP_FUNCTION_BUTTON + * @param btnAction button action - SL_SIMPLE_BUTTON_PRESSED, + * SL_SIMPLE_BUTTON_RELEASED or SL_SIMPLE_BUTTON_DISABLED + */ + void ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAction); + + /** + * @brief Callback called by the identify-server when an identify command is received + * + * @param identify identify structure the command applies on + */ + static void OnIdentifyStart(Identify * identify); + + /** + * @brief Callback called by the identify-server when an identify command is stopped or finished + * + * @param identify identify structure the command applies on + */ + static void OnIdentifyStop(Identify * identify); + +private: + static AppTask sAppTask; + + /** + * @brief AppTask initialisation function + * + * @return CHIP_ERROR + */ + CHIP_ERROR Init(); + + /** + * @brief PB0 Button event processing function + * Press and hold will trigger a factory reset timer start + * Press and release will restart BLEAdvertising if not commisionned + * + * @param aEvent button event being processed + */ + static void ButtonHandler(AppEvent * aEvent); + + /** + * @brief PB1 Button event processing function + * Function triggers a switch action sent to the CHIP task + * + * @param aEvent button event being processed + */ + static void SwitchActionEventHandler(AppEvent * aEvent); + + /** + * @brief Update Cluster State + * + * @param context current context + */ + static void UpdateClusterState(intptr_t context); + + /** + * @brief Handle lock update event + * + * @param aEvent event received + */ + static void LockActionEventHandler(AppEvent * aEvent); +}; diff --git a/examples/lock-app/silabs/SiWx917/include/CHIPProjectConfig.h b/examples/lock-app/silabs/SiWx917/include/CHIPProjectConfig.h new file mode 100644 index 00000000000000..9793e98ac9bc0a --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/include/CHIPProjectConfig.h @@ -0,0 +1,123 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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. +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE 20202021 +#endif + +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR 0xF00 +#endif + +// For convenience, Chip Security Test Mode can be enabled and the +// requirement for authentication in various protocols can be disabled. +// +// WARNING: These options make it possible to circumvent basic Chip security functionality, +// including message encryption. Because of this they MUST NEVER BE ENABLED IN PRODUCTION BUILDS. +// +#define CHIP_CONFIG_SECURITY_TEST_MODE 0 +#define CHIP_CONFIG_REQUIRE_AUTH 1 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID + * + * 0xFFF1: Test vendor + */ +#define CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID 0xFFF1 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID + * + * 0x8006: example lock app + */ +#define CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID 0x8006 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_HARDWARE_VERSION + * + * The hardware version number assigned to device or product by the device vendor. This + * number is scoped to the device product id, and typically corresponds to a revision of the + * physical device, a change to its packaging, and/or a change to its marketing presentation. + * This value is generally *not* incremented for device software versions. + */ +#define CHIP_DEVICE_CONFIG_DEVICE_HARDWARE_VERSION 1 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION + * + * A uint32_t identifying the software version running on the device. + */ +/* The SoftwareVersion attribute of the Basic cluster. */ +#ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x0001 +#endif + +/** + * CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE + * + * Enable support for Chip-over-BLE (CHIPoBLE). + */ +#define CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE 1 + +/** + * CHIP_DEVICE_CONFIG_TEST_SERIAL_NUMBER + * + * Enables the use of a hard-coded default serial number if none + * is found in Chip NV storage. + */ +#define CHIP_DEVICE_CONFIG_TEST_SERIAL_NUMBER "TEST_SN" + +/** + * CHIP_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS + * + * Enable recording UTC timestamps. + */ +#define CHIP_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS 1 + +/** + * CHIP_DEVICE_CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE + * + * A size, in bytes, of the individual debug event logging buffer. + */ +#define CHIP_DEVICE_CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE (512) + +/** + * @def CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL + * + * @brief + * Active retransmit interval, or time to wait before retransmission after + * subsequent failures in milliseconds. + * + * This is the default value, that might be adjusted by end device depending on its + * needs (e.g. sleeping period) using Service Discovery TXT record CRA key. + * + */ +#define CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL (2000_ms32) diff --git a/examples/lock-app/silabs/SiWx917/include/EventHandlerLibShell.h b/examples/lock-app/silabs/SiWx917/include/EventHandlerLibShell.h new file mode 100644 index 00000000000000..3ad9f11cbf2fdc --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/include/EventHandlerLibShell.h @@ -0,0 +1,40 @@ +/* + * + * Copyright (c) 2020 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. + */ +#pragma once + +#include + +class EventData +{ +public: + chip::EventId eventId; +}; + +class AlarmEventData : public EventData +{ +public: + DlAlarmCode alarmCode; +}; + +class DoorStateEventData : public EventData +{ +public: + DlDoorState doorState; +}; + +CHIP_ERROR RegisterLockEvents(); +void EventWorkerFunction(intptr_t context); diff --git a/examples/lock-app/silabs/SiWx917/include/LockManager.h b/examples/lock-app/silabs/SiWx917/include/LockManager.h new file mode 100644 index 00000000000000..b451921f804bc4 --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/include/LockManager.h @@ -0,0 +1,218 @@ +/* + * + * Copyright (c) 2019 Google LLC. + * 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 + +#include +#include + +#include "AppEvent.h" + +#include "FreeRTOS.h" +#include "timers.h" // provides FreeRTOS timer support + +#include + +struct WeekDaysScheduleInfo +{ + DlScheduleStatus status; + EmberAfPluginDoorLockWeekDaySchedule schedule; +}; + +struct YearDayScheduleInfo +{ + DlScheduleStatus status; + EmberAfPluginDoorLockYearDaySchedule schedule; +}; + +struct HolidayScheduleInfo +{ + DlScheduleStatus status; + EmberAfPluginDoorLockHolidaySchedule schedule; +}; + +namespace SI917DoorLock { +namespace ResourceRanges { +// Used to size arrays +static constexpr uint16_t kMaxUsers = 10; +static constexpr uint8_t kMaxCredentialsPerUser = 10; +static constexpr uint8_t kMaxWeekdaySchedulesPerUser = 10; +static constexpr uint8_t kMaxYeardaySchedulesPerUser = 10; +static constexpr uint8_t kMaxHolidaySchedules = 10; +static constexpr uint8_t kMaxCredentialSize = 8; + +static constexpr uint8_t kMaxCredentials = kMaxUsers * kMaxCredentialsPerUser; +} // namespace ResourceRanges + +namespace LockInitParams { + +struct LockParam +{ + // Read from zap attributes + uint16_t numberOfUsers = 0; + uint8_t numberOfCredentialsPerUser = 0; + uint8_t numberOfWeekdaySchedulesPerUser = 0; + uint8_t numberOfYeardaySchedulesPerUser = 0; + uint8_t numberOfHolidaySchedules = 0; +}; + +class ParamBuilder +{ +public: + ParamBuilder & SetNumberOfUsers(uint16_t numberOfUsers) + { + lockParam_.numberOfUsers = numberOfUsers; + return *this; + } + ParamBuilder & SetNumberOfCredentialsPerUser(uint8_t numberOfCredentialsPerUser) + { + lockParam_.numberOfCredentialsPerUser = numberOfCredentialsPerUser; + return *this; + } + ParamBuilder & SetNumberOfWeekdaySchedulesPerUser(uint8_t numberOfWeekdaySchedulesPerUser) + { + lockParam_.numberOfWeekdaySchedulesPerUser = numberOfWeekdaySchedulesPerUser; + return *this; + } + ParamBuilder & SetNumberOfYeardaySchedulesPerUser(uint8_t numberOfYeardaySchedulesPerUser) + { + lockParam_.numberOfYeardaySchedulesPerUser = numberOfYeardaySchedulesPerUser; + return *this; + } + ParamBuilder & SetNumberOfHolidaySchedules(uint8_t numberOfHolidaySchedules) + { + lockParam_.numberOfHolidaySchedules = numberOfHolidaySchedules; + return *this; + } + LockParam GetLockParam() { return lockParam_; } + +private: + LockParam lockParam_; +}; + +} // namespace LockInitParams +} // namespace SI917DoorLock + +using namespace ::chip; +using namespace SI917DoorLock::ResourceRanges; + +class LockManager +{ +public: + enum Action_t + { + LOCK_ACTION = 0, + UNLOCK_ACTION, + + INVALID_ACTION + } Action; + + enum State_t + { + kState_LockInitiated = 0, + kState_LockCompleted, + kState_UnlockInitiated, + kState_UnlockCompleted, + } State; + + CHIP_ERROR Init(chip::app::DataModel::Nullable state, + SI917DoorLock::LockInitParams::LockParam lockParam); + bool NextState(); + bool IsActionInProgress(); + bool InitiateAction(int32_t aActor, Action_t aAction); + + typedef void (*Callback_fn_initiated)(Action_t, int32_t aActor); + typedef void (*Callback_fn_completed)(Action_t); + void SetCallbacks(Callback_fn_initiated aActionInitiated_CB, Callback_fn_completed aActionCompleted_CB); + + bool Lock(chip::EndpointId endpointId, const Optional & pin, DlOperationError & err); + bool Unlock(chip::EndpointId endpointId, const Optional & pin, DlOperationError & err); + + bool GetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user); + bool SetUser(chip::EndpointId endpointId, uint16_t userIndex, chip::FabricIndex creator, chip::FabricIndex modifier, + const chip::CharSpan & userName, uint32_t uniqueId, DlUserStatus userStatus, DlUserType usertype, + DlCredentialRule credentialRule, const DlCredential * credentials, size_t totalCredentials); + + bool GetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, DlCredentialType credentialType, + EmberAfPluginDoorLockCredentialInfo & credential); + + bool SetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, chip::FabricIndex creator, chip::FabricIndex modifier, + DlCredentialStatus credentialStatus, DlCredentialType credentialType, const chip::ByteSpan & credentialData); + + DlStatus GetWeekdaySchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex, + EmberAfPluginDoorLockWeekDaySchedule & schedule); + + DlStatus SetWeekdaySchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex, DlScheduleStatus status, + DlDaysMaskMap daysMask, uint8_t startHour, uint8_t startMinute, uint8_t endHour, uint8_t endMinute); + + DlStatus GetYeardaySchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex, + EmberAfPluginDoorLockYearDaySchedule & schedule); + + DlStatus SetYeardaySchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex, DlScheduleStatus status, + uint32_t localStartTime, uint32_t localEndTime); + + DlStatus GetHolidaySchedule(chip::EndpointId endpointId, uint8_t holidayIndex, EmberAfPluginDoorLockHolidaySchedule & schedule); + + DlStatus SetHolidaySchedule(chip::EndpointId endpointId, uint8_t holidayIndex, DlScheduleStatus status, uint32_t localStartTime, + uint32_t localEndTime, DlOperatingMode operatingMode); + + bool IsValidUserIndex(uint16_t userIndex); + bool IsValidCredentialIndex(uint16_t credentialIndex, DlCredentialType type); + bool IsValidWeekdayScheduleIndex(uint8_t scheduleIndex); + bool IsValidYeardayScheduleIndex(uint8_t scheduleIndex); + bool IsValidHolidayScheduleIndex(uint8_t scheduleIndex); + + bool setLockState(chip::EndpointId endpointId, DlLockState lockState, const Optional & pin, + DlOperationError & err); + const char * lockStateToString(DlLockState lockState) const; + + bool ReadConfigValues(); + +private: + friend LockManager & LockMgr(); + State_t mState; + + Callback_fn_initiated mActionInitiated_CB; + Callback_fn_completed mActionCompleted_CB; + + void CancelTimer(void); + void StartTimer(uint32_t aTimeoutMs); + + static void TimerEventHandler(TimerHandle_t xTimer); + static void AutoLockTimerEventHandler(AppEvent * aEvent); + static void ActuatorMovementTimerEventHandler(AppEvent * aEvent); + + EmberAfPluginDoorLockUserInfo mLockUsers[kMaxUsers]; + EmberAfPluginDoorLockCredentialInfo mLockCredentials[kMaxCredentials]; + WeekDaysScheduleInfo mWeekdaySchedule[kMaxUsers][kMaxWeekdaySchedulesPerUser]; + YearDayScheduleInfo mYeardaySchedule[kMaxUsers][kMaxYeardaySchedulesPerUser]; + HolidayScheduleInfo mHolidaySchedule[kMaxHolidaySchedules]; + + char mUserNames[ArraySize(mLockUsers)][DOOR_LOCK_MAX_USER_NAME_SIZE]; + uint8_t mCredentialData[kMaxCredentials][kMaxCredentialSize]; + DlCredential mCredentials[kMaxUsers][kMaxCredentialsPerUser]; + + static LockManager sLock; + SI917DoorLock::LockInitParams::LockParam LockParams; +}; + +inline LockManager & LockMgr() +{ + return LockManager::sLock; +} diff --git a/examples/lock-app/silabs/SiWx917/src/AppTask.cpp b/examples/lock-app/silabs/SiWx917/src/AppTask.cpp new file mode 100644 index 00000000000000..c4878ee9ba8a7f --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/src/AppTask.cpp @@ -0,0 +1,440 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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" +#if defined(ENABLE_CHIP_SHELL) +#include "EventHandlerLibShell.h" +#endif // ENABLE_CHIP_SHELL + +#ifdef ENABLE_WSTK_LEDS +#include "LEDWidget.h" +#include "sl_simple_led_instances.h" +#endif // ENABLE_WSTK_LEDS + +#ifdef DISPLAY_ENABLED +#include "lcd.h" +#ifdef QR_CODE_ENABLED +#include "qrcodegen.h" +#endif // QR_CODE_ENABLED +#endif // DISPLAY_ENABLED + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +#ifdef ENABLE_WSTK_LEDS +#define SYSTEM_STATE_LED &sl_led_led0 +#define LOCK_STATE_LED &sl_led_led1 +#endif // ENABLE_WSTK_LEDS + +#define APP_FUNCTION_BUTTON &sl_button_btn0 +#define APP_LOCK_SWITCH &sl_button_btn1 + +using chip::app::Clusters::DoorLock::DlLockState; +using chip::app::Clusters::DoorLock::DlOperationError; +using chip::app::Clusters::DoorLock::DlOperationSource; + +using namespace chip; +using namespace ::chip::DeviceLayer; +using namespace ::chip::DeviceLayer::Internal; +using namespace SI917DoorLock::LockInitParams; + +namespace { +#ifdef ENABLE_WSTK_LEDS +LEDWidget sLockLED; +#endif // ENABLE_WSTK_LEDS + +EmberAfIdentifyEffectIdentifier sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; +} // namespace +/********************************************************** + * Identify Callbacks + *********************************************************/ + +namespace { +void OnTriggerIdentifyEffectCompleted(chip::System::Layer * systemLayer, void * appState) +{ + ChipLogProgress(Zcl, "Trigger Identify Complete"); + sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; + +#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 + AppTask::GetAppTask().StopStatusLEDTimer(); +#endif +} + +void OnTriggerIdentifyEffect(Identify * identify) +{ + sIdentifyEffect = identify->mCurrentEffectIdentifier; + + if (identify->mCurrentEffectIdentifier == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_CHANNEL_CHANGE) + { + ChipLogProgress(Zcl, "IDENTIFY_EFFECT_IDENTIFIER_CHANNEL_CHANGE - Not supported, use effect varriant %d", + identify->mEffectVariant); + sIdentifyEffect = static_cast(identify->mEffectVariant); + } + +#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 + AppTask::GetAppTask().StartStatusLEDTimer(); +#endif + + switch (sIdentifyEffect) + { + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BLINK: + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BREATHE: + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_OKAY: + (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(5), OnTriggerIdentifyEffectCompleted, + identify); + break; + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_FINISH_EFFECT: + (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify); + (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(1), OnTriggerIdentifyEffectCompleted, + identify); + break; + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT: + (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerIdentifyEffectCompleted, identify); + sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; + break; + default: + ChipLogProgress(Zcl, "No identifier effect"); + } +} + +Identify gIdentify = { + chip::EndpointId{ 1 }, + AppTask::GetAppTask().OnIdentifyStart, + AppTask::GetAppTask().OnIdentifyStop, + EMBER_ZCL_IDENTIFY_IDENTIFY_TYPE_VISIBLE_LED, + OnTriggerIdentifyEffect, +}; + +} // namespace + +using namespace chip::TLV; +using namespace ::chip::DeviceLayer; + +AppTask AppTask::sAppTask; + +CHIP_ERROR AppTask::Init() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + +#ifdef DISPLAY_ENABLED + GetLCD().Init((uint8_t *) "Lock-App", true); +#endif + + err = BaseApplication::Init(&gIdentify); + if (err != CHIP_NO_ERROR) + { + SILABS_LOG("BaseApplication::Init() failed"); + appError(err); + } + +#if defined(ENABLE_CHIP_SHELL) + err = RegisterLockEvents(); + if (err != CHIP_NO_ERROR) + { + SILABS_LOG("RegisterLockEvents() failed"); + appError(err); + } +#endif // ENABLE_CHIP_SHELL + + // Initial lock state + chip::app::DataModel::Nullable state; + chip::EndpointId endpointId{ 1 }; + chip::DeviceLayer::PlatformMgr().LockChipStack(); + chip::app::Clusters::DoorLock::Attributes::LockState::Get(endpointId, state); + + uint8_t numberOfCredentialsPerUser = 0; + if (!DoorLockServer::Instance().GetNumberOfCredentialsSupportedPerUser(endpointId, numberOfCredentialsPerUser)) + { + ChipLogError(Zcl, + "Unable to get number of credentials supported per user when initializing lock endpoint, defaulting to 5 " + "[endpointId=%d]", + endpointId); + numberOfCredentialsPerUser = 5; + } + + uint16_t numberOfUsers = 0; + if (!DoorLockServer::Instance().GetNumberOfUserSupported(endpointId, numberOfUsers)) + { + ChipLogError(Zcl, + "Unable to get number of supported users when initializing lock endpoint, defaulting to 10 [endpointId=%d]", + endpointId); + numberOfUsers = 10; + } + + uint8_t numberOfWeekdaySchedulesPerUser = 0; + if (!DoorLockServer::Instance().GetNumberOfWeekDaySchedulesPerUserSupported(endpointId, numberOfWeekdaySchedulesPerUser)) + { + ChipLogError( + Zcl, + "Unable to get number of supported weekday schedules when initializing lock endpoint, defaulting to 10 [endpointId=%d]", + endpointId); + numberOfWeekdaySchedulesPerUser = 10; + } + + uint8_t numberOfYeardaySchedulesPerUser = 0; + if (!DoorLockServer::Instance().GetNumberOfYearDaySchedulesPerUserSupported(endpointId, numberOfYeardaySchedulesPerUser)) + { + ChipLogError( + Zcl, + "Unable to get number of supported yearday schedules when initializing lock endpoint, defaulting to 10 [endpointId=%d]", + endpointId); + numberOfYeardaySchedulesPerUser = 10; + } + + uint8_t numberOfHolidaySchedules = 0; + if (!DoorLockServer::Instance().GetNumberOfHolidaySchedulesSupported(endpointId, numberOfHolidaySchedules)) + { + ChipLogError( + Zcl, + "Unable to get number of supported holiday schedules when initializing lock endpoint, defaulting to 10 [endpointId=%d]", + endpointId); + numberOfHolidaySchedules = 10; + } + + chip::DeviceLayer::PlatformMgr().UnlockChipStack(); + + err = LockMgr().Init(state, + ParamBuilder() + .SetNumberOfUsers(numberOfUsers) + .SetNumberOfCredentialsPerUser(numberOfCredentialsPerUser) + .SetNumberOfWeekdaySchedulesPerUser(numberOfWeekdaySchedulesPerUser) + .SetNumberOfYeardaySchedulesPerUser(numberOfYeardaySchedulesPerUser) + .SetNumberOfHolidaySchedules(numberOfHolidaySchedules) + .GetLockParam()); + + if (err != CHIP_NO_ERROR) + { + SILABS_LOG("LockMgr().Init() failed"); + appError(err); + } + + LockMgr().SetCallbacks(ActionInitiated, ActionCompleted); + +#ifdef ENABLE_WSTK_LEDS + // Initialize LEDs + sLockLED.Init(LOCK_STATE_LED); + sLockLED.Set(state.Value() == DlLockState::kUnlocked); +#endif // ENABLE_WSTK_LEDS + + chip::DeviceLayer::PlatformMgr().ScheduleWork(UpdateClusterState, reinterpret_cast(nullptr)); + + ConfigurationMgr().LogDeviceConfig(); + + return err; +} + +CHIP_ERROR AppTask::StartAppTask() +{ + return BaseApplication::StartAppTask(AppTaskMain); +} + +void AppTask::AppTaskMain(void * pvParameter) +{ + AppEvent event; + QueueHandle_t sAppEventQueue = *(static_cast(pvParameter)); + + CHIP_ERROR err = sAppTask.Init(); + if (err != CHIP_NO_ERROR) + { + SILABS_LOG("AppTask.Init() failed"); + appError(err); + } + +#if !(defined(CHIP_DEVICE_CONFIG_ENABLE_SED) && CHIP_DEVICE_CONFIG_ENABLE_SED) + sAppTask.StartStatusLEDTimer(); +#endif + + SILABS_LOG("App Task started"); + + // Users and credentials should be checked once from nvm flash on boot + LockMgr().ReadConfigValues(); + + while (true) + { + BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, portMAX_DELAY); + while (eventReceived == pdTRUE) + { + sAppTask.DispatchEvent(&event); + eventReceived = xQueueReceive(sAppEventQueue, &event, 0); + } + } +} + +void AppTask::OnIdentifyStart(Identify * identify) +{ + ChipLogProgress(Zcl, "onIdentifyStart"); + +#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 + sAppTask.StartStatusLEDTimer(); +#endif +} + +void AppTask::OnIdentifyStop(Identify * identify) +{ + ChipLogProgress(Zcl, "onIdentifyStop"); + +#if CHIP_DEVICE_CONFIG_ENABLE_SED == 1 + sAppTask.StopStatusLEDTimer(); +#endif +} + +void AppTask::LockActionEventHandler(AppEvent * aEvent) +{ + bool initiated = false; + LockManager::Action_t action; + int32_t actor; + CHIP_ERROR err = CHIP_NO_ERROR; + + if (aEvent->Type == AppEvent::kEventType_Lock) + { + action = static_cast(aEvent->LockEvent.Action); + actor = aEvent->LockEvent.Actor; + } + else if (aEvent->Type == AppEvent::kEventType_Button) + { + if (LockMgr().NextState() == true) + { + action = LockManager::LOCK_ACTION; + } + else + { + action = LockManager::UNLOCK_ACTION; + } + actor = AppEvent::kEventType_Button; + } + else + { + err = APP_ERROR_UNHANDLED_EVENT; + } + + if (err == CHIP_NO_ERROR) + { + initiated = LockMgr().InitiateAction(actor, action); + + if (!initiated) + { + SILABS_LOG("Action is already in progress or active."); + } + } +} + +void AppTask::ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAction) +{ + if (buttonHandle == NULL) + { + return; + } + + AppEvent button_event = {}; + button_event.Type = AppEvent::kEventType_Button; + button_event.ButtonEvent.Action = btnAction; + + SILABS_LOG("### Lock button #### "); + button_event.Handler = BaseApplication::ButtonHandler; + sAppTask.PostEvent(&button_event); +} + +void AppTask::ActionInitiated(LockManager::Action_t aAction, int32_t aActor) +{ + if (aAction == LockManager::UNLOCK_ACTION || aAction == LockManager::LOCK_ACTION) + { + bool locked = (aAction == LockManager::LOCK_ACTION); + SILABS_LOG("%s Action has been initiated", (locked) ? "Lock" : "Unlock"); +#ifdef ENABLE_WSTK_LEDS + sLockLED.Set(!locked); +#endif // ENABLE_WSTK_LEDS + +#ifdef DISPLAY_ENABLED + sAppTask.GetLCD().WriteDemoUI(locked); +#endif // DISPLAY_ENABLED + } + + if (aActor == AppEvent::kEventType_Button) + { + sAppTask.mSyncClusterToButtonAction = true; + } +} + +void AppTask::ActionCompleted(LockManager::Action_t aAction) +{ + // if the action has been completed by the lock, update the lock trait. + // Turn off the lock LED if in a LOCKED state OR + // Turn on the lock LED if in an UNLOCKED state. + if (aAction == LockManager::LOCK_ACTION) + { + SILABS_LOG("Lock Action has been completed") + } + else if (aAction == LockManager::UNLOCK_ACTION) + { + SILABS_LOG("Unlock Action has been completed") + } + + if (sAppTask.mSyncClusterToButtonAction) + { + chip::DeviceLayer::PlatformMgr().ScheduleWork(UpdateClusterState, reinterpret_cast(nullptr)); + sAppTask.mSyncClusterToButtonAction = false; + } +} + +void AppTask::ActionRequest(int32_t aActor, LockManager::Action_t aAction) +{ + AppEvent event; + event.Type = AppEvent::kEventType_Lock; + event.LockEvent.Actor = aActor; + event.LockEvent.Action = aAction; + event.Handler = LockActionEventHandler; + PostEvent(&event); +} + +void AppTask::UpdateClusterState(intptr_t context) +{ + bool unlocked = LockMgr().NextState(); + DlLockState newState = unlocked ? DlLockState::kUnlocked : DlLockState::kLocked; + + DlOperationSource source = DlOperationSource::kUnspecified; + + // write the new lock value + EmberAfStatus status = + DoorLockServer::Instance().SetLockState(1, newState, source) ? EMBER_ZCL_STATUS_SUCCESS : EMBER_ZCL_STATUS_FAILURE; + + if (status != EMBER_ZCL_STATUS_SUCCESS) + { + SILABS_LOG("ERR: updating lock state %x", status); + } +} diff --git a/examples/lock-app/silabs/SiWx917/src/EventHandlerLibShell.cpp b/examples/lock-app/silabs/SiWx917/src/EventHandlerLibShell.cpp new file mode 100644 index 00000000000000..02979f56238a23 --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/src/EventHandlerLibShell.cpp @@ -0,0 +1,211 @@ +/* + * + * Copyright (c) 2020 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. + */ + +#include "EventHandlerLibShell.h" +#include "AppTask.h" +#include "lib/shell/Engine.h" +#include "lib/shell/commands/Help.h" + +#include "app/server/Server.h" +#include "platform/CHIPDeviceLayer.h" +#include + +constexpr uint8_t lockEndpoint = 1; + +using namespace chip; +using namespace chip::app; +using namespace Clusters::DoorLock; +using Shell::Engine; +using Shell::shell_command_t; +using Shell::streamer_get; +using Shell::streamer_printf; + +Engine sShellDoorlockSubCommands; +Engine sShellDoorlockEventSubCommands; +Engine sShellDoorlockEventAlarmSubCommands; +Engine sShellDoorlockEventDoorStateSubCommands; + +/******************************************************** + * Doorlock shell functions + *********************************************************/ + +CHIP_ERROR DoorlockHelpHandler(int argc, char ** argv) +{ + sShellDoorlockSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); + return CHIP_NO_ERROR; +} + +CHIP_ERROR DoorlockCommandHandler(int argc, char ** argv) +{ + if (argc == 0) + { + return DoorlockHelpHandler(argc, argv); + } + + return sShellDoorlockSubCommands.ExecCommand(argc, argv); +} + +/******************************************************** + * Event shell functions + *********************************************************/ + +CHIP_ERROR EventHelpHandler(int argc, char ** argv) +{ + sShellDoorlockEventSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); + return CHIP_NO_ERROR; +} + +CHIP_ERROR EventDoorlockCommandHandler(int argc, char ** argv) +{ + if (argc == 0) + { + return EventHelpHandler(argc, argv); + } + + return sShellDoorlockEventSubCommands.ExecCommand(argc, argv); +} + +/******************************************************** + * Alarm shell functions + *********************************************************/ + +CHIP_ERROR AlarmHelpHandler(int argc, char ** argv) +{ + sShellDoorlockEventAlarmSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); + return CHIP_NO_ERROR; +} + +CHIP_ERROR AlarmEventHandler(int argc, char ** argv) +{ + + if (argc == 0) + { + return AlarmHelpHandler(argc, argv); + } + if (argc >= 2) + { + ChipLogError(Zcl, "Too many arguments provided to function %s, line %d", __func__, __LINE__); + return APP_ERROR_TOO_MANY_SHELL_ARGUMENTS; + } + + AlarmEventData * data = Platform::New(); + data->eventId = Events::DoorLockAlarm::Id; + data->alarmCode = static_cast(atoi(argv[0])); + + DeviceLayer::PlatformMgr().ScheduleWork(EventWorkerFunction, reinterpret_cast(data)); + + return CHIP_NO_ERROR; +} + +/******************************************************** + * Door state shell functions + *********************************************************/ + +CHIP_ERROR DoorStateHelpHandler(int argc, char ** argv) +{ + sShellDoorlockEventDoorStateSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); + return CHIP_NO_ERROR; +} + +CHIP_ERROR DoorStateEventHandler(int argc, char ** argv) +{ + + if (argc == 0) + { + return DoorStateHelpHandler(argc, argv); + } + if (argc >= 2) + { + ChipLogError(Zcl, "Too many arguments provided to function %s, line %d", __func__, __LINE__); + return APP_ERROR_TOO_MANY_SHELL_ARGUMENTS; + } + + DoorStateEventData * data = Platform::New(); + data->eventId = Events::DoorStateChange::Id; + data->doorState = static_cast(atoi(argv[0])); + + DeviceLayer::PlatformMgr().ScheduleWork(EventWorkerFunction, reinterpret_cast(data)); + + return CHIP_NO_ERROR; +} + +/** + * @brief configures lock matter shell + * + */ + +CHIP_ERROR RegisterLockEvents() +{ + static const shell_command_t sDoorlockSubCommands[] = { { &DoorlockHelpHandler, "help", "Usage: doorlock " }, + { &EventDoorlockCommandHandler, "event", + " Usage: doorlock event " } }; + + static const shell_command_t sDoorlockEventSubCommands[] = { + { &EventHelpHandler, "help", "Usage : doorlock event " }, + { &AlarmEventHandler, "lock-alarm", "Sends lock alarm event to lock app" }, + { &DoorStateEventHandler, "door-state-change", "Sends door state change event to lock app" } + }; + + static const shell_command_t sDoorlockEventAlarmSubCommands[] = { { &AlarmHelpHandler, "help", + "Usage : doorlock event lock-alarm AlarmCode" } }; + + static const shell_command_t sDoorlockEventDoorStateSubCommands[] = { + { &DoorStateHelpHandler, "help", "Usage : doorlock event door-state-change DoorState" } + }; + + static const shell_command_t sDoorLockCommand = { &DoorlockCommandHandler, "doorlock", + "doorlock commands. Usage: doorlock " }; + + sShellDoorlockEventAlarmSubCommands.RegisterCommands(sDoorlockEventAlarmSubCommands, ArraySize(sDoorlockEventAlarmSubCommands)); + + sShellDoorlockEventDoorStateSubCommands.RegisterCommands(sDoorlockEventDoorStateSubCommands, + ArraySize(sDoorlockEventDoorStateSubCommands)); + + sShellDoorlockEventSubCommands.RegisterCommands(sDoorlockEventSubCommands, ArraySize(sDoorlockEventSubCommands)); + sShellDoorlockSubCommands.RegisterCommands(sDoorlockSubCommands, ArraySize(sDoorlockSubCommands)); + + Engine::Root().RegisterCommands(&sDoorLockCommand, 1); + + return CHIP_NO_ERROR; +} + +void EventWorkerFunction(intptr_t context) +{ + VerifyOrReturn(context != 0, ChipLogError(NotSpecified, "EventWorkerFunction - Invalid work data")); + + EventData * data = reinterpret_cast(context); + + switch (data->eventId) + { + case Events::DoorLockAlarm::Id: { + AlarmEventData * alarmData = reinterpret_cast(context); + DoorLockServer::Instance().SendLockAlarmEvent(lockEndpoint, alarmData->alarmCode); + break; + } + + case Events::DoorStateChange::Id: { + DoorStateEventData * doorStateData = reinterpret_cast(context); + DoorLockServer::Instance().SetDoorState(lockEndpoint, doorStateData->doorState); + break; + } + + default: { + ChipLogError(Zcl, "Invalid Event Id %s, line %d", __func__, __LINE__); + break; + } + } +} diff --git a/examples/lock-app/silabs/SiWx917/src/LockManager.cpp b/examples/lock-app/silabs/SiWx917/src/LockManager.cpp new file mode 100644 index 00000000000000..0ef26ec0337b6b --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/src/LockManager.cpp @@ -0,0 +1,709 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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 "LockManager.h" + +#include "AppConfig.h" +#include "AppTask.h" +#include +#include +#include +#include + +LockManager LockManager::sLock; + +TimerHandle_t sLockTimer; + +using namespace ::chip::DeviceLayer::Internal; +using namespace SI917DoorLock::LockInitParams; + +CHIP_ERROR LockManager::Init(chip::app::DataModel::Nullable state, LockParam lockParam) +{ + + LockParams = lockParam; + + if (LockParams.numberOfUsers > kMaxUsers) + { + SILABS_LOG("Max number of users is greater than %d, the maximum amount of users currently supported on this platform", + kMaxUsers); + return APP_ERROR_ALLOCATION_FAILED; + } + + if (LockParams.numberOfCredentialsPerUser > kMaxCredentialsPerUser) + { + SILABS_LOG("Max number of credentials per user is greater than %d, the maximum amount of users currently supported on this " + "platform", + kMaxCredentialsPerUser); + return APP_ERROR_ALLOCATION_FAILED; + } + + if (LockParams.numberOfWeekdaySchedulesPerUser > kMaxWeekdaySchedulesPerUser) + { + SILABS_LOG( + "Max number of schedules is greater than %d, the maximum amount of schedules currently supported on this platform", + kMaxWeekdaySchedulesPerUser); + return APP_ERROR_ALLOCATION_FAILED; + } + + if (LockParams.numberOfYeardaySchedulesPerUser > kMaxYeardaySchedulesPerUser) + { + SILABS_LOG( + "Max number of schedules is greater than %d, the maximum amount of schedules currently supported on this platform", + kMaxYeardaySchedulesPerUser); + return APP_ERROR_ALLOCATION_FAILED; + } + + if (LockParams.numberOfHolidaySchedules > kMaxHolidaySchedules) + { + SILABS_LOG( + "Max number of schedules is greater than %d, the maximum amount of schedules currently supported on this platform", + kMaxHolidaySchedules); + return APP_ERROR_ALLOCATION_FAILED; + } + + // Create FreeRTOS sw timer for lock timer. + sLockTimer = xTimerCreate("lockTmr", // Just a text name, not used by the RTOS kernel + 1, // == default timer period (mS) + false, // no timer reload (==one-shot) + (void *) this, // init timer id = lock obj context + TimerEventHandler // timer callback handler + ); + + if (sLockTimer == NULL) + { + SILABS_LOG("sLockTimer timer create failed"); + return APP_ERROR_CREATE_TIMER_FAILED; + } + + if (state.Value() == DlLockState::kUnlocked) + mState = kState_UnlockCompleted; + else + mState = kState_LockCompleted; + + return CHIP_NO_ERROR; +} + +bool LockManager::IsValidUserIndex(uint16_t userIndex) +{ + return (userIndex < kMaxUsers); +} + +bool LockManager::IsValidCredentialIndex(uint16_t credentialIndex, DlCredentialType type) +{ + if (DlCredentialType::kProgrammingPIN == type) + { + return (0 == credentialIndex); // 0 is required index for Programming PIN + } + return (credentialIndex < kMaxCredentialsPerUser); +} + +bool LockManager::IsValidWeekdayScheduleIndex(uint8_t scheduleIndex) +{ + return (scheduleIndex < kMaxWeekdaySchedulesPerUser); +} + +bool LockManager::IsValidYeardayScheduleIndex(uint8_t scheduleIndex) +{ + return (scheduleIndex < kMaxYeardaySchedulesPerUser); +} + +bool LockManager::IsValidHolidayScheduleIndex(uint8_t scheduleIndex) +{ + return (scheduleIndex < kMaxHolidaySchedules); +} + +bool LockManager::ReadConfigValues() +{ + size_t outLen; + SILABSConfig::ReadConfigValueBin(SILABSConfig::kConfigKey_LockUser, reinterpret_cast(&mLockUsers), + sizeof(EmberAfPluginDoorLockUserInfo) * ArraySize(mLockUsers), outLen); + + SILABSConfig::ReadConfigValueBin(SILABSConfig::kConfigKey_Credential, reinterpret_cast(&mLockCredentials), + sizeof(EmberAfPluginDoorLockCredentialInfo) * ArraySize(mLockCredentials), outLen); + + SILABSConfig::ReadConfigValueBin(SILABSConfig::kConfigKey_LockUserName, reinterpret_cast(mUserNames), + sizeof(mUserNames), outLen); + + SILABSConfig::ReadConfigValueBin(SILABSConfig::kConfigKey_CredentialData, reinterpret_cast(mCredentialData), + sizeof(mCredentialData), outLen); + + SILABSConfig::ReadConfigValueBin(SILABSConfig::kConfigKey_UserCredentials, reinterpret_cast(mCredentials), + sizeof(DlCredential) * LockParams.numberOfUsers * LockParams.numberOfCredentialsPerUser, + outLen); + + SILABSConfig::ReadConfigValueBin(SILABSConfig::kConfigKey_WeekDaySchedules, reinterpret_cast(mWeekdaySchedule), + sizeof(EmberAfPluginDoorLockWeekDaySchedule) * LockParams.numberOfWeekdaySchedulesPerUser * + LockParams.numberOfUsers, + outLen); + + SILABSConfig::ReadConfigValueBin(SILABSConfig::kConfigKey_YearDaySchedules, reinterpret_cast(mYeardaySchedule), + sizeof(EmberAfPluginDoorLockYearDaySchedule) * LockParams.numberOfYeardaySchedulesPerUser * + LockParams.numberOfUsers, + outLen); + + SILABSConfig::ReadConfigValueBin(SILABSConfig::kConfigKey_HolidaySchedules, reinterpret_cast(&(mHolidaySchedule)), + sizeof(EmberAfPluginDoorLockHolidaySchedule) * LockParams.numberOfHolidaySchedules, outLen); + + return true; +} + +void LockManager::SetCallbacks(Callback_fn_initiated aActionInitiated_CB, Callback_fn_completed aActionCompleted_CB) +{ + mActionInitiated_CB = aActionInitiated_CB; + mActionCompleted_CB = aActionCompleted_CB; +} + +bool LockManager::IsActionInProgress() +{ + return (mState == kState_LockInitiated || mState == kState_UnlockInitiated); +} + +bool LockManager::NextState() +{ + return (mState == kState_UnlockCompleted); +} + +bool LockManager::InitiateAction(int32_t aActor, Action_t aAction) +{ + bool action_initiated = false; + State_t new_state; + + // Initiate Turn Lock/Unlock Action only when the previous one is complete. + if (mState == kState_LockCompleted && aAction == UNLOCK_ACTION) + { + action_initiated = true; + + new_state = kState_UnlockInitiated; + } + else if (mState == kState_UnlockCompleted && aAction == LOCK_ACTION) + { + action_initiated = true; + + new_state = kState_LockInitiated; + } + + if (action_initiated) + { + + StartTimer(ACTUATOR_MOVEMENT_PERIOS_MS); + + // Since the timer started successfully, update the state and trigger callback + mState = new_state; + + if (mActionInitiated_CB) + { + mActionInitiated_CB(aAction, aActor); + } + } + + return action_initiated; +} + +void LockManager::StartTimer(uint32_t aTimeoutMs) +{ + if (xTimerIsTimerActive(sLockTimer)) + { + SILABS_LOG("app timer already started!"); + CancelTimer(); + } + + // timer is not active, change its period to required value (== restart). + // FreeRTOS- Block for a maximum of 100 ticks if the change period command + // cannot immediately be sent to the timer command queue. + if (xTimerChangePeriod(sLockTimer, (aTimeoutMs / portTICK_PERIOD_MS), 100) != pdPASS) + { + SILABS_LOG("sLockTimer timer start() failed"); + appError(APP_ERROR_START_TIMER_FAILED); + } +} + +void LockManager::CancelTimer(void) +{ + if (xTimerStop(sLockTimer, 0) == pdFAIL) + { + SILABS_LOG("sLockTimer stop() failed"); + appError(APP_ERROR_STOP_TIMER_FAILED); + } +} + +void LockManager::TimerEventHandler(TimerHandle_t xTimer) +{ + // Get lock obj context from timer id. + LockManager * lock = static_cast(pvTimerGetTimerID(xTimer)); + + // The timer event handler will be called in the context of the timer task + // once sLockTimer expires. Post an event to apptask queue with the actual handler + // so that the event can be handled in the context of the apptask. + AppEvent event; + event.Type = AppEvent::kEventType_Timer; + event.TimerEvent.Context = lock; + event.Handler = ActuatorMovementTimerEventHandler; + AppTask::GetAppTask().PostEvent(&event); +} + +void LockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent) +{ + Action_t actionCompleted = INVALID_ACTION; + + LockManager * lock = static_cast(aEvent->TimerEvent.Context); + + if (lock->mState == kState_LockInitiated) + { + lock->mState = kState_LockCompleted; + actionCompleted = LOCK_ACTION; + } + else if (lock->mState == kState_UnlockInitiated) + { + lock->mState = kState_UnlockCompleted; + actionCompleted = UNLOCK_ACTION; + } + + if (actionCompleted != INVALID_ACTION) + { + if (lock->mActionCompleted_CB) + { + lock->mActionCompleted_CB(actionCompleted); + } + } +} + +bool LockManager::Lock(chip::EndpointId endpointId, const Optional & pin, DlOperationError & err) +{ + return setLockState(endpointId, DlLockState::kLocked, pin, err); +} + +bool LockManager::Unlock(chip::EndpointId endpointId, const Optional & pin, DlOperationError & err) +{ + return setLockState(endpointId, DlLockState::kUnlocked, pin, err); +} + +bool LockManager::GetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) +{ + VerifyOrReturnValue(userIndex > 0, false); // indices are one-indexed + + userIndex--; + + VerifyOrReturnValue(IsValidUserIndex(userIndex), false); + + ChipLogProgress(Zcl, "Door Lock App: LockManager::GetUser [endpoint=%d,userIndex=%hu]", endpointId, userIndex); + + const auto & userInDb = mLockUsers[userIndex]; + + user.userStatus = userInDb.userStatus; + if (DlUserStatus::kAvailable == user.userStatus) + { + ChipLogDetail(Zcl, "Found unoccupied user [endpoint=%d]", endpointId); + return true; + } + + user.userName = chip::CharSpan(userInDb.userName.data(), userInDb.userName.size()); + user.credentials = chip::Span(mCredentials[userIndex], userInDb.credentials.size()); + user.userUniqueId = userInDb.userUniqueId; + user.userType = userInDb.userType; + user.credentialRule = userInDb.credentialRule; + // So far there's no way to actually create the credential outside Matter, so here we always set the creation/modification + // source to Matter + user.creationSource = DlAssetSource::kMatterIM; + user.createdBy = userInDb.createdBy; + user.modificationSource = DlAssetSource::kMatterIM; + user.lastModifiedBy = userInDb.lastModifiedBy; + + ChipLogDetail(Zcl, + "Found occupied user " + "[endpoint=%d,name=\"%.*s\",credentialsCount=%u,uniqueId=%lx,type=%u,credentialRule=%u," + "createdBy=%d,lastModifiedBy=%d]", + endpointId, static_cast(user.userName.size()), user.userName.data(), user.credentials.size(), + user.userUniqueId, to_underlying(user.userType), to_underlying(user.credentialRule), user.createdBy, + user.lastModifiedBy); + + return true; +} + +bool LockManager::SetUser(chip::EndpointId endpointId, uint16_t userIndex, chip::FabricIndex creator, chip::FabricIndex modifier, + const chip::CharSpan & userName, uint32_t uniqueId, DlUserStatus userStatus, DlUserType usertype, + DlCredentialRule credentialRule, const DlCredential * credentials, size_t totalCredentials) +{ + ChipLogProgress(Zcl, + "Door Lock App: LockManager::SetUser " + "[endpoint=%d,userIndex=%d,creator=%d,modifier=%d,userName=%s,uniqueId=%ld " + "userStatus=%u,userType=%u,credentialRule=%u,credentials=%p,totalCredentials=%u]", + endpointId, userIndex, creator, modifier, userName.data(), uniqueId, to_underlying(userStatus), + to_underlying(usertype), to_underlying(credentialRule), credentials, totalCredentials); + + VerifyOrReturnValue(userIndex > 0, false); // indices are one-indexed + + userIndex--; + + VerifyOrReturnValue(IsValidUserIndex(userIndex), false); + + auto & userInStorage = mLockUsers[userIndex]; + + if (userName.size() > DOOR_LOCK_MAX_USER_NAME_SIZE) + { + ChipLogError(Zcl, "Cannot set user - user name is too long [endpoint=%d,index=%d]", endpointId, userIndex); + return false; + } + + if (totalCredentials > LockParams.numberOfCredentialsPerUser) + { + ChipLogError(Zcl, "Cannot set user - total number of credentials is too big [endpoint=%d,index=%d,totalCredentials=%u]", + endpointId, userIndex, totalCredentials); + return false; + } + + chip::Platform::CopyString(mUserNames[userIndex], userName); + userInStorage.userName = chip::CharSpan(mUserNames[userIndex], userName.size()); + userInStorage.userUniqueId = uniqueId; + userInStorage.userStatus = userStatus; + userInStorage.userType = usertype; + userInStorage.credentialRule = credentialRule; + userInStorage.lastModifiedBy = modifier; + userInStorage.createdBy = creator; + + for (size_t i = 0; i < totalCredentials; ++i) + { + mCredentials[userIndex][i] = credentials[i]; + mCredentials[userIndex][i].CredentialType = 1; + mCredentials[userIndex][i].CredentialIndex = i + 1; + } + + userInStorage.credentials = chip::Span(mCredentials[userIndex], totalCredentials); + + // Save user information in NVM flash + SILABSConfig::WriteConfigValueBin(SILABSConfig::kConfigKey_LockUser, reinterpret_cast(&mLockUsers), + sizeof(EmberAfPluginDoorLockUserInfo) * LockParams.numberOfUsers); + + SILABSConfig::WriteConfigValueBin(SILABSConfig::kConfigKey_UserCredentials, reinterpret_cast(mCredentials), + sizeof(DlCredential) * LockParams.numberOfUsers * LockParams.numberOfCredentialsPerUser); + + SILABSConfig::WriteConfigValueBin(SILABSConfig::kConfigKey_LockUserName, reinterpret_cast(mUserNames), + sizeof(mUserNames)); + + ChipLogProgress(Zcl, "Successfully set the user [mEndpointId=%d,index=%d]", endpointId, userIndex); + + return true; +} + +bool LockManager::GetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, DlCredentialType credentialType, + EmberAfPluginDoorLockCredentialInfo & credential) +{ + + if (DlCredentialType::kProgrammingPIN == credentialType) + { + VerifyOrReturnValue(IsValidCredentialIndex(credentialIndex, credentialType), + false); // programming pin index is only index allowed to contain 0 + } + else + { + VerifyOrReturnValue(IsValidCredentialIndex(--credentialIndex, credentialType), false); // otherwise, indices are one-indexed + } + + ChipLogProgress(Zcl, "Lock App: LockManager::GetCredential [credentialType=%u], credentialIndex=%d", + to_underlying(credentialType), credentialIndex); + + const auto & credentialInStorage = mLockCredentials[credentialIndex]; + + credential.status = credentialInStorage.status; + ChipLogDetail(Zcl, "CredentialStatus: %d, CredentialIndex: %d ", (int) credential.status, credentialIndex); + + if (DlCredentialStatus::kAvailable == credential.status) + { + ChipLogDetail(Zcl, "Found unoccupied credential "); + return true; + } + credential.credentialType = credentialInStorage.credentialType; + credential.credentialData = credentialInStorage.credentialData; + credential.createdBy = credentialInStorage.createdBy; + credential.lastModifiedBy = credentialInStorage.lastModifiedBy; + // So far there's no way to actually create the credential outside Matter, so here we always set the creation/modification + // source to Matter + credential.creationSource = DlAssetSource::kMatterIM; + credential.modificationSource = DlAssetSource::kMatterIM; + + ChipLogDetail(Zcl, "Found occupied credential [type=%u,dataSize=%u]", to_underlying(credential.credentialType), + credential.credentialData.size()); + + return true; +} + +bool LockManager::SetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, chip::FabricIndex creator, + chip::FabricIndex modifier, DlCredentialStatus credentialStatus, DlCredentialType credentialType, + const chip::ByteSpan & credentialData) +{ + + if (DlCredentialType::kProgrammingPIN == credentialType) + { + VerifyOrReturnValue(IsValidCredentialIndex(credentialIndex, credentialType), + false); // programming pin index is only index allowed to contain 0 + } + else + { + VerifyOrReturnValue(IsValidCredentialIndex(--credentialIndex, credentialType), false); // otherwise, indices are one-indexed + } + + ChipLogProgress(Zcl, + "Door Lock App: LockManager::SetCredential " + "[credentialStatus=%u,credentialType=%u,credentialDataSize=%u,creator=%d,modifier=%d]", + to_underlying(credentialStatus), to_underlying(credentialType), credentialData.size(), creator, modifier); + + auto & credentialInStorage = mLockCredentials[credentialIndex]; + + credentialInStorage.status = credentialStatus; + credentialInStorage.credentialType = credentialType; + credentialInStorage.createdBy = creator; + credentialInStorage.lastModifiedBy = modifier; + + memcpy(mCredentialData[credentialIndex], credentialData.data(), credentialData.size()); + credentialInStorage.credentialData = chip::ByteSpan{ mCredentialData[credentialIndex], credentialData.size() }; + + // Save credential information in NVM flash + SILABSConfig::WriteConfigValueBin(SILABSConfig::kConfigKey_Credential, reinterpret_cast(&mLockCredentials), + sizeof(EmberAfPluginDoorLockCredentialInfo) * LockParams.numberOfCredentialsPerUser); + + SILABSConfig::WriteConfigValueBin(SILABSConfig::kConfigKey_CredentialData, reinterpret_cast(&mCredentialData), + sizeof(mCredentialData)); + + ChipLogProgress(Zcl, "Successfully set the credential [credentialType=%u]", to_underlying(credentialType)); + + return true; +} + +DlStatus LockManager::GetWeekdaySchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex, + EmberAfPluginDoorLockWeekDaySchedule & schedule) +{ + + VerifyOrReturnValue(weekdayIndex > 0, DlStatus::kFailure); // indices are one-indexed + VerifyOrReturnValue(userIndex > 0, DlStatus::kFailure); // indices are one-indexed + + weekdayIndex--; + userIndex--; + + VerifyOrReturnValue(IsValidWeekdayScheduleIndex(weekdayIndex), DlStatus::kFailure); + VerifyOrReturnValue(IsValidUserIndex(userIndex), DlStatus::kFailure); + + const auto & scheduleInStorage = mWeekdaySchedule[userIndex][weekdayIndex]; + if (DlScheduleStatus::kAvailable == scheduleInStorage.status) + { + return DlStatus::kNotFound; + } + + schedule = scheduleInStorage.schedule; + + return DlStatus::kSuccess; +} + +DlStatus LockManager::SetWeekdaySchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex, + DlScheduleStatus status, DlDaysMaskMap daysMask, uint8_t startHour, uint8_t startMinute, + uint8_t endHour, uint8_t endMinute) +{ + + VerifyOrReturnValue(weekdayIndex > 0, DlStatus::kFailure); // indices are one-indexed + VerifyOrReturnValue(userIndex > 0, DlStatus::kFailure); // indices are one-indexed + + weekdayIndex--; + userIndex--; + + VerifyOrReturnValue(IsValidWeekdayScheduleIndex(weekdayIndex), DlStatus::kFailure); + VerifyOrReturnValue(IsValidUserIndex(userIndex), DlStatus::kFailure); + + auto & scheduleInStorage = mWeekdaySchedule[userIndex][weekdayIndex]; + + scheduleInStorage.schedule.daysMask = daysMask; + scheduleInStorage.schedule.startHour = startHour; + scheduleInStorage.schedule.startMinute = startMinute; + scheduleInStorage.schedule.endHour = endHour; + scheduleInStorage.schedule.endMinute = endMinute; + scheduleInStorage.status = status; + + // Save schedule information in NVM flash + SILABSConfig::WriteConfigValueBin( + SILABSConfig::kConfigKey_WeekDaySchedules, reinterpret_cast(mWeekdaySchedule), + sizeof(EmberAfPluginDoorLockWeekDaySchedule) * LockParams.numberOfWeekdaySchedulesPerUser * LockParams.numberOfUsers); + + return DlStatus::kSuccess; +} + +DlStatus LockManager::GetYeardaySchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex, + EmberAfPluginDoorLockYearDaySchedule & schedule) +{ + VerifyOrReturnValue(yearDayIndex > 0, DlStatus::kFailure); // indices are one-indexed + VerifyOrReturnValue(userIndex > 0, DlStatus::kFailure); // indices are one-indexed + + yearDayIndex--; + userIndex--; + + VerifyOrReturnValue(IsValidYeardayScheduleIndex(yearDayIndex), DlStatus::kFailure); + VerifyOrReturnValue(IsValidUserIndex(userIndex), DlStatus::kFailure); + + const auto & scheduleInStorage = mYeardaySchedule[userIndex][yearDayIndex]; + if (DlScheduleStatus::kAvailable == scheduleInStorage.status) + { + return DlStatus::kNotFound; + } + + schedule = scheduleInStorage.schedule; + + return DlStatus::kSuccess; +} + +DlStatus LockManager::SetYeardaySchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex, + DlScheduleStatus status, uint32_t localStartTime, uint32_t localEndTime) +{ + VerifyOrReturnValue(yearDayIndex > 0, DlStatus::kFailure); // indices are one-indexed + VerifyOrReturnValue(userIndex > 0, DlStatus::kFailure); // indices are one-indexed + + yearDayIndex--; + userIndex--; + + VerifyOrReturnValue(IsValidYeardayScheduleIndex(yearDayIndex), DlStatus::kFailure); + VerifyOrReturnValue(IsValidUserIndex(userIndex), DlStatus::kFailure); + + auto & scheduleInStorage = mYeardaySchedule[userIndex][yearDayIndex]; + + scheduleInStorage.schedule.localStartTime = localStartTime; + scheduleInStorage.schedule.localEndTime = localEndTime; + scheduleInStorage.status = status; + + // Save schedule information in NVM flash + SILABSConfig::WriteConfigValueBin( + SILABSConfig::kConfigKey_YearDaySchedules, reinterpret_cast(mYeardaySchedule), + sizeof(EmberAfPluginDoorLockYearDaySchedule) * LockParams.numberOfYeardaySchedulesPerUser * LockParams.numberOfUsers); + + return DlStatus::kSuccess; +} + +DlStatus LockManager::GetHolidaySchedule(chip::EndpointId endpointId, uint8_t holidayIndex, + EmberAfPluginDoorLockHolidaySchedule & schedule) +{ + VerifyOrReturnValue(holidayIndex > 0, DlStatus::kFailure); // indices are one-indexed + + holidayIndex--; + + VerifyOrReturnValue(IsValidHolidayScheduleIndex(holidayIndex), DlStatus::kFailure); + + const auto & scheduleInStorage = mHolidaySchedule[holidayIndex]; + if (DlScheduleStatus::kAvailable == scheduleInStorage.status) + { + return DlStatus::kNotFound; + } + + schedule = scheduleInStorage.schedule; + + return DlStatus::kSuccess; +} + +DlStatus LockManager::SetHolidaySchedule(chip::EndpointId endpointId, uint8_t holidayIndex, DlScheduleStatus status, + uint32_t localStartTime, uint32_t localEndTime, DlOperatingMode operatingMode) +{ + VerifyOrReturnValue(holidayIndex > 0, DlStatus::kFailure); // indices are one-indexed + + holidayIndex--; + + VerifyOrReturnValue(IsValidHolidayScheduleIndex(holidayIndex), DlStatus::kFailure); + + auto & scheduleInStorage = mHolidaySchedule[holidayIndex]; + + scheduleInStorage.schedule.localStartTime = localStartTime; + scheduleInStorage.schedule.localEndTime = localEndTime; + scheduleInStorage.schedule.operatingMode = operatingMode; + scheduleInStorage.status = status; + + // Save schedule information in NVM flash + SILABSConfig::WriteConfigValueBin(SILABSConfig::kConfigKey_HolidaySchedules, + reinterpret_cast(&(mHolidaySchedule)), + sizeof(EmberAfPluginDoorLockHolidaySchedule) * LockParams.numberOfHolidaySchedules); + + return DlStatus::kSuccess; +} + +const char * LockManager::lockStateToString(DlLockState lockState) const +{ + switch (lockState) + { + case DlLockState::kNotFullyLocked: + return "Not Fully Locked"; + case DlLockState::kLocked: + return "Locked"; + case DlLockState::kUnlocked: + return "Unlocked"; + case DlLockState::kUnknownEnumValue: + break; + } + + return "Unknown"; +} + +bool LockManager::setLockState(chip::EndpointId endpointId, DlLockState lockState, const Optional & pin, + DlOperationError & err) +{ + + // Assume pin is required until told otherwise + bool requirePin = true; + chip::app::Clusters::DoorLock::Attributes::RequirePINforRemoteOperation::Get(endpointId, &requirePin); + + // If a pin code is not given + if (!pin.HasValue()) + { + ChipLogDetail(Zcl, "Door Lock App: PIN code is not specified [endpointId=%d]", endpointId); + + // If a pin code is not required + if (!requirePin) + { + ChipLogDetail(Zcl, "Door Lock App: setting door lock state to \"%s\" [endpointId=%d]", lockStateToString(lockState), + endpointId); + + DoorLockServer::Instance().SetLockState(endpointId, lockState); + + return true; + } + + ChipLogError(Zcl, "Door Lock App: PIN code is not specified, but it is required [endpointId=%d]", endpointId); + + return false; + } + + // Check the PIN code + for (uint8_t i = 0; i < kMaxCredentials; i++) + { + if (mLockCredentials[i].credentialType != DlCredentialType::kPin || + mLockCredentials[i].status == DlCredentialStatus::kAvailable) + { + continue; + } + + if (mLockCredentials[i].credentialData.data_equal(pin.Value())) + { + ChipLogDetail(Zcl, + "Lock App: specified PIN code was found in the database, setting lock state to \"%s\" [endpointId=%d]", + lockStateToString(lockState), endpointId); + + DoorLockServer::Instance().SetLockState(endpointId, lockState); + + return true; + } + } + + ChipLogDetail(Zcl, + "Door Lock App: specified PIN code was not found in the database, ignoring command to set lock state to \"%s\" " + "[endpointId=%d]", + lockStateToString(lockState), endpointId); + + err = DlOperationError::kInvalidCredential; + return false; +} diff --git a/examples/lock-app/silabs/SiWx917/src/ZclCallbacks.cpp b/examples/lock-app/silabs/SiWx917/src/ZclCallbacks.cpp new file mode 100644 index 00000000000000..9c1812a90c0a04 --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/src/ZclCallbacks.cpp @@ -0,0 +1,156 @@ +/* + * + * Copyright (c) 2020 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 "AppConfig.h" +#include "LockManager.h" +#include + +#include +#include +#include +#include + +using namespace ::chip::app::Clusters; +using namespace ::chip::DeviceLayer::Internal; + +void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size, + uint8_t * value) +{ + ClusterId clusterId = attributePath.mClusterId; + AttributeId attributeId = attributePath.mAttributeId; + ChipLogProgress(Zcl, "Cluster callback: " ChipLogFormatMEI, ChipLogValueMEI(clusterId)); + + if (clusterId == DoorLock::Id && attributeId == DoorLock::Attributes::LockState::Id) + { + DoorLock::DlLockState lockState = *(reinterpret_cast(value)); + ChipLogProgress(Zcl, "Door lock cluster: " ChipLogFormatMEI " state %d", ChipLogValueMEI(clusterId), + to_underlying(lockState)); + } +} + +/** @brief DoorLock Cluster Init + * + * This function is called when a specific cluster is initialized. It gives the + * application an opportunity to take care of cluster initialization procedures. + * It is called exactly once for each endpoint where cluster is present. + * + * @param endpoint Ver.: always + * + */ +void emberAfDoorLockClusterInitCallback(EndpointId endpoint) {} + +bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Optional & pinCode, DlOperationError & err) +{ + ChipLogProgress(Zcl, "Door Lock App: Lock Command endpoint=%d", endpointId); + bool status = LockMgr().Lock(endpointId, pinCode, err); + if (status == true) + { + LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::LOCK_ACTION); + } + return status; +} + +bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Optional & pinCode, + DlOperationError & err) +{ + ChipLogProgress(Zcl, "Door Lock App: Unlock Command endpoint=%d", endpointId); + bool status = LockMgr().Unlock(endpointId, pinCode, err); + if (status == true) + { + LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLOCK_ACTION); + } + + return status; +} + +bool emberAfPluginDoorLockGetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, DlCredentialType credentialType, + EmberAfPluginDoorLockCredentialInfo & credential) +{ + return LockMgr().GetCredential(endpointId, credentialIndex, credentialType, credential); +} + +bool emberAfPluginDoorLockSetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, chip::FabricIndex creator, + chip::FabricIndex modifier, DlCredentialStatus credentialStatus, + DlCredentialType credentialType, const chip::ByteSpan & credentialData) +{ + return LockMgr().SetCredential(endpointId, credentialIndex, creator, modifier, credentialStatus, credentialType, + credentialData); +} + +bool emberAfPluginDoorLockGetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) +{ + return LockMgr().GetUser(endpointId, userIndex, user); +} + +bool emberAfPluginDoorLockSetUser(chip::EndpointId endpointId, uint16_t userIndex, chip::FabricIndex creator, + chip::FabricIndex modifier, const chip::CharSpan & userName, uint32_t uniqueId, + DlUserStatus userStatus, DlUserType usertype, DlCredentialRule credentialRule, + const DlCredential * credentials, size_t totalCredentials) +{ + + return LockMgr().SetUser(endpointId, userIndex, creator, modifier, userName, uniqueId, userStatus, usertype, credentialRule, + credentials, totalCredentials); +} + +DlStatus emberAfPluginDoorLockGetSchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex, + EmberAfPluginDoorLockWeekDaySchedule & schedule) +{ + return LockMgr().GetWeekdaySchedule(endpointId, weekdayIndex, userIndex, schedule); +} + +DlStatus emberAfPluginDoorLockGetSchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex, + EmberAfPluginDoorLockYearDaySchedule & schedule) +{ + return LockMgr().GetYeardaySchedule(endpointId, yearDayIndex, userIndex, schedule); +} + +DlStatus emberAfPluginDoorLockGetSchedule(chip::EndpointId endpointId, uint8_t holidayIndex, + EmberAfPluginDoorLockHolidaySchedule & holidaySchedule) +{ + return LockMgr().GetHolidaySchedule(endpointId, holidayIndex, holidaySchedule); +} + +DlStatus emberAfPluginDoorLockSetSchedule(chip::EndpointId endpointId, uint8_t weekdayIndex, uint16_t userIndex, + DlScheduleStatus status, DlDaysMaskMap daysMask, uint8_t startHour, uint8_t startMinute, + uint8_t endHour, uint8_t endMinute) +{ + return LockMgr().SetWeekdaySchedule(endpointId, weekdayIndex, userIndex, status, daysMask, startHour, startMinute, endHour, + endMinute); +} + +DlStatus emberAfPluginDoorLockSetSchedule(chip::EndpointId endpointId, uint8_t yearDayIndex, uint16_t userIndex, + DlScheduleStatus status, uint32_t localStartTime, uint32_t localEndTime) +{ + return LockMgr().SetYeardaySchedule(endpointId, yearDayIndex, userIndex, status, localStartTime, localEndTime); +} + +DlStatus emberAfPluginDoorLockSetSchedule(chip::EndpointId endpointId, uint8_t holidayIndex, DlScheduleStatus status, + uint32_t localStartTime, uint32_t localEndTime, DlOperatingMode operatingMode) +{ + return LockMgr().SetHolidaySchedule(endpointId, holidayIndex, status, localStartTime, localEndTime, operatingMode); +} + +void emberAfPluginDoorLockOnAutoRelock(chip::EndpointId endpointId) +{ + // Apply the relock state in the application control + LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::LOCK_ACTION); +} diff --git a/examples/lock-app/silabs/SiWx917/src/main.cpp b/examples/lock-app/silabs/SiWx917/src/main.cpp new file mode 100644 index 00000000000000..c3cbf82681bfdc --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/src/main.cpp @@ -0,0 +1,85 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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 + +#include "AppConfig.h" +#include "init_ccpPlatform.h" +#include +#include +#include +#include +#ifdef EFR32_ATTESTATION_CREDENTIALS +#include +#else +#include +#endif + +#define BLE_DEV_NAME "SiLabs-Door-Lock" +extern "C" void sl_button_on_change(); + +using namespace ::chip; +using namespace ::chip::Inet; +using namespace ::chip::DeviceLayer; +using namespace ::chip::Credentials; + +#define UNUSED_PARAMETER(a) (a = a) + +volatile int apperror_cnt; +static chip::DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider; + +// ================================================================================ +// Main Code +// ================================================================================ +int main(void) +{ + init_ccpPlatform(); + + if (SI917MatterConfig::InitMatter(BLE_DEV_NAME) != CHIP_NO_ERROR) + appError(CHIP_ERROR_INTERNAL); + + gExampleDeviceInfoProvider.SetStorageDelegate(&Server::GetInstance().GetPersistentStorage()); + chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); + + chip::DeviceLayer::PlatformMgr().LockChipStack(); + // Initialize device attestation config +#ifdef SI917_ATTESTATION_CREDENTIALS + SetDeviceAttestationCredentialsProvider(SI917::GetSI917DacProvider()); +#else + SetDeviceAttestationCredentialsProvider(Examples::GetExampleDACProvider()); +#endif + chip::DeviceLayer::PlatformMgr().UnlockChipStack(); + + SILABS_LOG("Starting App Task"); + if (AppTask::GetAppTask().StartAppTask() != CHIP_NO_ERROR) + appError(CHIP_ERROR_INTERNAL); + + SILABS_LOG("Starting FreeRTOS scheduler"); + vTaskStartScheduler(); + + // Should never get here. + chip::Platform::MemoryShutdown(); + SILABS_LOG("vTaskStartScheduler() failed"); + appError(CHIP_ERROR_INTERNAL); +} + +void sl_button_on_change() +{ + AppTask::GetAppTask().ButtonEventHandler(APP_FUNCTION_BUTTON, SL_SIMPLE_BUTTON_PRESSED); +} diff --git a/examples/lock-app/silabs/SiWx917/third_party/connectedhomeip b/examples/lock-app/silabs/SiWx917/third_party/connectedhomeip new file mode 120000 index 00000000000000..59307833b4fee9 --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/third_party/connectedhomeip @@ -0,0 +1 @@ +../../../../.. \ No newline at end of file diff --git a/examples/lock-app/silabs/SiWx917/with_pw_rpc.gni b/examples/lock-app/silabs/SiWx917/with_pw_rpc.gni new file mode 100644 index 00000000000000..faa281a6a4597c --- /dev/null +++ b/examples/lock-app/silabs/SiWx917/with_pw_rpc.gni @@ -0,0 +1,32 @@ +# Copyright (c) 2021 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. + +# add this gni as import in your build args to use pigweed in the example +# 'import("//with_pw_rpc.gni")' + +import("//build_overrides/chip.gni") +import("${chip_root}/config/efr32/lib/pw_rpc/pw_rpc.gni") +import("${chip_root}/examples/platform/silabs/efr32/args.gni") + +efr32_sdk_target = get_label_info(":sdk", "label_no_toolchain") + +chip_enable_pw_rpc = true +chip_enable_openthread = true +chip_openthread_ftd = true + +cpp_standard = "gnu++17" + +# To fit in flash +chip_detail_logging = false +show_qr_code = false diff --git a/examples/platform/silabs/SiWx917/matter_config.cpp b/examples/platform/silabs/SiWx917/matter_config.cpp index caa6a34b218482..e34c1fc180ed4f 100644 --- a/examples/platform/silabs/SiWx917/matter_config.cpp +++ b/examples/platform/silabs/SiWx917/matter_config.cpp @@ -57,7 +57,7 @@ using namespace ::chip::DeviceLayer; #include "SiWx917DeviceDataProvider.h" #if EFR32_OTA_ENABLED -void EFR32MatterConfig::InitOTARequestorHandler(System::Layer * systemLayer, void * appState) +void SI917MatterConfig::InitOTARequestorHandler(System::Layer * systemLayer, void * appState) { #if 0 // TODO : OTA is not planned now for CCP OTAConfig::Init(); diff --git a/examples/window-app/silabs/SiWx917/.gn b/examples/window-app/silabs/SiWx917/.gn new file mode 100644 index 00000000000000..4c078f6acefdcc --- /dev/null +++ b/examples/window-app/silabs/SiWx917/.gn @@ -0,0 +1,29 @@ +# Copyright (c) 2020 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. + +import("//build_overrides/build.gni") + +# The location of the build configuration file. +buildconfig = "${build_root}/config/BUILDCONFIG.gn" + +# CHIP uses angle bracket includes. +check_system_includes = true + +default_args = { + target_cpu = "arm" + target_os = "freertos" + chip_openthread_ftd = true + + import("//args.gni") +} diff --git a/examples/window-app/silabs/SiWx917/BUILD.gn b/examples/window-app/silabs/SiWx917/BUILD.gn new file mode 100644 index 00000000000000..5aea21172c38f0 --- /dev/null +++ b/examples/window-app/silabs/SiWx917/BUILD.gn @@ -0,0 +1,336 @@ +# Copyright (c) 2020 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") +import("//build_overrides/efr32_sdk.gni") +import("//build_overrides/pigweed.gni") + +import("${build_root}/config/defaults.gni") +import("${chip_root}/src/platform/device.gni") +import("${efr32_sdk_build_root}/SiWx917_sdk.gni") +import("${efr32_sdk_build_root}/efr32_executable.gni") + +assert(current_os == "freertos") + +project_dir = "${chip_root}/examples/window-app" +efr32_project_dir = "${project_dir}/silabs/SiWx917" +examples_plat_dir = "${chip_root}/examples/platform/silabs/SiWx917" +examples_common_plat_dir = "${chip_root}/examples/platform/silabs" + +declare_args() { + # Dump memory usage at link time. + chip_print_memory_usage = false + + # Monitor & log memory usage at runtime. + enable_heap_monitoring = false + + # Enable Sleepy end device + enable_sleepy_device = false + + # OTA timeout in seconds + OTA_periodic_query_timeout = 86400 + + # Wifi related stuff - they are overridden by gn -args="use_wf200=true" + use_wf200 = false + use_rs911x = false + use_rs911x_sockets = false + sl_wfx_config_softap = false + sl_wfx_config_scan = true + + # Disable LCD on supported devices + disable_lcd = true + + # Argument to Disable IPv4 for wifi(rs911) + chip_enable_wifi_ipv4 = false + + # Argument to force enable WPA3 security + rs91x_wpa3_only = false + + #default WiFi SSID + chip_default_wifi_ssid = "" + + #default Wifi Password + chip_default_wifi_psk = "" +} + +declare_args() { + # Enables LCD Qr Code on supported devices + show_qr_code = !disable_lcd +} + +# qr code cannot be true if lcd is disabled +assert(!(disable_lcd && show_qr_code)) + +# Sanity check +assert(!(chip_enable_wifi && chip_enable_openthread)) +assert(!(use_rs911x && chip_enable_openthread)) +assert(!(use_wf200 && chip_enable_openthread)) +if (chip_enable_wifi) { + assert(use_rs911x || use_wf200) + enable_openthread_cli = false + import("${chip_root}/src/platform/silabs/SiWx917/wifi_args.gni") +} + +# ThunderBoards, Explorer Kit and MGM240L do not support LCD (No LCD) +if (silabs_board == "BRD4166A" || silabs_board == "BRD2601B" || + silabs_board == "BRD2703A" || silabs_board == "BRD4319A") { + show_qr_code = false + disable_lcd = true +} + +defines = [] + +# WiFi settings +if (chip_enable_wifi) { + # disabling LCD for MG24 for wifi + if (silabs_board == "BRD4186C" || silabs_board == "BRD4187C") { + show_qr_code = false + disable_lcd = true + } + if (chip_default_wifi_ssid != "") { + defines += [ + "CHIP_ONNETWORK_PAIRING=1", + "CHIP_WIFI_SSID=\"${chip_default_wifi_ssid}\"", + ] + } + if (chip_default_wifi_psk != "") { + assert(chip_default_wifi_ssid != "", + "ssid can't be null if psk is provided") + defines += [ "CHIP_WIFI_PSK=\"${chip_default_wifi_psk}\"" ] + } + wifi_sdk_dir = "${chip_root}/src/platform/silabs/SiWx917/wifi" + efr32_lwip_defs = [ "LWIP_NETIF_API=1" ] + if (lwip_ipv4) { + efr32_lwip_defs += [ + "LWIP_IPV4=1", + + # adds following options to provide + # them to .cpp source files + # flags ported from lwipopts file + # TODO: move lwipopts to one location + "LWIP_ARP=1", + "LWIP_ICMP=1", + "LWIP_IGMP=1", + "LWIP_DHCP=1", + "LWIP_DNS=0", + ] + } else { + efr32_lwip_defs += [ "LWIP_IPV4=0" ] + } + if (lwip_ipv6) { + efr32_lwip_defs += [ "LWIP_IPV6=1" ] + } else { + efr32_lwip_defs += [ "LWIP_IPV6=0" ] + } + + wiseconnect_sdk_root = "${chip_root}/third_party/silabs/wisemcu-wifi-bt-sdk" + import("${examples_plat_dir}/SiWx917/rs911x.gni") +} + +efr32_sdk("sdk") { + sources = [ + "${efr32_project_dir}/include/CHIPProjectConfig.h", + "${examples_plat_dir}/FreeRTOSConfig.h", + ] + + include_dirs = [ + "${chip_root}/src/platform/silabs/SiWx917", + "${efr32_project_dir}/include", + "${examples_plat_dir}", + "${chip_root}/src/lib", + "${examples_common_plat_dir}", + ] + + defines += [ + "BOARD_ID=${silabs_board}", + "OTA_PERIODIC_TIMEOUT=${OTA_periodic_query_timeout}", + ] + + # WiFi Settings + if (chip_enable_wifi) { + if (use_rs911x) { + defines += rs911x_defs + include_dirs += rs911x_plat_incs + } else if (use_wf200) { + defines += wf200_defs + include_dirs += wf200_plat_incs + } + + if (use_rs911x_sockets) { + include_dirs += [ "${examples_plat_dir}/wifi/rsi-sockets" ] + defines += rs911x_sock_defs + } else { + # Using LWIP instead of the native TCP/IP stack + defines += efr32_lwip_defs + } + + if (sl_wfx_config_softap) { + defines += [ "SL_WFX_CONFIG_SOFTAP" ] + } + if (sl_wfx_config_scan) { + defines += [ "SL_WFX_CONFIG_SCAN" ] + } + } +} + +efr32_executable("window_app") { + output_name = "chip-efr32-window-example.out" + public_configs = [ "${efr32_sdk_build_root}:silabs_config" ] + output_dir = root_out_dir + include_dirs = [ + "include", + "${project_dir}/common/include", + ] + defines = [] + + sources = [ + "${examples_common_plat_dir}/heap_4_silabs.c", + "${examples_plat_dir}/init_ccpPlatform.cpp", + "${examples_plat_dir}/matter_config.cpp", + "${examples_plat_dir}/siwx917_utils.cpp", + "${project_dir}/common/src/WindowApp.cpp", + "${project_dir}/common/src/ZclCallbacks.cpp", + "src/WindowAppImpl.cpp", + "src/main.cpp", + ] + + if (use_wstk_leds) { + #sources += [ "${examples_plat_dir}/LEDWidget.cpp" ] + } + + if (chip_build_libshell || enable_openthread_cli || use_wf200 || use_rs911x) { + #sources += [ "${examples_plat_dir}/uart.cpp" ] + } + + deps = [ + ":sdk", + "${chip_root}/examples/providers:device_info_provider", + "${chip_root}/examples/window-app/common:window-common", + "${chip_root}/src/lib", + "${chip_root}/src/setup_payload", + ] + + # OpenThread Settings + if (chip_enable_openthread) { + deps += [ + "${chip_root}/third_party/openthread:openthread", + "${chip_root}/third_party/openthread:openthread-platform", + "${examples_plat_dir}:efr-matter-shell", + ] + } + + # if (chip_enable_ota_requestor) { + # defines += [ "EFR32_OTA_ENABLED" ] + # sources += [ "${examples_plat_dir}/OTAConfig.cpp" ] + # } + + # WiFi Settings + if (chip_enable_wifi) { + if (use_rs911x) { + sources += rs911x_src_plat + + # All the stuff from wiseconnect + sources += rs911x_src_sapi + + # Apparently - the rsi library needs this (though we may not use use it) + sources += rs911x_src_sock + include_dirs += rs911x_inc_plat + + if (use_rs911x_sockets) { + # + # Using native sockets inside RS911x + # + include_dirs += rs911x_sock_inc + } else { + # + # We use LWIP - not built-in sockets + # + sources += rs911x_src_lwip + } + } else if (use_wf200) { + sources += wf200_plat_src + include_dirs += wf200_plat_incs + } + + if (chip_enable_wifi_ipv4) { + defines += [ "CHIP_DEVICE_CONFIG_ENABLE_IPV4" ] + } + + if (rs91x_wpa3_only) { + # TODO: Change this macro once WF200 support is provided + defines += [ "WIFI_ENABLE_SECURITY_WPA3=1" ] + } + } + + if (!disable_lcd) { + sources += [ + "${examples_plat_dir}/display/demo-ui.c", + "${examples_plat_dir}/display/lcd.cpp", + "src/LcdPainter.cpp", + ] + include_dirs += [ "${examples_plat_dir}/display" ] + defines += [ "DISPLAY_ENABLED" ] + + if (show_qr_code) { + deps += [ "${chip_root}/examples/common/QRCode" ] + defines += [ "QR_CODE_ENABLED" ] + } + } + + if (enable_heap_monitoring) { + defines += [ "HEAP_MONITORING" ] + + sources += [ "${examples_common_plat_dir}/MemMonitoring.cpp" ] + } + + ldscript = "${examples_plat_dir}/ldscripts/${silabs_family}.ld" + + inputs = [ ldscript ] + + ldflags = [ "-T" + rebase_path(ldscript, root_build_dir) ] + + if (chip_print_memory_usage) { + ldflags += [ + "-Wl,--print-memory-usage", + "-fstack-usage", + ] + } + + # WiFi Settings + if (chip_enable_wifi) { + ldflags += [ + "-Wl,--defsym", + "-Wl,SILABS_WIFI=1", + ] + } + + # Attestation Credentials + if (chip_build_platform_attestation_credentials_provider) { + deps += [ "${examples_plat_dir}:SiWx917-attestation-credentials" ] + } + + # Factory Data Provider + if (use_efr32_factory_data_provider) { + deps += [ "${examples_plat_dir}:efr32-factory-data-provider" ] + } +} + +group("efr32") { + deps = [ ":window_app" ] +} + +group("default") { + deps = [ ":efr32" ] +} diff --git a/examples/window-app/silabs/SiWx917/README.md b/examples/window-app/silabs/SiWx917/README.md new file mode 100644 index 00000000000000..dda149285a8f91 --- /dev/null +++ b/examples/window-app/silabs/SiWx917/README.md @@ -0,0 +1,386 @@ +# Matter EFR32 Window Covering Example + +An example showing the use of CHIP on the Silicon Labs EFR32 MG12 and MG24. + +
+ +- [Matter EFR32 Window Covering Example](#matter-efr32-window-covering-example) + - [Introduction](#introduction) + - [Building](#building) + - [Flashing the Application](#flashing-the-application) + - [Viewing Logging Output](#viewing-logging-output) + - [Running the Complete Example](#running-the-complete-example) + - [Notes](#notes) + - [OTA Software Update](#ota-software-update) + - [Building options](#building-options) + - [Disabling logging](#disabling-logging) + - [Debug build / release build](#debug-build--release-build) + - [Disabling LCD](#disabling-lcd) + - [KVS maximum entry count](#kvs-maximum-entry-count) + +
+ +> **NOTE:** Silicon Laboratories now maintains a public matter GitHub repo with +> frequent releases thoroughly tested and validated. Developers looking to +> develop matter products with silabs hardware are encouraged to use our latest +> release with added tools and documentation. +> [Silabs Matter Github](https://github.com/SiliconLabs/matter/releases) + + + +## Introduction + +The EFR32 window-covering example provides a baseline demonstration of a Window +Covering device, built using Matter and the Silicon Labs gecko SDK. It can be +controlled by a Chip controller over an Openthread or Wifi network. + +The EFR32 device can be commissioned over Bluetooth Low Energy where the device +and the Chip controller will exchange security information with the Rendez-vous +procedure. In the case of Thread, the Thread Network credentials are provided to +the EFR32 device which will then join the Thread network. + +If the LCD is enabled, the LCD on the Silabs WSTK shows a QR Code containing the +needed commissioning information for the BLE connection and starting the +Rendez-vous procedure. Once the device is commissioned, the displays shows a +representation of the window covering state. + +The window-covering example is intended to serve both as a means to explore the +workings of Matter as well as a template for creating real products based on the +Silicon Labs platform. + + + +## Building + +- Download the + [Simplicity Commander](https://www.silabs.com/mcu/programming-options) + command line tool, and ensure that `commander` is your shell search path. + (For Mac OS X, `commander` is located inside + `Commander.app/Contents/MacOS/`.) + +- Download and install a suitable ARM gcc tool chain: + [GNU Arm Embedded Toolchain 9-2019-q4-major](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads) + +- Install some additional tools(likely already present for CHIP developers): + + # Linux + $ sudo apt-get install git ninja-build + + # Mac OS X + $ brew install ninja + +- Supported hardware: + + - > For the latest supported hardware please refer to the + > [Hardware Requirements](https://github.com/SiliconLabs/matter/blob/latest/docs/silabs/general/HARDWARE_REQUIREMENTS.md) + > in the Silicon Labs Matter Github Repo + + MG12 boards: + + - BRD4161A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@19dBm + - BRD4162A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4163A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm, + 868MHz@19dBm + - BRD4164A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@19dBm + - BRD4166A / SLTB004A / Thunderboard Sense 2 / 2.4GHz@10dBm + - BRD4170A / SLWSTK6000B / Multiband Wireless Starter Kit / 2.4GHz@19dBm, + 915MHz@19dBm + - BRD4304A / SLWSTK6000B / MGM12P Module / 2.4GHz@19dBm + + MG21 boards: Currently not supported due to RAM limitation. + + - BRD4180A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + + MG24 boards : + + - BRD2601B / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD2703A / SLWSTK6000B / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4186A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4186C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@10dBm + - BRD4187A / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + - BRD4187C / SLWSTK6006A / Wireless Starter Kit / 2.4GHz@20dBm + +* Build the example application: + + cd ~/connectedhomeip + ./scripts/examples/gn_efr32_example.sh ./examples/window-app/efr32/ ./out/window-app BRD4161A + +- To delete generated executable, libraries and object files use: + + $ cd ~/connectedhomeip + $ rm -rf ./out/ + + OR use GN/Ninja directly + + $ cd ~/connectedhomeip/examples/window-app/efr32 + $ git submodule update --init + $ source third_party/connectedhomeip/scripts/activate.sh + $ export EFR32_BOARD=BRD4161A + $ gn gen out/debug + $ ninja -C out/debug + +- To delete generated executable, libraries and object files use: + + $ cd ~/connectedhomeip/examples/window-app/efr32 + $ rm -rf out/ + +* Build the example as Sleepy End Device (SED) + + $ ./scripts/examples/gn_efr32_example.sh ./examples/window-app/efr32/ ./out/window-app_SED BRD4161A --sed + + or use gn as previously mentioned but adding the following arguments: + + $ gn gen out/debug '--args=silabs_board="BRD4161A" enable_sleepy_device=true chip_openthread_ftd=false' + +* Build the example with pigweed RCP + + $ ./scripts/examples/gn_efr32_example.sh examples/window-app/efr32/ out/window_app_rpc BRD4161A 'import("//with_pw_rpc.gni")' + + or use GN/Ninja Directly + + $ cd ~/connectedhomeip/examples/window-app/efr32 + $ git submodule update --init + $ source third_party/connectedhomeip/scripts/activate.sh + $ export EFR32_BOARD=BRD4161A + $ gn gen out/debug --args='import("//with_pw_rpc.gni")' + $ ninja -C out/debug + + [Running Pigweed RPC console](#running-pigweed-rpc-console) + +For more build options, help is provided when running the build script without +arguments + + ./scripts/examples/gn_efr32_example.sh + + + +## Flashing the Application + +- On the command line: + + $ cd ~/connectedhomeip/examples/window-app/efr32 + $ python3 out/debug/chip-efr32-window-example.flash.py + +- Or with the Ozone debugger, just load the .out file. + + + +## Viewing Logging Output + +The example application is built to use the SEGGER Real Time Transfer (RTT) +facility for log output. RTT is a feature built-in to the J-Link Interface MCU +on the WSTK development board. It allows bi-directional communication with an +embedded application without the need for a dedicated UART. + +Using the RTT facility requires downloading and installing the _SEGGER J-Link +Software and Documentation Pack_ +([web site](https://www.segger.com/downloads/jlink#J-LinkSoftwareAndDocumentationPack)). + +Alternatively, SEGGER Ozone J-Link debugger can be used to view RTT logs too +after flashing the .out file. + +- Download the J-Link installer by navigating to the appropriate URL and + agreeing to the license agreement. + +- [JLink_Linux_x86_64.deb](https://www.segger.com/downloads/jlink/JLink_Linux_x86_64.deb) +- [JLink_MacOSX.pkg](https://www.segger.com/downloads/jlink/JLink_MacOSX.pkg) + +* Install the J-Link software + + $ cd ~/Downloads + $ sudo dpkg -i JLink_Linux_V*_x86_64.deb + +* In Linux, grant the logged in user the ability to talk to the development + hardware via the linux tty device (/dev/ttyACMx) by adding them to the + dialout group. + + $ sudo usermod -a -G dialout ${USER} + +Once the above is complete, log output can be viewed using the JLinkExe tool in +combination with JLinkRTTClient as follows: + +- Run the JLinkExe tool with arguments to autoconnect to the WSTK board: + + For MG12 use: + + $ JLinkExe -device EFR32MG12PXXXF1024 -if JTAG -speed 4000 -autoconnect 1 + + For MG21 use: + + $ JLinkExe -device EFR32MG21AXXXF1024 -if SWD -speed 4000 -autoconnect 1 + +- In a second terminal, run the JLinkRTTClient to view logs: + + $ JLinkRTTClient + + + +## Running the Complete Example + +- It is assumed here that you already have an OpenThread border router + configured and running. If not see the following guide + [Openthread_border_router](https://github.com/project-chip/connectedhomeip/blob/master/docs/guides/openthread_border_router_pi.md) + for more information on how to setup a border router on a raspberryPi. + + Take note that the RCP code is available directly through + [Simplicity Studio 5](https://www.silabs.com/products/development-tools/software/simplicity-studio/simplicity-studio-5) + under File->New->Project Wizard->Examples->Thread : ot-rcp + +- User interface : **LCD** The LCD on Silabs WSTK shows a QR Code. This QR + Code is be scanned by the CHIP Tool app For the Rendez-vous procedure over + BLE + + * On devices that do not have or support the LCD Display like the BRD4166A Thunderboard Sense 2, + a URL can be found in the RTT logs. + + [SVR] Copy/paste the below URL in a browser to see the QR Code: + [SVR] https://project-chip.github.io/connectedhomeip/qrcode.html?data=CH%3AI34NM%20-00%200C9SS0 + + **LED 0** 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 1** Shows the state of the window covering + + - _Solid On_ ; The window cover if fully open + - _Off_ ; The window cover if fully closed + - _Blinking slowly_ ; The window cover is half-open, either by tilt, or lift + - _Blinking quickly_ ; The window cover is being automatically open or closed + + **Push Button 0** Increase either tilt or lift, and factory reset + + - Pressed and release: The lift/tilt increases by 10% + + - Pressed and hold for 6 s: Initiates the factory reset of the device. + Releasing the button within the 6-second window cancels the factory reset + procedure. **LEDs** blink in unison when the factory reset procedure is + initiated. + + **Push Button 1** Decreases either tilt or lift, or switch the cover type + + - Pressed and release: The lift/tilt decreases by 10% + + - Press and hold for 3 s: Cycle between window covering type (Rollershade, Drapery, Tilt Blind - Lift and Tilt). + + **Push Button0 and Button1** Switch between lift and tilt + + - Pressing and release both buttons at the same time: switches between lift and tilt modes. Most window covering types support either lift only, or tilt only, but type 0x08 support both (default) + + - Pressing and hold both buttons at the same time: Cycles between window covering 1, and window covering 2. + +* Once the device is provisioned, it will join the Thread network is + established, look for the RTT log + + ``` +     [DL] Device Role: CHILD +     [DL] Partition Id:0x6A7491B7 +     [DL] \_OnPlatformEvent default: event->Type = 32778 +     [DL] OpenThread State Changed (Flags: 0x00000001) +     [DL] Thread Unicast Addresses: +     [DL]    2001:DB8::E1A2:87F1:7D5D:FECA/64 valid preferred +     [DL]    FDDE:AD00:BEEF::FF:FE00:2402/64 valid preferred rloc +     [DL]    FDDE:AD00:BEEF:0:383F:5E81:A05A:B168/64 valid preferred +     [DL]    FE80::D8F2:592E:C109:CF00/64 valid preferred +     [DL] LwIP Thread interface addresses updated +     [DL] FE80::D8F2:592E:C109:CF00 IPv6 link-local address, preferred) +     [DL] FDDE:AD00:BEEF:0:383F:5E81:A05A:B168 Thread mesh-local address, preferred) +     [DL] 2001:DB8::E1A2:87F1:7D5D:FECA IPv6 global unicast address, preferred) + ``` + + (you can verify that the device is on the thread network with the command + `router table` using a serial terminal (screen / minicom etc.) on the board + running the window-app example. You can also get the address list with the + command ipaddr again in the serial terminal ) + + You can provision the Chip device using Chip tool Android or iOS app or + through CLI commands on your OT BR + + The + [CHIPTool](https://github.com/project-chip/connectedhomeip/blob/master/examples/chip-tool/README.md) + can now be used to send ZCL commands to the window covering device. For + instance, to set the window covering lift by percentage: + + ``` + chip-tool pairing ble-thread 1 hex: 20202021 3840 + + chip-tool onoff on 1 1 + + chip-tool windowcovering go-to-tilt-percentage 50 0 1 1 + ``` + + To see the supported window covering cluster commands, use: + + ``` + chip-tool windowcovering + ``` + +### Notes + +- Depending on your network settings your router might not provide native ipv6 + addresses to your devices (Border router / PC). If this is the case, you + need to add a static ipv6 addresses on both device and then an ipv6 route to + the border router on your PC + + # On Border Router : + $ sudo ip addr add dev 2002::2/64 + + # On PC (Linux) : + $ sudo ip addr add dev 2002::1/64 + + # Add Ipv6 route on PC (Linux) + $ sudo ip route add /64 via 2002::2 + + + +## OTA Software Update + +For the description of Software Update process with EFR32 example applications +see +[EFR32 OTA Software Update](../../../docs/guides/silabs_efr32_software_update.md) + +## Building options + +All of Silabs's examples within the Matter repo have all the features enabled by +default, as to provide the best end user experience. However some of those +features can easily be toggled on or off. Here is a short list of options : + +### Disabling logging + +chip_progress_logging, chip_detail_logging, chip_automation_logging + + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32 ./out/lighting-app BRD4164A "chip_detail_logging=false chip_automation_logging=false chip_progress_logging=false" + +### Debug build / release build + +is_debug + + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32 ./out/lighting-app BRD4164A "is_debug=false" + +### Disabling LCD + +show_qr_code + + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32 ./out/lighting-app BRD4164A "show_qr_code=false" + +### KVS maximum entry count + +kvs_max_entries + + Set the maximum Kvs entries that can be stored in NVM (Default 75) + Thresholds: 30 <= kvs_max_entries <= 255 + + $ ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32 ./out/lighting-app BRD4164A kvs_max_entries=50 diff --git a/examples/window-app/silabs/SiWx917/args.gni b/examples/window-app/silabs/SiWx917/args.gni new file mode 100644 index 00000000000000..421090dbc97aae --- /dev/null +++ b/examples/window-app/silabs/SiWx917/args.gni @@ -0,0 +1,24 @@ +# Copyright (c) 2020 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. + +import("//build_overrides/chip.gni") +import("${chip_root}/config/standalone/args.gni") +import("${chip_root}/src/platform/silabs/SiWx917/args.gni") + +efr32_sdk_target = get_label_info(":sdk", "label_no_toolchain") + +chip_enable_ota_requestor = true +chip_enable_openthread = true +openthread_external_platform = + "${chip_root}/third_party/openthread/platforms/efr32:libopenthread-efr32" diff --git a/examples/window-app/silabs/SiWx917/build_for_wifi_args.gni b/examples/window-app/silabs/SiWx917/build_for_wifi_args.gni new file mode 100644 index 00000000000000..770c3ab66ebb62 --- /dev/null +++ b/examples/window-app/silabs/SiWx917/build_for_wifi_args.gni @@ -0,0 +1,20 @@ +# Copyright (c) 2020 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. +import("//build_overrides/chip.gni") + +efr32_sdk_target = get_label_info(":sdk", "label_no_toolchain") +chip_enable_openthread = false +import("${chip_root}/src/platform/silabs/SiWx917/wifi_args.gni") + +chip_enable_ota_requestor = true diff --git a/examples/window-app/silabs/SiWx917/build_for_wifi_gnfile.gn b/examples/window-app/silabs/SiWx917/build_for_wifi_gnfile.gn new file mode 100644 index 00000000000000..d391814190d09f --- /dev/null +++ b/examples/window-app/silabs/SiWx917/build_for_wifi_gnfile.gn @@ -0,0 +1,28 @@ +# Copyright (c) 2020 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. + +import("//build_overrides/build.gni") + +# The location of the build configuration file. +buildconfig = "${build_root}/config/BUILDCONFIG.gn" + +# CHIP uses angle bracket includes. +check_system_includes = true + +default_args = { + target_cpu = "arm" + target_os = "freertos" + chip_enable_wifi = true + import("//build_for_wifi_args.gni") +} diff --git a/examples/window-app/silabs/SiWx917/build_overrides b/examples/window-app/silabs/SiWx917/build_overrides new file mode 120000 index 00000000000000..995884e6163eb5 --- /dev/null +++ b/examples/window-app/silabs/SiWx917/build_overrides @@ -0,0 +1 @@ +../../../build_overrides \ No newline at end of file diff --git a/examples/window-app/silabs/SiWx917/include/AppConfig.h b/examples/window-app/silabs/SiWx917/include/AppConfig.h new file mode 100644 index 00000000000000..661819aea7648b --- /dev/null +++ b/examples/window-app/silabs/SiWx917/include/AppConfig.h @@ -0,0 +1,70 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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 + +// ---- Window Example App Config ---- +#define APP_TASK_NAME "APP" + +// EFR Logging +#ifdef __cplusplus +extern "C" { +#endif + +#define LCD_SIZE 128 +#define LCD_MARGIN_SIZE 1 +#define LCD_BORDER_SIZE 2 +#define LCD_FRAME_SIZE (2 * LCD_MARGIN_SIZE + LCD_BORDER_SIZE) +#define LCD_COVER_SIZE (LCD_SIZE - 2 * LCD_FRAME_SIZE) +#define LIFT_OPEN_LIMIT 0 +#define LIFT_CLOSED_LIMIT (LCD_COVER_SIZE - 1) +#define LIFT_DELTA 1000 // 10% +#define TILT_OPEN_LIMIT 1 +#define TILT_CLOSED_LIMIT (LCD_COVER_SIZE / 10 - 1) +#define TILT_DELTA 1000 // 10% + +#define WINDOW_COVER_COUNT 2 + +#ifndef WINDOW_COVER_ENDPOINT1 +#define WINDOW_COVER_ENDPOINT1 1 +#endif + +#ifndef WINDOW_COVER_ENDPOINT2 +#define WINDOW_COVER_ENDPOINT2 2 +#endif + +#ifndef LONG_PRESS_TIMEOUT +#define LONG_PRESS_TIMEOUT 3000 +#endif + +#ifndef COVER_LIFT_TILT_TIMEOUT +#define COVER_LIFT_TILT_TIMEOUT 500 +#endif + +void silabsInitLog(void); + +void efr32Log(const char * aFormat, ...); +#define SILABS_LOG(...) efr32Log(__VA_ARGS__); + +#ifdef __cplusplus +} + +#include +void appError(CHIP_ERROR error); +#endif diff --git a/examples/window-app/silabs/SiWx917/include/CHIPProjectConfig.h b/examples/window-app/silabs/SiWx917/include/CHIPProjectConfig.h new file mode 100644 index 00000000000000..6a12dbc3794c8f --- /dev/null +++ b/examples/window-app/silabs/SiWx917/include/CHIPProjectConfig.h @@ -0,0 +1,153 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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. +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE 20202021 +#endif + +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR 0xF00 +#endif +// For convenience, Chip Security Test Mode can be enabled and the +// requirement for authentication in various protocols can be disabled. +// +// WARNING: These options make it possible to circumvent basic Chip security functionality, +// including message encryption. Because of this they MUST NEVER BE ENABLED IN PRODUCTION BUILDS. +// +#define CHIP_CONFIG_SECURITY_TEST_MODE 0 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID + * + * 0xFFF1: Test vendor. + */ +#define CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID 0xFFF1 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID + * + * 0x8009: example window app + */ +#define CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID 0x8010 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_HARDWARE_VERSION + * + * The hardware version number assigned to device or product by the device vendor. This + * number is scoped to the device product id, and typically corresponds to a revision of the + * physical device, a change to its packaging, and/or a change to its marketing presentation. + * This value is generally *not* incremented for device software versions. + */ +#define CHIP_DEVICE_CONFIG_DEVICE_HARDWARE_VERSION 1 + +/** + * CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION + * + * A uint32_t identifying the software version running on the device. + */ +/* The SoftwareVersion attribute of the Basic cluster. */ +#ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x0001 +#endif + +/** + * CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE + * + * Enable support for Chip-over-BLE (CHIPoBLE). + */ +#define CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE 1 + +/** + * CHIP_DEVICE_CONFIG_TEST_SERIAL_NUMBER + * + * Enables the use of a hard-coded default serial number if none + * is found in Chip NV storage. + */ +#define CHIP_DEVICE_CONFIG_TEST_SERIAL_NUMBER "TEST_SN" + +/** + * CHIP_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS + * + * Enable recording UTC timestamps. + */ +#define CHIP_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS 1 + +/** + * CHIP_DEVICE_CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE + * + * A size, in bytes, of the individual debug event logging buffer. + */ +#define CHIP_DEVICE_CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE (512) + +/** + * CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_INTERVAL + * + * The interval (in units of 0.625ms) at which the device will send BLE advertisements while + * in fast advertising mode. + * + * 40 (25ms). + */ +#define CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_INTERVAL 40 + +/** + * CHIP_DEVICE_CONFIG_BLE_SLOW_ADVERTISING_INTERVAL + * + * The interval (in units of 0.625ms) at which the device will send BLE advertisements while + * in slow advertisement mode. + * + * 800 (500ms). + */ +#define CHIP_DEVICE_CONFIG_BLE_SLOW_ADVERTISING_INTERVAL 800 + +/** + * CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_TIMEOUT + * + * The amount of time in miliseconds after which BLE should change his advertisements + * from fast interval to slow interval. + * + * 30000 (30 secondes). + */ +#define CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_TIMEOUT (30 * 1000) + +/** + * @def CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL + * + * @brief + * Active retransmit interval, or time to wait before retransmission after + * subsequent failures in milliseconds. + * + * This is the default value, that might be adjusted by end device depending on its + * needs (e.g. sleeping period) using Service Discovery TXT record CRA key. + * + */ +#define CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL (2000_ms32) + +#define CHIP_DEVICE_CONFIG_ENABLE_EXTENDED_DISCOVERY 1 diff --git a/examples/window-app/silabs/SiWx917/include/LcdPainter.h b/examples/window-app/silabs/SiWx917/include/LcdPainter.h new file mode 100644 index 00000000000000..4a30bdede2c3ea --- /dev/null +++ b/examples/window-app/silabs/SiWx917/include/LcdPainter.h @@ -0,0 +1,113 @@ +/* + * + * Copyright (c) 2019 Google LLC. + * 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 + +// clang-format off +#include "app-common/app-common/zap-generated/enums.h" +#include "app-common/app-common/zap-generated/cluster-enums.h" +// clang-format on + +#include + +#include + +enum class LcdIcon +{ + None = 0, + One, + Two, + Lift, + Tilt +}; + +class PixelPainter +{ +public: + PixelPainter(uint16_t lift, uint16_t tilt); + virtual ~PixelPainter() = default; + virtual uint8_t Color(uint32_t x, uint32_t y) = 0; + +protected: + uint16_t mLift; + uint16_t mTilt; +}; + +class CompositePainter : public PixelPainter +{ +public: + CompositePainter(uint16_t lift, uint16_t tilt, PixelPainter * painter1, PixelPainter * painter2, PixelPainter * painter3); + uint8_t Color(uint32_t x, uint32_t y); + +private: + PixelPainter * mPainter1; + PixelPainter * mPainter2; + PixelPainter * mPainter3; +}; + +class FramePainter : public PixelPainter +{ + using PixelPainter::PixelPainter; + uint8_t Color(uint32_t x, uint32_t y); +}; + +class IconPainter : public PixelPainter +{ +public: + IconPainter(uint16_t lift, uint16_t tilt, LcdIcon icon); + uint8_t Color(uint32_t x, uint32_t y); + +private: + uint8_t mIconSize; + uint8_t mIconOffset; + LcdIcon mIcon; +}; + +class VerticalShadePainter : public PixelPainter +{ +public: + using PixelPainter::PixelPainter; + uint8_t Color(uint32_t x, uint32_t y); +}; + +class HorizontalShadePainter : public PixelPainter +{ +public: + using PixelPainter::PixelPainter; + uint8_t Color(uint32_t x, uint32_t y); +}; + +class VerticalBlindPainter : public PixelPainter +{ +public: + VerticalBlindPainter(uint16_t lift, uint16_t tilt); + uint8_t Color(uint32_t x, uint32_t y); + +private: + constexpr static uint8_t sBandCount = 12; + uint8_t mBandSize; +}; + +class LcdPainter +{ +public: + static void Paint(SilabsLCD & lcd, chip::app::Clusters::WindowCovering::Type type, uint16_t lift, uint16_t tilt, LcdIcon icon); + +private: + static PixelPainter * GetCoverPainter(chip::app::Clusters::WindowCovering::Type type, uint16_t lift, uint16_t tilt); +}; diff --git a/examples/window-app/silabs/SiWx917/include/WindowAppImpl.h b/examples/window-app/silabs/SiWx917/include/WindowAppImpl.h new file mode 100644 index 00000000000000..86158e3d4da14b --- /dev/null +++ b/examples/window-app/silabs/SiWx917/include/WindowAppImpl.h @@ -0,0 +1,142 @@ +/* + * + * Copyright (c) 2021 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. + */ + +#pragma once + +#include + +#ifdef ENABLE_WSTK_LEDS +#include "LEDWidget.h" +#include "sl_simple_led_instances.h" +#endif // ENABLE_WSTK_LEDS + +#include +#include +#include + +#include +#include +#include +#ifdef DISPLAY_ENABLED +#include +#endif + +#define SL_SIMPLE_BUTTON_MODE_POLL 0U ///< BUTTON input capture using polling +#define SL_SIMPLE_BUTTON_MODE_POLL_AND_DEBOUNCE 1U ///< BUTTON input capture using polling and debouncing +#define SL_SIMPLE_BUTTON_MODE_INTERRUPT 2U ///< BUTTON input capture using interrupt + +#define SL_SIMPLE_BUTTON_DISABLED 2U ///< BUTTON state is disabled +#define SL_SIMPLE_BUTTON_PRESSED 1U ///< BUTTON state is pressed +#define SL_SIMPLE_BUTTON_RELEASED 0U ///< BUTTON state is released + +typedef uint8_t sl_button_mode_t; ///< BUTTON mode +typedef uint8_t sl_button_state_t; ///< BUTTON state +typedef struct sl_button sl_button_t; + +/// A BUTTON instance +typedef struct sl_button +{ + void * context; ///< The context for this BUTTON instance + void (*init)(const sl_button_t * handle); ///< Member function to initialize BUTTON instance + void (*poll)(const sl_button_t * handle); ///< Member function to poll BUTTON + void (*enable)(const sl_button_t * handle); ///< Member function to enable BUTTON + void (*disable)(const sl_button_t * handle); ///< Member function to disable BUTTON + sl_button_state_t (*get_state)(const sl_button_t * handle); ///< Member function to retrieve BUTTON state +} sl_button; + +const sl_button_t sl_button_btn0 = { + .context = NULL, + .init = NULL, + .poll = NULL, + .enable = NULL, + .disable = NULL, + .get_state = NULL, +}; +#define APP_FUNCTION_BUTTON &sl_button_btn0 + +const sl_button_t sl_button_btn1 = { + .context = NULL, + .init = NULL, + .poll = NULL, + .enable = NULL, + .disable = NULL, + .get_state = NULL, +}; +#define APP_LIGHT_SWITCH &sl_button_btn1 + +class WindowAppImpl : public WindowApp +{ +public: + static WindowAppImpl sInstance; + + WindowAppImpl(); + CHIP_ERROR Init() override; + CHIP_ERROR Start() override; + void Finish() override; + void PostEvent(const WindowApp::Event & event) override; + void PostAttributeChange(chip::EndpointId endpoint, chip::AttributeId attributeId) override; + friend void sl_button_on_change(const sl_button_t * handle); + void OnButtonChange(const sl_button_t * handle); + +protected: + struct Timer : public WindowApp::Timer + { + TimerHandle_t mHandler = nullptr; + + Timer(const char * name, uint32_t timeoutInMs, Callback callback, void * context); + void IsrStart(); + void Start() override; + void Stop() override; + + private: + static void TimerCallback(TimerHandle_t xTimer); + }; + + struct Button : public WindowApp::Button + { + Button(Button::Id id, const char * name); + }; + + WindowApp::Timer * CreateTimer(const char * name, uint32_t timeoutInMs, WindowApp::Timer::Callback callback, + void * context) override; + WindowApp::Button * CreateButton(WindowApp::Button::Id id, const char * name) override; + void ProcessEvents(); + void DispatchEvent(const WindowApp::Event & event) override; + void UpdateLEDs(); + void UpdateLCD(); + void OnMainLoop() override; + + static void OnTaskCallback(void * parameter); + static void OnIconTimeout(WindowApp::Timer & timer); + +private: + void DispatchEventAttributeChange(chip::EndpointId endpoint, chip::AttributeId attribute); + TaskHandle_t mHandle = nullptr; + QueueHandle_t mQueue = nullptr; + +#ifdef ENABLE_WSTK_LEDS + LEDWidget mStatusLED; + LEDWidget mActionLED; +#endif // ENABLE_WSTK_LEDS + + // Get QR Code and emulate its content using NFC tag + char mQRCodeBuffer[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1]; +#ifdef DISPLAY_ENABLED + Timer mIconTimer; + LcdIcon mIcon = LcdIcon::None; +#endif +}; diff --git a/examples/window-app/silabs/SiWx917/src/LcdPainter.cpp b/examples/window-app/silabs/SiWx917/src/LcdPainter.cpp new file mode 100644 index 00000000000000..e1a8041f075c79 --- /dev/null +++ b/examples/window-app/silabs/SiWx917/src/LcdPainter.cpp @@ -0,0 +1,226 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * 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 + +using namespace chip::app::Clusters::WindowCovering; + +constexpr uint32_t sTiltIcon[] = { + 0xffffffff, 0xffffffff, 0xc0000003, 0xc0000003, 0xc0000003, 0xc0000003, 0xc1ffffc3, 0xc1ffffc3, + 0xc1ffffc3, 0xc003c003, 0xc003c003, 0xc003c003, 0xc003c003, 0xc003c003, 0xc003c003, 0xc003c003, + 0xc003c003, 0xc003c003, 0xc003c003, 0xc003c003, 0xc003c003, 0xc003c003, 0xc003c003, 0xc003c003, + 0xc003c003, 0xc003c003, 0xc0000003, 0xc0000003, 0xc0000003, 0xc0000003, 0xffffffff, 0xffffffff, +}; + +constexpr uint32_t sLiftIcon[] = { + 0xffffffff, 0xffffffff, 0xc0000003, 0xc0000003, 0xc0000003, 0xc0000003, 0xc0f00003, 0xc0f00003, + 0xc0f00003, 0xc0f00003, 0xc0f00003, 0xc0f00003, 0xc0f00003, 0xc0f00003, 0xc0f00003, 0xc0f00003, + 0xc0f00003, 0xc0f00003, 0xc0f00003, 0xc0f00003, 0xc0f00003, 0xc0f00003, 0xc0f00003, 0xc0ffffc3, + 0xc0ffffc3, 0xc0ffffc3, 0xc0000003, 0xc0000003, 0xc0000003, 0xc0000003, 0xffffffff, 0xffffffff, +}; + +constexpr uint32_t sOneIcon[] = { + 0xffffffff, 0xffffffff, 0xc0000003, 0xc0000003, 0xc0000003, 0xc00fe003, 0xc01fe003, 0xc03fe003, + 0xc07fe003, 0xc0fbe003, 0xc1f3e003, 0xc3e3e003, 0xc003e003, 0xc003e003, 0xc003e003, 0xc003e003, + 0xc003e003, 0xc003e003, 0xc003e003, 0xc003e003, 0xc003e003, 0xc003e003, 0xc003e003, 0xc3ffffc3, + 0xc3ffffc3, 0xc3ffffc3, 0xc3ffffc3, 0xc0000003, 0xc0000003, 0xc0000003, 0xffffffff, 0xffffffff, +}; + +constexpr uint32_t sTwoIcon[] = { + 0xffffffff, 0xffffffff, 0xc0000003, 0xc0000003, 0xc0000003, 0xc07ffe03, 0xc0ffff03, 0xc1ffff83, + 0xc3ffffc3, 0xc3f00fc3, 0xc3e007c3, 0xc3e007c3, 0xc00007c3, 0xc00007c3, 0xc0000fc3, 0xc00fff83, + 0xc03fff03, 0xc07ffe03, 0xc0fc0003, 0xc1f80003, 0xc1f00003, 0xc3e00003, 0xc3e00003, 0xc3ffffc3, + 0xc3ffffc3, 0xc3ffffc3, 0xc3ffffc3, 0xc0000003, 0xc0000003, 0xc0000003, 0xffffffff, 0xffffffff, +}; + +PixelPainter::PixelPainter(uint16_t lift, uint16_t tilt) : mLift(lift), mTilt(tilt) {} + +CompositePainter::CompositePainter(uint16_t lift, uint16_t tilt, PixelPainter * painter1, PixelPainter * painter2, + PixelPainter * painter3) : + PixelPainter(lift, tilt), + mPainter1(painter1), mPainter2(painter2), mPainter3(painter3) +{} + +uint8_t CompositePainter::Color(uint32_t x, uint32_t y) +{ + int8_t pixel = -1; + if (mPainter1) + { + pixel = mPainter1->Color(x, y); + if (pixel >= 0) + { + return pixel; + } + } + if (mPainter2) + { + pixel = mPainter2->Color(x, y); + if (pixel >= 0) + { + return pixel; + } + } + if (mPainter3) + { + pixel = mPainter3->Color(x, y); + if (pixel >= 0) + { + return pixel; + } + } + return pixel; +} + +uint8_t FramePainter::Color(uint32_t x, uint32_t y) +{ + if (x < LCD_FRAME_SIZE || x >= (LCD_SIZE - LCD_FRAME_SIZE) || y < LCD_FRAME_SIZE || y >= (LCD_SIZE - LCD_FRAME_SIZE)) + { + if (x < LCD_MARGIN_SIZE || x >= (LCD_SIZE - LCD_MARGIN_SIZE) || y < LCD_MARGIN_SIZE || y >= (LCD_SIZE - LCD_MARGIN_SIZE)) + { + return 0; + } + else + { + return (x < (LCD_MARGIN_SIZE + LCD_BORDER_SIZE)) || (x >= (LCD_SIZE - LCD_BORDER_SIZE - LCD_MARGIN_SIZE)) || + (y < (LCD_MARGIN_SIZE + LCD_BORDER_SIZE)) || (y >= (LCD_SIZE - LCD_BORDER_SIZE - LCD_MARGIN_SIZE)); + } + } + return -1; +} + +IconPainter::IconPainter(uint16_t lift, uint16_t tilt, LcdIcon icon) : PixelPainter(lift, tilt), mIcon(icon) +{ + mIconSize = sizeof(sTiltIcon) / sizeof(uint32_t); + mIconOffset = (LCD_SIZE - mIconSize) / 2; +} + +uint8_t IconPainter::Color(uint32_t x, uint32_t y) +{ + const uint32_t * icon = nullptr; + switch (mIcon) + { + case LcdIcon::One: + icon = sOneIcon; + break; + case LcdIcon::Two: + icon = sTwoIcon; + break; + case LcdIcon::Lift: + icon = sLiftIcon; + break; + case LcdIcon::Tilt: + icon = sTiltIcon; + break; + default: + return -1; + } + if (x >= mIconOffset && x < (mIconOffset + mIconSize) && y >= mIconOffset && y < (mIconOffset + mIconSize)) + { + uint32_t x0 = x - mIconOffset; + uint32_t y0 = y - mIconOffset; + return (icon[y0] & (1 << (31 - x0))) > 0; + } + return -1; +} + +uint8_t VerticalShadePainter::Color(uint32_t x, uint32_t y) +{ + return (y % 2) && y < (uint32_t)(LCD_FRAME_SIZE + mLift); +} + +uint8_t HorizontalShadePainter::Color(uint32_t x, uint32_t y) +{ + return (x % 2) && x < (uint32_t)(LCD_FRAME_SIZE + mLift); +} + +VerticalBlindPainter::VerticalBlindPainter(uint16_t lift, uint16_t tilt) : PixelPainter(lift, tilt) +{ + mBandSize = (LCD_COVER_SIZE / sBandCount); +} + +uint8_t VerticalBlindPainter::Color(uint32_t x, uint32_t y) +{ + if (x % 2) + { + return 0; + } + else + { + uint32_t closedCount = (mLift + 1) / mBandSize; + uint32_t bandCount = (y - LCD_FRAME_SIZE) / mBandSize; + // ChipLogProgress(Zcl, "BLIND: ccount:%u, ccount:%u", clusterId); + if (bandCount <= closedCount) + { + return y <= (LCD_FRAME_SIZE + mBandSize * bandCount + mTilt); + } + else + { + return 0; + } + } +} + +PixelPainter * LcdPainter::GetCoverPainter(Type type, uint16_t lift, uint16_t tilt) +{ + switch (type) + { + case Type::kRollerShade: + case Type::kRollerShade2Motor: + case Type::kRollerShadeExterior: + case Type::kRollerShadeExterior2Motor: + return new VerticalShadePainter(lift, tilt); + case Type::kDrapery: + case Type::kAwning: + return new HorizontalShadePainter(lift, tilt); + case Type::kShutter: + case Type::kTiltBlindTiltOnly: + case Type::kTiltBlindLiftAndTilt: + return new VerticalBlindPainter(lift, tilt); + case Type::kProjectorScreen: + case Type::kUnknown: + default: + return new VerticalShadePainter(lift, tilt); + } + + return nullptr; +} + +void LcdPainter::Paint(SilabsLCD & lcd, Type type, uint16_t lift, uint16_t tilt, LcdIcon icon) +{ + FramePainter framePaint = FramePainter(lift, tilt); + IconPainter iconPaint = IconPainter(lift, tilt, icon); + PixelPainter * coverPaint = GetCoverPainter(type, lift, tilt); + CompositePainter compositePaint = CompositePainter(lift, tilt, &framePaint, &iconPaint, coverPaint); + void * context = lcd.Context(); + + lcd.Clear(); + + for (int i = 0; i < LCD_SIZE; i++) + { + for (int j = 0; j < LCD_SIZE; j++) + { + if (compositePaint.Color(i, j)) + { + lcd.DrawPixel(context, i, j); + } + } + } + lcd.Update(); + delete coverPaint; +} diff --git a/examples/window-app/silabs/SiWx917/src/WindowAppImpl.cpp b/examples/window-app/silabs/SiWx917/src/WindowAppImpl.cpp new file mode 100644 index 00000000000000..8cb6774f9d8da4 --- /dev/null +++ b/examples/window-app/silabs/SiWx917/src/WindowAppImpl.cpp @@ -0,0 +1,547 @@ +/* + * + * Copyright (c) 2021 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef QR_CODE_ENABLED +#include +#else +#include "SiWx917DeviceDataProvider.h" +#include +#include +#endif // QR_CODE_ENABLED + +#ifdef ENABLE_WSTK_LEDS +#include +#endif // ENABLE_WSTK_LEDS +extern "C" void sl_button_on_change(); + +#ifdef SL_WIFI +#include "wfx_host_events.h" +#include +#include +#endif + +#ifdef DISPLAY_ENABLED +#include +SilabsLCD slLCD; +#endif + +#define APP_TASK_STACK_SIZE (4096) +#define APP_TASK_PRIORITY 2 +#define APP_EVENT_QUEUE_SIZE 10 +#define EXAMPLE_VENDOR_ID 0xcafe + +#define LCD_ICON_TIMEOUT 1000 + +using namespace chip::app::Clusters::WindowCovering; +using namespace chip; +using namespace ::chip::DeviceLayer; +#define APP_STATE_LED &sl_led_led0 +#define APP_ACTION_LED &sl_led_led1 + +#ifdef SL_WIFI +chip::app::Clusters::NetworkCommissioning::Instance + sWiFiNetworkCommissioningInstance(0 /* Endpoint Id */, &(chip::DeviceLayer::NetworkCommissioning::SlWiFiDriver::GetInstance())); +#endif +//------------------------------------------------------------------------------ +// Timers +//------------------------------------------------------------------------------ + +WindowAppImpl::Timer::Timer(const char * name, uint32_t timeoutInMs, Callback callback, void * context) : + WindowApp::Timer(name, timeoutInMs, callback, context) +{ + mHandler = xTimerCreate(name, // Just a text name, not used by the RTOS kernel + timeoutInMs, // == default timer period (mS) + false, // no timer reload (==one-shot) + (void *) this, // init timer id = app task obj context + TimerCallback // timer callback handler + ); + if (mHandler == NULL) + { + SILABS_LOG("Timer create failed"); + appError(CHIP_ERROR_INTERNAL); + } +} + +void WindowAppImpl::Timer::Start() +{ + if (xTimerIsTimerActive(mHandler)) + { + Stop(); + } + + // Timer is not active + if (xTimerStart(mHandler, 100) != pdPASS) + { + SILABS_LOG("Timer start() failed"); + appError(CHIP_ERROR_INTERNAL); + } + + mIsActive = true; +} + +void WindowAppImpl::Timer::IsrStart() +{ + portBASE_TYPE taskWoken = pdFALSE; // For FreeRTOS timer (below). + // Start/restart the button debounce timer (Note ISR version of FreeRTOS + // api call here). + xTimerStartFromISR(mHandler, &taskWoken); + if (taskWoken != pdFALSE) + { + taskYIELD(); + } + mIsActive = true; +} + +void WindowAppImpl::Timer::Stop() +{ + mIsActive = false; + if (xTimerStop(mHandler, 0) == pdFAIL) + { + SILABS_LOG("Timer stop() failed"); + appError(CHIP_ERROR_INTERNAL); + } +} + +void WindowAppImpl::Timer::TimerCallback(TimerHandle_t xTimer) +{ + Timer * timer = (Timer *) pvTimerGetTimerID(xTimer); + if (timer) + { + timer->Timeout(); + } +} + +//------------------------------------------------------------------------------ +// Main Task +//------------------------------------------------------------------------------ + +StackType_t sAppStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)]; +StaticTask_t sAppTaskStruct; + +uint8_t sAppEventQueueBuffer[APP_EVENT_QUEUE_SIZE * sizeof(WindowApp::Event)]; +StaticQueue_t sAppEventQueueStruct; + +WindowAppImpl WindowAppImpl::sInstance; + +WindowApp & WindowApp::Instance() +{ + return WindowAppImpl::sInstance; +} + +#ifdef DISPLAY_ENABLED +WindowAppImpl::WindowAppImpl() : mIconTimer("Timer:icon", LCD_ICON_TIMEOUT, OnIconTimeout, this) {} +#else +WindowAppImpl::WindowAppImpl() {} +#endif + +void WindowAppImpl::OnTaskCallback(void * parameter) +{ +#ifdef SL_WIFI + /* + * Wait for the WiFi to be initialized + */ + SILABS_LOG("APP: Wait WiFi Init"); + while (!wfx_hw_ready()) + { + vTaskDelay(10); + } + SILABS_LOG("APP: Done WiFi Init"); + /* We will init server when we get IP */ + sWiFiNetworkCommissioningInstance.Init(); + /* added for commissioning with wifi */ +#endif + + sInstance.Run(); +} + +void WindowAppImpl::OnIconTimeout(WindowApp::Timer & timer) +{ +#ifdef DISPLAY_ENABLED + sInstance.mIcon = LcdIcon::None; + sInstance.UpdateLCD(); +#endif +} + +CHIP_ERROR WindowAppImpl::Init() +{ + WindowApp::Init(); + + // Initialize App Task + mHandle = xTaskCreateStatic(OnTaskCallback, APP_TASK_NAME, ArraySize(sAppStack), NULL, 1, sAppStack, &sAppTaskStruct); + if (NULL == mHandle) + { + SILABS_LOG("Failed to allocate app task"); + return CHIP_ERROR_NO_MEMORY; + } + + // Initialize App Queue + mQueue = xQueueCreateStatic(APP_EVENT_QUEUE_SIZE, sizeof(WindowApp::Event), sAppEventQueueBuffer, &sAppEventQueueStruct); + if (NULL == mQueue) + { + SILABS_LOG("Failed to allocate app event queue"); + return CHIP_ERROR_NO_MEMORY; + } + + // Initialize LEDs +#ifdef ENABLE_WSTK_LEDS + LEDWidget::InitGpio(); + mStatusLED.Init(APP_STATE_LED); + mActionLED.Init(APP_ACTION_LED); +#endif // ENABLE_WSTK_LEDS + +#ifdef DISPLAY_ENABLED + slLCD.Init(); +#endif + +#ifndef QR_CODE_ENABLED + // Create buffer for QR code that can fit max size and null terminator. + char qrCodeBuffer[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1]; + chip::MutableCharSpan QRCode(qrCodeBuffer); + + if (EFR32::EFR32DeviceDataProvider::GetDeviceDataProvider().GetSetupPayload(QRCode) == CHIP_NO_ERROR) + { + PrintQrCodeURL(QRCode); + } + else + { + SILABS_LOG("Getting QR code failed!"); + } +#endif // QR_CODE_ENABLED + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WindowAppImpl::Start() +{ + SILABS_LOG("Starting FreeRTOS scheduler"); + vTaskStartScheduler(); + + return CHIP_NO_ERROR; +} + +void WindowAppImpl::Finish() +{ + WindowApp::Finish(); + chip::Platform::MemoryShutdown(); + // Should never get here. + SILABS_LOG("vTaskStartScheduler() failed"); + appError(CHIP_ERROR_INTERNAL); +} + +void WindowAppImpl::PostEvent(const WindowApp::Event & event) +{ + if (mQueue) + { + BaseType_t status; + if (xPortIsInsideInterrupt()) + { + BaseType_t higherPrioTaskWoken = pdFALSE; + status = xQueueSendFromISR(mQueue, &event, &higherPrioTaskWoken); + +#ifdef portYIELD_FROM_ISR + portYIELD_FROM_ISR(higherPrioTaskWoken); +#elif portEND_SWITCHING_ISR // portYIELD_FROM_ISR or portEND_SWITCHING_ISR + portEND_SWITCHING_ISR(higherPrioTaskWoken); +#else // portYIELD_FROM_ISR or portEND_SWITCHING_ISR +#error "Must have portYIELD_FROM_ISR or portEND_SWITCHING_ISR" +#endif // portYIELD_FROM_ISR or portEND_SWITCHING_ISR + } + else + { + status = xQueueSend(mQueue, &event, 1); + } + + if (!status) + { + SILABS_LOG("Failed to post event to app task event queue"); + } + } +} + +void WindowAppImpl::PostAttributeChange(chip::EndpointId endpoint, chip::AttributeId attributeId) +{ + Instance().PostEvent(WindowApp::Event(WindowApp::EventId::AttributeChange, endpoint, attributeId)); +} + +void WindowAppImpl::ProcessEvents() +{ + WindowApp::Event event = EventId::None; + + BaseType_t received = xQueueReceive(mQueue, &event, pdMS_TO_TICKS(10)); + while (pdTRUE == received) + { + DispatchEvent(event); + received = xQueueReceive(mQueue, &event, 0); + } +} + +WindowApp::Timer * WindowAppImpl::CreateTimer(const char * name, uint32_t timeoutInMs, WindowApp::Timer::Callback callback, + void * context) +{ + return new Timer(name, timeoutInMs, callback, context); +} + +WindowApp::Button * WindowAppImpl::CreateButton(WindowApp::Button::Id id, const char * name) +{ + return new Button(id, name); +} + +void WindowAppImpl::DispatchEventAttributeChange(chip::EndpointId endpoint, chip::AttributeId attribute) +{ + switch (attribute) + { + /* RO OperationalStatus */ + case Attributes::OperationalStatus::Id: + UpdateLEDs(); + break; + /* RO Type: not supposed to dynamically change -> Cycling Window Covering Demo */ + case Attributes::Type::Id: + /* ============= Positions for Position Aware ============= */ + case Attributes::CurrentPositionLiftPercent100ths::Id: + case Attributes::CurrentPositionTiltPercent100ths::Id: + UpdateLCD(); + break; + /* ### ATTRIBUTEs CHANGEs IGNORED ### */ + /* RO EndProductType: not supposed to dynamically change */ + case Attributes::EndProductType::Id: + /* RO ConfigStatus: set by WC server */ + case Attributes::ConfigStatus::Id: + /* RO SafetyStatus: set by WC server */ + case Attributes::SafetyStatus::Id: + /* RW Mode: User can change */ + case Attributes::Mode::Id: + default: + break; + } +} + +void WindowAppImpl::DispatchEvent(const WindowApp::Event & event) +{ + WindowApp::DispatchEvent(event); + switch (event.mId) + { + case EventId::AttributeChange: + DispatchEventAttributeChange(event.mEndpoint, event.mAttributeId); + break; + case EventId::ResetWarning: + SILABS_LOG("Factory Reset Triggered. Release button within %ums to cancel.", LONG_PRESS_TIMEOUT); + // Turn off all LEDs before starting blink to make sure blink is + // co-ordinated. + UpdateLEDs(); + break; + case EventId::ResetCanceled: + SILABS_LOG("Factory Reset has been Canceled"); + UpdateLEDs(); + break; + case EventId::ProvisionedStateChanged: + UpdateLEDs(); + UpdateLCD(); + break; + + case EventId::WinkOn: + case EventId::WinkOff: + mState.isWinking = (EventId::WinkOn == event.mId); + UpdateLEDs(); + break; + case EventId::ConnectivityStateChanged: + case EventId::BLEConnectionsChanged: + UpdateLEDs(); + break; +#ifdef DISPLAY_ENABLED + case EventId::CoverTypeChange: + UpdateLCD(); + break; + case EventId::CoverChange: + mIconTimer.Start(); + mIcon = (GetCover().mEndpoint == 1) ? LcdIcon::One : LcdIcon::Two; + UpdateLCD(); + break; + case EventId::TiltModeChange: + mIconTimer.Start(); + mIcon = mTiltMode ? LcdIcon::Tilt : LcdIcon::Lift; + UpdateLCD(); + break; +#endif + default: + break; + } +} + +void WindowAppImpl::UpdateLEDs() +{ + Cover & cover = GetCover(); + if (mResetWarning) + { +#ifdef ENABLE_WSTK_LEDS + mStatusLED.Set(false); + mStatusLED.Blink(500); + + mActionLED.Set(false); + mActionLED.Blink(500); +#endif // ENABLE_WSTK_LEDS + } + else + { + if (mState.isWinking) + { +#ifdef ENABLE_WSTK_LEDS + mStatusLED.Blink(200, 200); +#endif // ENABLE_WSTK_LEDS + } + else +#if CHIP_ENABLE_OPENTHREAD + if (mState.isThreadProvisioned && mState.isThreadEnabled) +#else + if (mState.isWiFiProvisioned && mState.isWiFiEnabled) +#endif + + { +#ifdef ENABLE_WSTK_LEDS + mStatusLED.Blink(950, 50); +#endif // ENABLE_WSTK_LEDS + } + else if (mState.haveBLEConnections) + { +#ifdef ENABLE_WSTK_LEDS + mStatusLED.Blink(100, 100); +#endif // ENABLE_WSTK_LEDS + } + else + { +#ifdef ENABLE_WSTK_LEDS + mStatusLED.Blink(50, 950); +#endif // ENABLE_WSTK_LEDS + } + + // Action LED + NPercent100ths current; + LimitStatus liftLimit = LimitStatus::Intermediate; + + chip::DeviceLayer::PlatformMgr().LockChipStack(); + Attributes::CurrentPositionLiftPercent100ths::Get(cover.mEndpoint, current); + chip::DeviceLayer::PlatformMgr().UnlockChipStack(); + + if (!current.IsNull()) + { + AbsoluteLimits limits = { .open = WC_PERCENT100THS_MIN_OPEN, .closed = WC_PERCENT100THS_MAX_CLOSED }; + liftLimit = CheckLimitState(current.Value(), limits); + } + + if (OperationalState::Stall != cover.mLiftOpState) + { +#ifdef ENABLE_WSTK_LEDS + mActionLED.Blink(100); +#endif // ENABLE_WSTK_LEDS + } + else if (LimitStatus::IsUpOrOpen == liftLimit) + { +#ifdef ENABLE_WSTK_LEDS + mActionLED.Set(true); +#endif // ENABLE_WSTK_LEDS + } + else if (LimitStatus::IsDownOrClose == liftLimit) + { +#ifdef ENABLE_WSTK_LEDS + mActionLED.Set(false); +#endif // ENABLE_WSTK_LEDS + } + else + { +#ifdef ENABLE_WSTK_LEDS + mActionLED.Blink(1000); +#endif // ENABLE_WSTK_LEDS + } + } +} + +void WindowAppImpl::UpdateLCD() +{ + // Update LCD +#ifdef DISPLAY_ENABLED +#if CHIP_ENABLE_OPENTHREAD + if (mState.isThreadProvisioned) +#else + if (mState.isWiFiProvisioned) +#endif // CHIP_ENABLE_OPENTHREAD + { + Cover & cover = GetCover(); + chip::app::DataModel::Nullable lift; + chip::app::DataModel::Nullable tilt; + + chip::DeviceLayer::PlatformMgr().LockChipStack(); + Type type = TypeGet(cover.mEndpoint); + + Attributes::CurrentPositionLift::Get(cover.mEndpoint, lift); + Attributes::CurrentPositionTilt::Get(cover.mEndpoint, tilt); + chip::DeviceLayer::PlatformMgr().UnlockChipStack(); + +#ifdef DISPLAY_ENABLED + if (!tilt.IsNull() && !lift.IsNull()) + { + LcdPainter::Paint(slLCD, type, lift.Value(), tilt.Value(), mIcon); + } +#endif + } +#ifdef QR_CODE_ENABLED + else + { + chip::MutableCharSpan qrCode(mQRCodeBuffer); + if (GetQRCode(qrCode, chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)) == CHIP_NO_ERROR) + { + slLCD.SetQRCode((uint8_t *) qrCode.data(), qrCode.size()); + slLCD.ShowQRCode(true, true); + } + } +#endif // QR_CODE_ENABLED +#endif // DISPLAY_ENABLED +} + +void WindowAppImpl::OnMainLoop() +{ +#ifdef ENABLE_WSTK_LEDS + mStatusLED.Animate(); + mActionLED.Animate(); +#endif // ENABLE_WSTK_LEDS +} + +//------------------------------------------------------------------------------ +// Buttons +//------------------------------------------------------------------------------ +WindowAppImpl::Button::Button(WindowApp::Button::Id id, const char * name) : WindowApp::Button(id, name) {} + +void WindowAppImpl::OnButtonChange(const sl_button_t * handle) +{ + WindowApp::Button * btn = static_cast