diff --git a/examples/chef/chef.py b/examples/chef/chef.py index 0b70aa898c791f..299229e890a92f 100755 --- a/examples/chef/chef.py +++ b/examples/chef/chef.py @@ -56,6 +56,7 @@ def load_config() -> None: config = dict() config["nrfconnect"] = dict() config["esp32"] = dict() + config["silabs-thread"] = dict() configFile = f"{_CHEF_SCRIPT_PATH}/config.yaml" if (os.path.exists(configFile)): @@ -71,6 +72,10 @@ def load_config() -> None: config["nrfconnect"]["TTY"] = None config["esp32"]["IDF_PATH"] = os.environ.get('IDF_PATH') config["esp32"]["TTY"] = None + config["silabs-thread"]["GECKO_SDK"] = f"{_REPO_BASE_PATH}third_party/efr32_sdk/repo" + config["silabs-thread"]["TTY"] = None + config["silabs-thread"]["CU"] = None + print(yaml.dump(config)) yaml.dump(config, configStream) configStream.close() @@ -110,6 +115,7 @@ def main(argv: Sequence[str]) -> None: nrfconnect esp32 linux + silabs-thread Device Types: {deviceTypes} @@ -146,7 +152,7 @@ def main(argv: Sequence[str]) -> None: action='store', dest="build_target", help="specifies target platform. Default is esp32. See info below for currently supported target platforms", - choices=['nrfconnect', 'esp32', 'linux', ], + choices=['nrfconnect', 'esp32', 'linux', 'silabs-thread'], metavar="TARGET", default="esp32") parser.add_option("-r", "--rpc", help="enables Pigweed RPC interface. Enabling RPC disables the shell interface. Your sdkconfig configurations will be reverted to default. Default is PW RPC off. When enabling or disabling this flag, on the first build force a clean build with -c", action="store_true", dest="do_rpc") @@ -188,6 +194,8 @@ def main(argv: Sequence[str]) -> None: shell.run_cmd("export ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb") elif options.build_target == "linux": pass + elif options.build_target == "silabs-thread": + print('Path to gecko sdk is configured within Matter.') else: print(f"Target {options.build_target} not supported") @@ -204,6 +212,8 @@ def main(argv: Sequence[str]) -> None: print("Updating toolchain") shell.run_cmd( f"cd {_REPO_BASE_PATH} && python3 scripts/setup/nrfconnect/update_ncs.py --update") + elif options.build_target == "silabs-thread": + print("Silabs-thread toolchain not supported. Skipping") elif options.build_target == "linux": print("Linux toolchain update not supported. Skipping") @@ -256,6 +266,8 @@ def main(argv: Sequence[str]) -> None: elif options.build_target == "nrfconnect": shell.run_cmd(f"cd {_CHEF_SCRIPT_PATH}/nrfconnect") shell.run_cmd("west build -t menuconfig") + elif (options.build_target == "silabs-thread") or (options.build_target == "silabs-wifi"): + print("Menuconfig not available on Silabs-thread target. Skipping") elif options.build_target == "linux": print("Menuconfig not available on Linux target. Skipping") @@ -267,12 +279,18 @@ def main(argv: Sequence[str]) -> None: print("Building...") if options.do_rpc: print("RPC PW enabled") - shell.run_cmd( - f"export SDKCONFIG_DEFAULTS={_CHEF_SCRIPT_PATH}/esp32/sdkconfig_rpc.defaults") + if options.build_target == "esp32": + shell.run_cmd( + f"export SDKCONFIG_DEFAULTS={_CHEF_SCRIPT_PATH}/esp32/sdkconfig_rpc.defaults") + else: + print(f"RPC PW on {options.build_target} not supported") + else: print("RPC PW disabled") + if (options.build_target == "esp32"): shell.run_cmd( f"export SDKCONFIG_DEFAULTS={_CHEF_SCRIPT_PATH}/esp32/sdkconfig.defaults") + print( f"Product ID 0x{options.pid:02X} / Vendor ID 0x{options.vid:02X}") shell.run_cmd(f"cd {_CHEF_SCRIPT_PATH}") @@ -301,6 +319,15 @@ def main(argv: Sequence[str]) -> None: if options.do_rpc: nrf_build_cmds.append("-- -DOVERLAY_CONFIG=rpc.overlay") shell.run_cmd(" ".join(nrf_build_cmds)) + + elif options.build_target == "silabs-thread": + shell.run_cmd(f"cd {_CHEF_SCRIPT_PATH}/efr32") + if options.do_clean: + shell.run_cmd(f"rm -rf out/{options.sample_device_type_name}") + shell.run_cmd( + f"""{_REPO_BASE_PATH}/scripts/examples/gn_efr32_example.sh ./ out/{options.sample_device_type_name} BRD4186A \'sample_name=\"{options.sample_device_type_name}\"\' enable_openthread_cli=true chip_build_libshell=true \'{'import("//with_pw_rpc.gni")' if options.do_rpc else ""}\'""") + shell.run_cmd(f"cd {_CHEF_SCRIPT_PATH}") + elif options.build_target == "linux": shell.run_cmd(f"cd {_CHEF_SCRIPT_PATH}/linux") with open(f"{_CHEF_SCRIPT_PATH}/linux/args.gni", "w") as f: @@ -345,6 +372,11 @@ def main(argv: Sequence[str]) -> None: shell.run_cmd("west flash --erase") else: shell.run_cmd("west flash") + elif (options.build_target == "silabs-thread") or (options.build_target == "silabs-wifi"): + shell.run_cmd(f"cd {_CHEF_SCRIPT_PATH}/efr32") + shell.run_cmd(f"python3 out/{options.sample_device_type_name}/BRD4186A/chip-efr32-chef-example.flash.py") + + shell.run_cmd(f"cd {_CHEF_SCRIPT_PATH}") # # Terminal interaction @@ -364,6 +396,13 @@ def main(argv: Sequence[str]) -> None: exit(1) shell.run_cmd("killall screen") shell.run_cmd(f"screen {config['nrfconnect']['TTY']} 115200") + elif (options.build_target == "silabs-thread"): + if config['silabs-thread']['TTY'] is None: + print('The path for the serial enumeration for silabs-thread is not set. Make sure silabs-thread.TTY is set on your config.yaml file') + exit(1) + + shell.run_cmd("killall screen") + shell.run_cmd(f"screen {config['silabs-thread']['TTY']} 115200 8-N-1") elif options.build_target == "linux": print( f"{_CHEF_SCRIPT_PATH}/linux/out/{options.sample_device_type_name}") @@ -374,8 +413,20 @@ def main(argv: Sequence[str]) -> None: # RPC Console # if options.do_rpc_console: - shell.run_cmd( - f"python3 -m chip_rpc.console --device {config['esp32']['TTY']}") + if options.build_target == "esp32": + shell.run_cmd( + f"python3 -m chip_rpc.console --device {config['esp32']['TTY']}") + elif (options.build_target == "silabs-thread"): + if (sys.platform == "linux") or (sys.platform == "linux2"): + if(config['silabs-thread']['TTY'] is None): + print('The path for the serial enumeration for silabs-thread is not set. Make sure silabs-thread.TTY is set on your config.yaml file') + exit(1) + shell.run_cmd(f"python3 -m chip_rpc.console --device {config['silabs-thread']['TTY']} -b 115200") + elif sys.platform == "darwin": + if(config['silabs-thread']['CU'] is None): + print('The path for the serial enumeration for silabs-thread is not set. Make sure silabs-thread.CU is set on your config.yaml file') + exit(1) + shell.run_cmd(f"python3 -m chip_rpc.console --device {config['silabs-thread']['CU']} -b 115200") print("Done") diff --git a/examples/chef/efr32/.gn b/examples/chef/efr32/.gn new file mode 100644 index 00000000000000..0ff42d50e06ef0 --- /dev/null +++ b/examples/chef/efr32/.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_openthread_ftd = true + import("//args.gni") +} diff --git a/examples/chef/efr32/BUILD.gn b/examples/chef/efr32/BUILD.gn new file mode 100644 index 00000000000000..071101cc34d883 --- /dev/null +++ b/examples/chef/efr32/BUILD.gn @@ -0,0 +1,347 @@ +# 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}/efr32_executable.gni") +import("${efr32_sdk_build_root}/efr32_sdk.gni") + +import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") +import("${chip_root}/src/platform/device.gni") + +import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") +import("${chip_root}/src/app/chip_data_model.gni") + +if (chip_enable_pw_rpc) { + import("//build_overrides/pigweed.gni") + import("$dir_pw_build/target_types.gni") +} + +assert(current_os == "freertos") + +chef_project_dir = "${chip_root}/examples/chef" +efr32_project_dir = "${chef_project_dir}/efr32" +examples_plat_dir = "${chip_root}/examples/platform/efr32" + +declare_args() { + # Dump memory usage at link time. + chip_print_memory_usage = false + + # PIN code for PASE session establishment. + setupPinCode = 20202021 + setupDiscriminator = 3840 + + # 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 = false + + sample_name = "" +} + +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) +} + +chip_data_model("chef-common") { + zap_file = "${chef_project_dir}/devices/${sample_name}.zap" + + zap_pregenerated_dir = "${chef_project_dir}/out/${sample_name}/zap-generated/" + is_server = true +} + +# BRD4166A --> ThunderBoard Sense 2 (No LCD) +if (efr32_board == "BRD4166A" || efr32_board == "BRD4180A") { + show_qr_code = false + disable_lcd = true +} + +# WiFi settings +if (chip_enable_wifi) { + wifi_sdk_dir = "${chip_root}/third_party/efr32_sdk/repo/matter/wifi" + efr32_lwip_defs = [ "LWIP_NETIF_API=1" ] + efr32_lwip_defs += [ + "LWIP_IPV4=1", + "LWIP_ARP=1", + "LWIP_ICMP=1", + "LWIP_DHCP=1", + "LWIP_IPV6_ND=1", + "LWIP_IGMP=1", + ] + + if (use_rs911x) { + wiseconnect_sdk_root = + "${chip_root}/third_party/efr32_sdk/wiseconnect-wifi-bt-sdk" + import("${wifi_sdk_dir}/rs911x/rs911x.gni") + } else { + import("${wifi_sdk_dir}/wf200/wf200.gni") + } +} + +efr32_sdk("sdk") { + sources = [ + "${efr32_project_dir}/include/CHIPProjectConfig.h", + "${examples_plat_dir}/FreeRTOSConfig.h", + ] + + include_dirs = [ + "${chip_root}/src/platform/EFR32", + "${efr32_project_dir}/include", + "${examples_plat_dir}", + "${chip_root}/src/lib", + ] + + defines = [ + "BOARD_ID=${efr32_board}", + "CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE=${setupPinCode}", + "CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR=${setupDiscriminator}", + "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("chef_app") { + output_name = "chip-efr32-chef-example.out" + include_dirs = [ "include" ] + defines = [] + + sources = [ + "${examples_plat_dir}/LEDWidget.cpp", + "${examples_plat_dir}/efr32_utils.cpp", + "${examples_plat_dir}/heap_4_silabs.c", + "${examples_plat_dir}/init_efrPlatform.cpp", + "${examples_plat_dir}/matter_config.cpp", + "src/AppTask.cpp", + "src/LightingManager.cpp", + "src/ZclCallbacks.cpp", + "src/main.cpp", + ] + + if (chip_enable_pw_rpc || chip_build_libshell || enable_openthread_cli) { + sources += [ "${examples_plat_dir}/uart.cpp" ] + } + + deps = [ + ":chef-common", + ":sdk", + "${chip_root}/src/lib", + "${chip_root}/src/setup_payload", + ] + + # OpenThread Settings + if (chip_enable_openthread) { + deps += [ + "${chip_root}/third_party/openthread/platforms:libopenthread-platform", + "${chip_root}/third_party/openthread/platforms:libopenthread-platform-utils", + "${examples_plat_dir}:efr-matter-shell", + ] + if (chip_openthread_ftd) { + deps += [ + "${chip_root}/third_party/openthread/repo:libopenthread-cli-ftd", + "${chip_root}/third_party/openthread/repo:libopenthread-ftd", + ] + } else { + deps += [ + "${chip_root}/third_party/openthread/repo:libopenthread-cli-mtd", + "${chip_root}/third_party/openthread/repo:libopenthread-mtd", + ] + } + } + + 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 (!disable_lcd) { + sources += [ "${examples_plat_dir}/display/lcd.c" ] + defines += [ "DISPLAY_ENABLED" ] + 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", + "PW_RPC_OTCLI_SERVICE=1", + "PW_RPC_THREAD_SERVICE=1", + "PW_RPC_TRACING_SERVICE=1", + ] + + sources += [ + "${chip_root}/examples/common/pigweed/RpcService.cpp", + "${chip_root}/examples/common/pigweed/efr32/PigweedLoggerMutex.cpp", + "${examples_plat_dir}/PigweedLogger.cpp", + "${examples_plat_dir}/Rpc.cpp", + ] + + deps += [ + "$dir_pw_hdlc:rpc_channel_output", + "$dir_pw_stream:sys_io_stream", + "$dir_pw_trace", + "$dir_pw_trace_tokenized", + "$dir_pw_trace_tokenized:trace_rpc_service", + "${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", + "${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_plat_dir}/MemMonitoring.cpp" ] + defines += [ "HEAP_MONITORING" ] + } + + ldscript = "${examples_plat_dir}/ldscripts/${efr32_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 = [ ":chef_app" ] +} + +group("default") { + deps = [ ":efr32" ] +} diff --git a/examples/chef/efr32/args.gni b/examples/chef/efr32/args.gni new file mode 100644 index 00000000000000..96b6262f50b93e --- /dev/null +++ b/examples/chef/efr32/args.gni @@ -0,0 +1,25 @@ +# 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("//build_overrides/pigweed.gni") +import("${chip_root}/src/platform/EFR32/args.gni") + +efr32_sdk_target = get_label_info(":sdk", "label_no_toolchain") + +chip_enable_ota_requestor = true + +pw_log_BACKEND = "${chip_root}/src/lib/support/pw_log_chip" +pw_assert_BACKEND = "$dir_pw_assert_log" +chip_enable_openthread = true diff --git a/examples/chef/efr32/build_for_wifi_args.gni b/examples/chef/efr32/build_for_wifi_args.gni new file mode 100644 index 00000000000000..bf21912871e59d --- /dev/null +++ b/examples/chef/efr32/build_for_wifi_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("//build_overrides/pigweed.gni") + +efr32_sdk_target = get_label_info(":sdk", "label_no_toolchain") +chip_enable_openthread = false +import("${chip_root}/src/platform/EFR32/wifi_args.gni") + +chip_enable_ota_requestor = true + +pw_log_BACKEND = "${chip_root}/src/lib/support/pw_log_chip" +pw_assert_BACKEND = "$dir_pw_assert_log" diff --git a/examples/chef/efr32/build_for_wifi_gnfile.gn b/examples/chef/efr32/build_for_wifi_gnfile.gn new file mode 100644 index 00000000000000..d391814190d09f --- /dev/null +++ b/examples/chef/efr32/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/chef/efr32/build_overrides b/examples/chef/efr32/build_overrides new file mode 120000 index 00000000000000..e578e73312ebd1 --- /dev/null +++ b/examples/chef/efr32/build_overrides @@ -0,0 +1 @@ +../../build_overrides \ No newline at end of file diff --git a/examples/chef/efr32/include/AppConfig.h b/examples/chef/efr32/include/AppConfig.h new file mode 100644 index 00000000000000..91501d889a6234 --- /dev/null +++ b/examples/chef/efr32/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 efr32LogInit(void); + +void efr32Log(const char * aFormat, ...); +#define EFR32_LOG(...) efr32Log(__VA_ARGS__); +void appError(int err); + +#ifdef __cplusplus +} + +#include +void appError(CHIP_ERROR error); +#endif diff --git a/examples/chef/efr32/include/AppEvent.h b/examples/chef/efr32/include/AppEvent.h new file mode 100644 index 00000000000000..7a19b719edad25 --- /dev/null +++ b/examples/chef/efr32/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/chef/efr32/include/AppTask.h b/examples/chef/efr32/include/AppTask.h new file mode 100644 index 00000000000000..be75aff4df2de9 --- /dev/null +++ b/examples/chef/efr32/include/AppTask.h @@ -0,0 +1,95 @@ +/* + * + * 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 + +#include +#include + +#include "AppEvent.h" +#include "LightingManager.h" +#include "sl_simple_button_instances.h" + +#include "FreeRTOS.h" +#include "timers.h" // provides FreeRTOS timer support +#include +#include + +// 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) + +class AppTask +{ + +public: + CHIP_ERROR StartAppTask(); + static void AppTaskMain(void * pvParameter); + + void PostLightActionRequest(int32_t aActor, LightingManager::Action_t aAction); + void PostEvent(const AppEvent * event); + + void ButtonEventHandler(const sl_button_t * buttonHandle, uint8_t btnAction); + +private: + friend AppTask & GetAppTask(void); + + CHIP_ERROR Init(); + + static void ActionInitiated(LightingManager::Action_t aAction, int32_t aActor); + static void ActionCompleted(LightingManager::Action_t aAction); + + void CancelTimer(void); + + void DispatchEvent(AppEvent * event); + + static void FunctionTimerEventHandler(AppEvent * aEvent); + static void FunctionHandler(AppEvent * aEvent); + static void SwitchActionEventHandler(AppEvent * aEvent); + static void TimerEventHandler(TimerHandle_t xTimer); + + static void UpdateClusterState(void); + + void StartTimer(uint32_t aTimeoutMs); + + enum Function_t + { + kFunction_NoneSelected = 0, + kFunction_SoftwareUpdate = 0, + kFunction_StartBleAdv = 1, + kFunction_FactoryReset = 2, + + kFunction_Invalid + } Function; + + Function_t mFunction; + bool mFunctionTimerActive; + bool mSyncClusterToButtonAction; + + static AppTask sAppTask; +}; + +inline AppTask & GetAppTask(void) +{ + return AppTask::sAppTask; +} diff --git a/examples/chef/efr32/include/CHIPProjectConfig.h b/examples/chef/efr32/include/CHIPProjectConfig.h new file mode 100644 index 00000000000000..123870156bcd4d --- /dev/null +++ b/examples/chef/efr32/include/CHIPProjectConfig.h @@ -0,0 +1,143 @@ +/* + * + * 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_STRING + * + * A string identifying the software version running on the device. + * CHIP service currently expects the software version to be in the format + * {MAJOR_VERSION}.0d{MINOR_VERSION} + */ +#ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "0.1ALPHA" +#endif + +/** + * 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_ENABLE_CHIP_TIME_SERVICE_TIME_SYNC + * + * Enables synchronizing the device's real time clock with a remote Chip Time service + * using the Chip Time Sync protocol. + */ +#define CHIP_DEVICE_CONFIG_ENABLE_CHIP_TIME_SERVICE_TIME_SYNC 0 + +/** + * 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_DEFAULT_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_DEFAULT_ACTIVE_RETRY_INTERVAL (2000_ms32) + +#define CHIP_DEVICE_CONFIG_ENABLE_EXTENDED_DISCOVERY 1 diff --git a/examples/chef/efr32/include/LightingManager.h b/examples/chef/efr32/include/LightingManager.h new file mode 100644 index 00000000000000..3aa9871e919074 --- /dev/null +++ b/examples/chef/efr32/include/LightingManager.h @@ -0,0 +1,85 @@ +/* + * + * 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 "AppEvent.h" + +#include "FreeRTOS.h" +#include "timers.h" // provides FreeRTOS timer support + +#include + +class LightingManager +{ +public: + enum Action_t + { + ON_ACTION = 0, + OFF_ACTION, + + INVALID_ACTION + } Action; + + enum State_t + { + kState_OffInitiated = 0, + kState_OffCompleted, + kState_OnInitiated, + kState_OnCompleted, + } State; + + CHIP_ERROR Init(); + bool IsLightOn(); + void EnableAutoTurnOff(bool aOn); + void SetAutoTurnOffDuration(uint32_t aDurationInSecs); + 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); + +private: + friend LightingManager & LightMgr(void); + State_t mState; + + Callback_fn_initiated mActionInitiated_CB; + Callback_fn_completed mActionCompleted_CB; + + bool mAutoTurnOff; + uint32_t mAutoTurnOffDuration; + bool mAutoTurnOffTimerArmed; + + void CancelTimer(void); + void StartTimer(uint32_t aTimeoutMs); + + static void TimerEventHandler(TimerHandle_t xTimer); + static void AutoTurnOffTimerEventHandler(AppEvent * aEvent); + static void ActuatorMovementTimerEventHandler(AppEvent * aEvent); + + static LightingManager sLight; +}; + +inline LightingManager & LightMgr(void) +{ + return LightingManager::sLight; +} diff --git a/examples/chef/efr32/src/AppTask.cpp b/examples/chef/efr32/src/AppTask.cpp new file mode 100644 index 00000000000000..15ff91d64a5fa6 --- /dev/null +++ b/examples/chef/efr32/src/AppTask.cpp @@ -0,0 +1,546 @@ +/* + * + * 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" +#include "LEDWidget.h" +#include "sl_simple_led_instances.h" + +#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 + +#ifdef EMBER_AF_PLUGIN_IDENTIFY_SERVER +#include +#endif + +#include + +#include +#include + +#include +#include + +#include + +#include + +#include +#if CHIP_ENABLE_OPENTHREAD +#include +#include +#include +#endif +#ifdef SL_WIFI +#include "wfx_host_events.h" +#include +#include +#endif /* SL_WIFI */ + +#define FACTORY_RESET_TRIGGER_TIMEOUT 3000 +#define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT 3000 +#define APP_TASK_STACK_SIZE (4096) +#define APP_TASK_PRIORITY 2 +#define APP_EVENT_QUEUE_SIZE 10 +#define EXAMPLE_VENDOR_ID 0xcafe + +#define SYSTEM_STATE_LED &sl_led_led0 +#define APP_FUNCTION_BUTTON &sl_button_btn0 + +using namespace chip; +using namespace ::chip::DeviceLayer; + +namespace { +TimerHandle_t sFunctionTimer; // FreeRTOS app sw timer. + +TaskHandle_t sAppTaskHandle; +QueueHandle_t sAppEventQueue; + +LEDWidget sStatusLED; + +#ifdef SL_WIFI +bool sIsWiFiProvisioned = false; +bool sIsWiFiEnabled = false; +bool sIsWiFiAttached = false; + +app::Clusters::NetworkCommissioning::Instance + sWiFiNetworkCommissioningInstance(0 /* Endpoint Id */, &(NetworkCommissioning::SlWiFiDriver::GetInstance())); +#endif /* SL_WIFI */ + +#if CHIP_ENABLE_OPENTHREAD +bool sIsThreadProvisioned = false; +bool sIsThreadEnabled = false; +#endif /* CHIP_ENABLE_OPENTHREAD */ +bool sHaveBLEConnections = false; + +#ifdef EMBER_AF_PLUGIN_IDENTIFY_SERVER +EmberAfIdentifyEffectIdentifier sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; +#endif // EMBER_AF_PLUGIN_IDENTIFY_SERVER + +uint8_t sAppEventQueueBuffer[APP_EVENT_QUEUE_SIZE * sizeof(AppEvent)]; +StaticQueue_t sAppEventQueueStruct; + +StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)]; +StaticTask_t appTaskStruct; + +namespace { +#ifdef EMBER_AF_PLUGIN_IDENTIFY_SERVER +void OnTriggerIdentifyEffectCompleted(chip::System::Layer * systemLayer, void * appState) +{ + sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; +} +#endif // EMBER_AF_PLUGIN_IDENTIFY_SERVER +} // namespace + +#ifdef EMBER_AF_PLUGIN_IDENTIFY_SERVER +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); + } + + 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"); + } +} +#endif // EMBER_AF_PLUGIN_IDENTIFY_SERVER + +#ifdef EMBER_AF_PLUGIN_IDENTIFY_SERVER +Identify gIdentify = { + chip::EndpointId{ 1 }, + [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStart"); }, + [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStop"); }, + EMBER_ZCL_IDENTIFY_IDENTIFY_TYPE_VISIBLE_LED, + OnTriggerIdentifyEffect, +}; +#endif // EMBER_AF_PLUGIN_IDENTIFY_SERVER + +} // namespace +using namespace chip::TLV; +using namespace ::chip::Credentials; +using namespace ::chip::DeviceLayer; + +AppTask AppTask::sAppTask; + +CHIP_ERROR AppTask::StartAppTask() +{ + sAppEventQueue = xQueueCreateStatic(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent), sAppEventQueueBuffer, &sAppEventQueueStruct); + if (sAppEventQueue == NULL) + { + EFR32_LOG("Failed to allocate app event queue"); + appError(APP_ERROR_EVENT_QUEUE_FAILED); + } + + // Start App task. + sAppTaskHandle = xTaskCreateStatic(AppTaskMain, APP_TASK_NAME, ArraySize(appStack), NULL, 1, appStack, &appTaskStruct); + return (sAppTaskHandle == nullptr) ? APP_ERROR_CREATE_TASK_FAILED : CHIP_NO_ERROR; +} + +CHIP_ERROR AppTask::Init() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + +#ifdef SL_WIFI + /* + * Wait for the WiFi to be initialized + */ + EFR32_LOG("APP: Wait WiFi Init"); + while (!wfx_hw_ready()) + { + vTaskDelay(10); + } + EFR32_LOG("APP: Done WiFi Init"); + /* We will init server when we get IP */ + + sWiFiNetworkCommissioningInstance.Init(); +#endif + + chip::DeviceLayer::PlatformMgr().LockChipStack(); + // Initialize device attestation config + SetDeviceAttestationCredentialsProvider(Examples::GetExampleDACProvider()); + chip::DeviceLayer::PlatformMgr().UnlockChipStack(); + + // Create FreeRTOS sw timer for Function Selection. + sFunctionTimer = xTimerCreate("FnTmr", // 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 = app task obj context + TimerEventHandler // timer callback handler + ); + if (sFunctionTimer == NULL) + { + EFR32_LOG("funct timer create failed"); + appError(APP_ERROR_CREATE_TIMER_FAILED); + } + + EFR32_LOG("Current Software Version: %s", CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); + err = LightMgr().Init(); + if (err != CHIP_NO_ERROR) + { + EFR32_LOG("LightMgr().Init() failed"); + appError(err); + } + + LightMgr().SetCallbacks(ActionInitiated, ActionCompleted); + + // Initialize LEDs + LEDWidget::InitGpio(); + sStatusLED.Init(SYSTEM_STATE_LED); + UpdateClusterState(); + + ConfigurationMgr().LogDeviceConfig(); + +// Print setup info on LCD if available +#ifdef DISPLAY_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 (GetQRCode(QRCode, chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)) == CHIP_NO_ERROR) + { + LCDWriteQRCode((uint8_t *) QRCode.data()); + } + else + { + EFR32_LOG("Getting QR code failed!"); + } +#else + PrintOnboardingCodes(chip::RendezvousInformationFlag(chip::RendezvousInformationFlag::kBLE)); +#endif + + return err; +} + +void AppTask::AppTaskMain(void * pvParameter) +{ + AppEvent event; + + CHIP_ERROR err = sAppTask.Init(); + if (err != CHIP_NO_ERROR) + { + EFR32_LOG("AppTask.Init() failed"); + appError(err); + } + + EFR32_LOG("App Task started"); + + while (true) + { + BaseType_t eventReceived = xQueueReceive(sAppEventQueue, &event, pdMS_TO_TICKS(10)); + while (eventReceived == pdTRUE) + { + sAppTask.DispatchEvent(&event); + eventReceived = xQueueReceive(sAppEventQueue, &event, 0); + } + + // Collect connectivity and configuration state from the CHIP stack. Because + // the CHIP event loop is being run in a separate task, the stack must be + // locked while these values are queried. However we use a non-blocking + // lock request (TryLockCHIPStack()) to avoid blocking other UI activities + // when the CHIP task is busy (e.g. with a long crypto operation). + if (PlatformMgr().TryLockChipStack()) + { +#ifdef SL_WIFI + sIsWiFiProvisioned = ConnectivityMgr().IsWiFiStationProvisioned(); + sIsWiFiEnabled = ConnectivityMgr().IsWiFiStationEnabled(); + sIsWiFiAttached = ConnectivityMgr().IsWiFiStationConnected(); +#endif /* SL_WIFI */ +#if CHIP_ENABLE_OPENTHREAD + sIsThreadProvisioned = ConnectivityMgr().IsThreadProvisioned(); + sIsThreadEnabled = ConnectivityMgr().IsThreadEnabled(); +#endif /* CHIP_ENABLE_OPENTHREAD */ + sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0); + PlatformMgr().UnlockChipStack(); + } + + // Update the status LED if factory reset has not been initiated. + // + // If system has "full connectivity", keep the LED On constantly. + // + // If thread and service provisioned, but not attached to the thread network + // yet OR no connectivity to the service OR subscriptions are not fully + // established THEN blink the LED Off for a short period of time. + // + // If the system has ble connection(s) uptill the stage above, THEN blink + // the LEDs at an even rate of 100ms. + // + // Otherwise, blink the LED ON for a very short time. + if (sAppTask.mFunction != kFunction_FactoryReset) + { + if (gIdentify.mActive) + { + sStatusLED.Blink(250, 250); + } + if (sIdentifyEffect != EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT) + { + if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BLINK) + { + sStatusLED.Blink(50, 50); + } + if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BREATHE) + { + sStatusLED.Blink(1000, 1000); + } + if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_OKAY) + { + sStatusLED.Blink(300, 700); + } + } +#if CHIP_ENABLE_OPENTHREAD + if (sIsThreadProvisioned && sIsThreadEnabled) +#else + if (sIsWiFiProvisioned && sIsWiFiEnabled && !sIsWiFiAttached) +#endif + { + sStatusLED.Blink(950, 50); + } + else if (sHaveBLEConnections) { sStatusLED.Blink(100, 100); } + else { sStatusLED.Blink(50, 950); } + } + + sStatusLED.Animate(); + } +} + +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; + + if (buttonHandle == APP_FUNCTION_BUTTON) + { + button_event.Handler = FunctionHandler; + sAppTask.PostEvent(&button_event); + } +} + +void AppTask::TimerEventHandler(TimerHandle_t xTimer) +{ + AppEvent event; + event.Type = AppEvent::kEventType_Timer; + event.TimerEvent.Context = (void *) xTimer; + event.Handler = FunctionTimerEventHandler; + sAppTask.PostEvent(&event); +} + +void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) +{ + if (aEvent->Type != AppEvent::kEventType_Timer) + { + return; + } + + // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT, + // initiate factory reset + if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv) + { + EFR32_LOG("Factory Reset Triggered. Release button within %ums to cancel.", FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); + + // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to + // cancel, if required. + sAppTask.StartTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); + + sAppTask.mFunction = kFunction_FactoryReset; + + // Turn off all LEDs before starting blink to make sure blink is + // co-ordinated. + sStatusLED.Set(false); + sStatusLED.Blink(500); + } + else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) + { + // Actually trigger Factory Reset + sAppTask.mFunction = kFunction_NoneSelected; + chip::Server::GetInstance().ScheduleFactoryReset(); + } +} + +void AppTask::FunctionHandler(AppEvent * aEvent) +{ + // To trigger software update: press the APP_FUNCTION_BUTTON button briefly (< + // FACTORY_RESET_TRIGGER_TIMEOUT) To initiate factory reset: press the + // APP_FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT + + // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT All LEDs start blinking after + // FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated. + // To cancel factory reset: release the APP_FUNCTION_BUTTON once all LEDs + // start blinking within the FACTORY_RESET_CANCEL_WINDOW_TIMEOUT + if (aEvent->ButtonEvent.Action == SL_SIMPLE_BUTTON_PRESSED) + { + if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected) + { + sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT); + sAppTask.mFunction = kFunction_StartBleAdv; + } + } + else + { + // If the button was released before factory reset got initiated, start BLE advertissement in fast mode + if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_StartBleAdv) + { + sAppTask.CancelTimer(); + sAppTask.mFunction = kFunction_NoneSelected; + +#ifdef SL_WIFI + if (!ConnectivityMgr().IsWiFiStationProvisioned()) +#else + if (!ConnectivityMgr().IsThreadProvisioned()) +#endif /* !SL_WIFI */ + { + // Enable BLE advertisements + ConnectivityMgr().SetBLEAdvertisingEnabled(true); + ConnectivityMgr().SetBLEAdvertisingMode(ConnectivityMgr().kFastAdvertising); + } + else { EFR32_LOG("Network is already provisioned, Ble advertissement not enabled"); } + } + } +} + +void AppTask::CancelTimer() +{ + if (xTimerStop(sFunctionTimer, 0) == pdFAIL) + { + EFR32_LOG("app timer stop() failed"); + appError(APP_ERROR_STOP_TIMER_FAILED); + } + + mFunctionTimerActive = false; +} + +void AppTask::StartTimer(uint32_t aTimeoutInMs) +{ + if (xTimerIsTimerActive(sFunctionTimer)) + { + EFR32_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(sFunctionTimer, aTimeoutInMs / portTICK_PERIOD_MS, 100) != pdPASS) + { + EFR32_LOG("app timer start() failed"); + appError(APP_ERROR_START_TIMER_FAILED); + } + + mFunctionTimerActive = true; +} + +void AppTask::ActionInitiated(LightingManager::Action_t aAction, int32_t aActor) +{ + if (aActor == AppEvent::kEventType_Button) + { + sAppTask.mSyncClusterToButtonAction = true; + } +} + +void AppTask::ActionCompleted(LightingManager::Action_t aAction) +{ + if (sAppTask.mSyncClusterToButtonAction) + { + UpdateClusterState(); + sAppTask.mSyncClusterToButtonAction = false; + } +} + +void AppTask::PostEvent(const AppEvent * aEvent) +{ + if (sAppEventQueue != NULL) + { + BaseType_t status; + if (xPortIsInsideInterrupt()) + { + BaseType_t higherPrioTaskWoken = pdFALSE; + status = xQueueSendFromISR(sAppEventQueue, aEvent, &higherPrioTaskWoken); + +#ifdef portYIELD_FROM_ISR + portYIELD_FROM_ISR(higherPrioTaskWoken); +#elif portEND_SWITCHING_ISR // portYIELD_FROM_ISR or portEND_SWITCHING_ISR + portEND_SWITCHING_ISR(higherPrioTaskWoken); +#else // portYIELD_FROM_ISR or portEND_SWITCHING_ISR +#error "Must have portYIELD_FROM_ISR or portEND_SWITCHING_ISR" +#endif // portYIELD_FROM_ISR or portEND_SWITCHING_ISR + } + else + { + status = xQueueSend(sAppEventQueue, aEvent, 1); + } + + if (!status) + EFR32_LOG("Failed to post event to app task event queue"); + } + else + { + EFR32_LOG("Event Queue is NULL should never happen"); + } +} + +void AppTask::DispatchEvent(AppEvent * aEvent) +{ + if (aEvent->Handler) + { + aEvent->Handler(aEvent); + } + else + { + EFR32_LOG("Event received with no handler. Dropping event."); + } +} + +void AppTask::UpdateClusterState(void) {} diff --git a/examples/chef/efr32/src/LightingManager.cpp b/examples/chef/efr32/src/LightingManager.cpp new file mode 100644 index 00000000000000..7b206fd1ab6f37 --- /dev/null +++ b/examples/chef/efr32/src/LightingManager.cpp @@ -0,0 +1,225 @@ +/* + * + * 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 "LightingManager.h" + +#include "AppConfig.h" +#include "AppTask.h" +#include + +LightingManager LightingManager::sLight; + +TimerHandle_t sLightTimer; + +CHIP_ERROR LightingManager::Init() +{ + // Create FreeRTOS sw timer for light timer. + sLightTimer = xTimerCreate("lightTmr", // 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 = light obj context + TimerEventHandler // timer callback handler + ); + + if (sLightTimer == NULL) + { + EFR32_LOG("sLightTimer timer create failed"); + return APP_ERROR_CREATE_TIMER_FAILED; + } + + mState = kState_OffCompleted; + mAutoTurnOffTimerArmed = false; + mAutoTurnOff = false; + mAutoTurnOffDuration = 0; + + return CHIP_NO_ERROR; +} + +void LightingManager::SetCallbacks(Callback_fn_initiated aActionInitiated_CB, Callback_fn_completed aActionCompleted_CB) +{ + mActionInitiated_CB = aActionInitiated_CB; + mActionCompleted_CB = aActionCompleted_CB; +} + +bool LightingManager::IsActionInProgress() +{ + return (mState == kState_OffInitiated || mState == kState_OnInitiated); +} + +bool LightingManager::IsLightOn() +{ + return (mState == kState_OnCompleted); +} + +void LightingManager::EnableAutoTurnOff(bool aOn) +{ + mAutoTurnOff = aOn; +} + +void LightingManager::SetAutoTurnOffDuration(uint32_t aDurationInSecs) +{ + mAutoTurnOffDuration = aDurationInSecs; +} + +bool LightingManager::InitiateAction(int32_t aActor, Action_t aAction) +{ + bool action_initiated = false; + State_t new_state; + + // Initiate Turn On/Off Action only when the previous one is complete. + if (mState == kState_OffCompleted && aAction == ON_ACTION) + { + action_initiated = true; + + new_state = kState_OnInitiated; + } + else if (mState == kState_OnCompleted && aAction == OFF_ACTION) + { + action_initiated = true; + + new_state = kState_OffInitiated; + } + + if (action_initiated) + { + if (mAutoTurnOffTimerArmed && new_state == kState_OffInitiated) + { + // If auto turn off timer has been armed and someone initiates turning off, + // cancel the timer and continue as normal. + mAutoTurnOffTimerArmed = false; + + CancelTimer(); + } + + 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 LightingManager::StartTimer(uint32_t aTimeoutMs) +{ + if (xTimerIsTimerActive(sLightTimer)) + { + EFR32_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(sLightTimer, (aTimeoutMs / portTICK_PERIOD_MS), 100) != pdPASS) + { + EFR32_LOG("sLightTimer timer start() failed"); + appError(APP_ERROR_START_TIMER_FAILED); + } +} + +void LightingManager::CancelTimer(void) +{ + if (xTimerStop(sLightTimer, 0) == pdFAIL) + { + EFR32_LOG("sLightTimer stop() failed"); + appError(APP_ERROR_STOP_TIMER_FAILED); + } +} + +void LightingManager::TimerEventHandler(TimerHandle_t xTimer) +{ + // Get light obj context from timer id. + LightingManager * light = static_cast(pvTimerGetTimerID(xTimer)); + + // The timer event handler will be called in the context of the timer task + // once sLightTimer 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 = light; + if (light->mAutoTurnOffTimerArmed) + { + event.Handler = AutoTurnOffTimerEventHandler; + } + else + { + event.Handler = ActuatorMovementTimerEventHandler; + } + GetAppTask().PostEvent(&event); +} + +void LightingManager::AutoTurnOffTimerEventHandler(AppEvent * aEvent) +{ + LightingManager * light = static_cast(aEvent->TimerEvent.Context); + int32_t actor = 0; + + // Make sure auto turn off timer is still armed. + if (!light->mAutoTurnOffTimerArmed) + { + return; + } + + light->mAutoTurnOffTimerArmed = false; + + EFR32_LOG("Auto Turn Off has been triggered!"); + + light->InitiateAction(actor, OFF_ACTION); +} + +void LightingManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent) +{ + Action_t actionCompleted = INVALID_ACTION; + + LightingManager * light = static_cast(aEvent->TimerEvent.Context); + + if (light->mState == kState_OffInitiated) + { + light->mState = kState_OffCompleted; + actionCompleted = OFF_ACTION; + } + else if (light->mState == kState_OnInitiated) + { + light->mState = kState_OnCompleted; + actionCompleted = ON_ACTION; + } + + if (actionCompleted != INVALID_ACTION) + { + if (light->mActionCompleted_CB) + { + light->mActionCompleted_CB(actionCompleted); + } + + if (light->mAutoTurnOff && actionCompleted == ON_ACTION) + { + // Start the timer for auto turn off + light->StartTimer(light->mAutoTurnOffDuration * 1000); + + light->mAutoTurnOffTimerArmed = true; + + EFR32_LOG("Auto Turn off enabled. Will be triggered in %u seconds", light->mAutoTurnOffDuration); + } + } +} diff --git a/examples/chef/efr32/src/ZclCallbacks.cpp b/examples/chef/efr32/src/ZclCallbacks.cpp new file mode 100644 index 00000000000000..92520e5111309b --- /dev/null +++ b/examples/chef/efr32/src/ZclCallbacks.cpp @@ -0,0 +1,66 @@ +/* + * + * 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 "LightingManager.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 == 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/chef/efr32/src/main.cpp b/examples/chef/efr32/src/main.cpp new file mode 100644 index 00000000000000..21e8296ead1ac8 --- /dev/null +++ b/examples/chef/efr32/src/main.cpp @@ -0,0 +1,61 @@ +/* + * + * 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_efrPlatform.h" +#include "sl_simple_button_instances.h" +#include "sl_system_kernel.h" +#include + +#define BLE_DEV_NAME "SiLabs-Chef-App" +using namespace ::chip; +using namespace ::chip::Inet; +using namespace ::chip::DeviceLayer; + +#define UNUSED_PARAMETER(a) (a = a) + +volatile int apperror_cnt; +// ================================================================================ +// Main Code +// ================================================================================ +int main(void) +{ + init_efrPlatform(); + if (EFR32MatterConfig::InitMatter(BLE_DEV_NAME) != CHIP_NO_ERROR) + appError(CHIP_ERROR_INTERNAL); + + EFR32_LOG("Starting App Task"); + if (GetAppTask().StartAppTask() != CHIP_NO_ERROR) + appError(CHIP_ERROR_INTERNAL); + + EFR32_LOG("Starting FreeRTOS scheduler"); + sl_system_kernel_start(); + + // Should never get here. + chip::Platform::MemoryShutdown(); + EFR32_LOG("vTaskStartScheduler() failed"); + appError(CHIP_ERROR_INTERNAL); +} + +void sl_button_on_change(const sl_button_t * handle) +{ + GetAppTask().ButtonEventHandler(handle, sl_button_get_state(handle)); +} diff --git a/examples/chef/efr32/third_party/connectedhomeip b/examples/chef/efr32/third_party/connectedhomeip new file mode 120000 index 00000000000000..c866b86874994d --- /dev/null +++ b/examples/chef/efr32/third_party/connectedhomeip @@ -0,0 +1 @@ +../../../.. \ No newline at end of file diff --git a/examples/chef/efr32/with_pw_rpc.gni b/examples/chef/efr32/with_pw_rpc.gni new file mode 100644 index 00000000000000..c705b92b12f6fd --- /dev/null +++ b/examples/chef/efr32/with_pw_rpc.gni @@ -0,0 +1,31 @@ +# 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/efr32/args.gni") + +efr32_sdk_target = get_label_info(":sdk", "label_no_toolchain") + +chip_enable_pw_rpc = true +chip_enable_openthread = true +chip_build_pw_trace_lib = true + +cpp_standard = "gnu++17" + +# Light app on EFR enables tracing server +pw_trace_BACKEND = "$dir_pw_trace_tokenized"