From d7caeecf47f029817ef154c57e6856c4f4c4e8a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Ku=C4=87ma?= Date: Thu, 7 Sep 2023 15:52:24 +0200 Subject: [PATCH] Anjay-zephyr-client 23.09 Features - Added Bubblemaker app - Added tmo_dev_edge board as a target for demo and minimal samples with the possibility to switch preferred network bearer and perform firmware update using the external flash --- CHANGELOG.md | 9 +- README.md | 28 ++ bubblemaker/CMakeLists.txt | 110 ++++ bubblemaker/Kconfig | 12 + bubblemaker/README.md | 55 ++ .../boards/nrf7002dk_nrf5340_cpuapp.conf | 57 +++ .../boards/nrf7002dk_nrf5340_cpuapp.overlay | 139 ++++++ bubblemaker/boards/nrf9160dk_nrf9160_ns.conf | 87 ++++ .../boards/nrf9160dk_nrf9160_ns.overlay | 141 ++++++ bubblemaker/boards/nrf_led_strip_bindings.h | 14 + .../pm_static_nrf9160dk_nrf9160_ns.yml | 121 +++++ bubblemaker/prj.conf | 39 ++ bubblemaker/src/bubblemaker.c | 116 +++++ bubblemaker/src/bubblemaker.h | 36 ++ bubblemaker/src/led_strip.c | 195 ++++++++ bubblemaker/src/led_strip.h | 19 + bubblemaker/src/main_app.c | 200 ++++++++ bubblemaker/src/peripherals.h | 53 ++ bubblemaker/src/sensors.c | 469 ++++++++++++++++++ bubblemaker/src/sensors.h | 23 + bubblemaker/src/status_led.c | 77 +++ bubblemaker/src/status_led.h | 30 ++ bubblemaker/src/water_meter.c | 467 +++++++++++++++++ bubblemaker/src/water_meter.h | 39 ++ bubblemaker/src/water_pump.c | 391 +++++++++++++++ bubblemaker/src/water_pump.h | 35 ++ bubblemaker/west-nrf.yml | 32 ++ demo/README.md | 64 ++- demo/boards/disco_l475_iot1.conf | 2 +- demo/boards/nrf9160dk_nrf9160_ns.conf | 4 +- .../boards/nrf9160dk_nrf9160_ns_extflash.conf | 4 +- demo/boards/thingy91_nrf9160_ns.conf | 5 +- demo/boards/tmo_dev_edge.conf | 101 ++++ demo/boards/tmo_dev_edge.overlay | 17 + demo/boards/tmo_dev_edge_partitions.overlay | 30 ++ .../mcuboot/boards/tmo_dev_edge.conf | 6 + .../mcuboot/boards/tmo_dev_edge.overlay | 7 + demo/runtime_cert.conf | 4 + demo/src/status_led.c | 23 +- demo/west-nrf.yml | 2 +- demo/west-t-mobile.yml | 36 ++ demo/west.yml | 2 +- ei_demo/boards/thingy91_nrf9160_ns.conf | 1 + ei_demo/west-nrf.yml | 2 +- minimal/README.md | 15 + minimal/boards/tmo_dev_edge.conf | 70 +++ minimal/west-nrf.yml | 2 +- minimal/west-t-mobile.yml | 36 ++ minimal/west.yml | 2 +- 49 files changed, 3399 insertions(+), 30 deletions(-) create mode 100644 bubblemaker/CMakeLists.txt create mode 100644 bubblemaker/Kconfig create mode 100644 bubblemaker/README.md create mode 100644 bubblemaker/boards/nrf7002dk_nrf5340_cpuapp.conf create mode 100644 bubblemaker/boards/nrf7002dk_nrf5340_cpuapp.overlay create mode 100644 bubblemaker/boards/nrf9160dk_nrf9160_ns.conf create mode 100644 bubblemaker/boards/nrf9160dk_nrf9160_ns.overlay create mode 100644 bubblemaker/boards/nrf_led_strip_bindings.h create mode 100644 bubblemaker/pm_static_nrf9160dk_nrf9160_ns.yml create mode 100644 bubblemaker/prj.conf create mode 100644 bubblemaker/src/bubblemaker.c create mode 100644 bubblemaker/src/bubblemaker.h create mode 100644 bubblemaker/src/led_strip.c create mode 100644 bubblemaker/src/led_strip.h create mode 100644 bubblemaker/src/main_app.c create mode 100644 bubblemaker/src/peripherals.h create mode 100644 bubblemaker/src/sensors.c create mode 100644 bubblemaker/src/sensors.h create mode 100644 bubblemaker/src/status_led.c create mode 100644 bubblemaker/src/status_led.h create mode 100644 bubblemaker/src/water_meter.c create mode 100644 bubblemaker/src/water_meter.h create mode 100644 bubblemaker/src/water_pump.c create mode 100644 bubblemaker/src/water_pump.h create mode 100644 bubblemaker/west-nrf.yml create mode 100644 demo/boards/tmo_dev_edge.conf create mode 100644 demo/boards/tmo_dev_edge.overlay create mode 100644 demo/boards/tmo_dev_edge_partitions.overlay create mode 100644 demo/child_image/mcuboot/boards/tmo_dev_edge.conf create mode 100644 demo/child_image/mcuboot/boards/tmo_dev_edge.overlay create mode 100644 demo/runtime_cert.conf create mode 100644 demo/west-t-mobile.yml create mode 100644 minimal/boards/tmo_dev_edge.conf create mode 100644 minimal/west-t-mobile.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 60ead42..33f3651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 23.09 (September 7th, 2023) + +### Features +- Added Bubblemaker app +- Added tmo_dev_edge board as a target for demo and minimal samples with the possibility to switch + preferred network bearer and perform firmware update using the external flash + ## 23.06 (June 23rd, 2023) ### Features @@ -10,7 +17,7 @@ - Updated sdk-nrf to 2.3.0 - Updated Anjay-zephyr to 3.4.1 - Adjusted partition layout and MCUboot configuration on nRF9160DK builds to - support newer version of sdk-nrf + support the newer version of sdk-nrf - Added overlay for nRF9160DK in demo application which enables experimental Advanced Firmware Update object (/33629) ### Bugfixes diff --git a/README.md b/README.md index e13156a..dc57d6c 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ The following examples are present: nRF52840 Development kit
nRF7002 Development kit
Arduino Nano 33 BLE Sense Lite
+ DevEdge
any other board of your choice (by adding appropriate *.conf/*.overlay files). FOTA support for selected boards. @@ -43,6 +44,7 @@ The following examples are present: ESP32-DevKitC
nRF52840 Development kit
Arduino Nano 33 BLE Sense Lite
+ DevEdge
@@ -52,6 +54,14 @@ The following examples are present: Nordic Thingy:91 Prototyping kit + + bubblemaker/ + + An interactive example containing integration of water meters and different sensors with LwM2M protocol. Supported boards:
+ nRF9160 Development kit
+ nRF7002 Development kit
+ + @@ -137,6 +147,24 @@ multiple configurations for different targets). Choose your target and click "Build Configuration" in order to build the application. Next builds can be started by choosing Actions -> Build. +### Enabling GPS implementation (for nRF9160 targets) + +Anjay-zephyr-client has the support for GPS implementation for nRF9160 targets +which can be enabled using `Kconfig` options. Some of the available options are: +- `CONFIG_ANJAY_ZEPHYR_GPS` - enables/disables support for GPS +- `CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS` - enables/disables A-GPS using Nordic +Location Services over LwM2M +- `CONFIG_ANJAY_ZEPHYR_GPS_NRF_PRIO_MODE_PERMITTED` - enables/disables support +for allowing temporary activation of the GPS priority mode + - if set, Anjay Zephyr will temporarily activate the GPS priority over LTE + idle mode **which shuts down LTE completely** and attempts to acquire the fix + in case GPS fix cannot be produced + - by default, this option is disabled and can be enabled using `Kconfig` or + shell commands (`anjay config set gps_prio_mode_permitted`) +- `CONFIG_ANJAY_ZEPHYR_GPS_NRF_PRIO_MODE_COOLDOWN` - determines (in seconds) how +much time must pass after a failed try to produce a GPS fix to enable GPS +priority mode again + ## Connecting to the LwM2M Server To connect to [Coiote IoT Device diff --git a/bubblemaker/CMakeLists.txt b/bubblemaker/CMakeLists.txt new file mode 100644 index 0000000..0b58bf7 --- /dev/null +++ b/bubblemaker/CMakeLists.txt @@ -0,0 +1,110 @@ +# Copyright 2020-2023 AVSystem +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.13.1) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(anjay_zephyr_demo) +set(root_dir ${ANJAY_ZEPHYR_CLIENT_DIR}) + +if(CONFIG_PARTITION_MANAGER_ENABLED) + # defined in nrf/cmake/partition_manager.cmake + if(NOT EXISTS "${static_configuration_file}") + set(pm_missing_message "Missing static partition manager file for board: ${BOARD}") + + if(ACTIVE_BOARD_REVISION) + set(pm_missing_message "${pm_missing_message}, rev: ${ACTIVE_BOARD_REVISION}") + endif() + + if(CONF_FILE_BUILD_TYPE) + set(pm_missing_message "${pm_missing_message}, build type: ${CONF_FILE_BUILD_TYPE}") + endif() + + message(FATAL_ERROR "${pm_missing_message}") + endif() +elseif(CONFIG_ANJAY_CLIENT_BUILD_MCUBOOT_AUTOMATICALLY) + if("${CONFIG_MCUBOOT_SIGNATURE_KEY_FILE}" STREQUAL "") + if(NOT CONFIG_MCUBOOT_GENERATE_UNSIGNED_IMAGE) + message(FATAL_ERROR "Either MCUBOOT_SIGNATURE_KEY_FILE or MCUBOOT_GENERATE_UNSIGNED_IMAGE need to be set in Kconfig") + endif() + else() + set(MCUBOOT_SIGNATURE_KEY_FILE "${CONFIG_MCUBOOT_SIGNATURE_KEY_FILE}") + if(NOT IS_ABSOLUTE "${MCUBOOT_SIGNATURE_KEY_FILE}") + set(MCUBOOT_SIGNATURE_KEY_FILE "${WEST_TOPDIR}/${MCUBOOT_SIGNATURE_KEY_FILE}") + endif() + endif() + make_directory("${CMAKE_CURRENT_BINARY_DIR}/mcuboot") + execute_process(COMMAND "${CMAKE_COMMAND}" + -GNinja + "-DAPPLICATION_CONFIG_DIR=${CMAKE_CURRENT_SOURCE_DIR}/child_image/mcuboot" + "-DBOARD=${BOARD}" + "-DCONF_FILE=${ZEPHYR_BASE}/../bootloader/mcuboot/boot/zephyr/prj.conf" + "-DCONF_FILE_INCLUDE_FRAGMENTS=ON" + "-DCONFIG_BOOT_SIGNATURE_KEY_FILE=\"${MCUBOOT_SIGNATURE_KEY_FILE}\"" + "-DDTC_OVERLAY_FILE=${CMAKE_CURRENT_SOURCE_DIR}/child_image/mcuboot/boards/${BOARD}.overlay" + "${ZEPHYR_BASE}/../bootloader/mcuboot/boot/zephyr" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mcuboot" + RESULT_VARIABLE MCUBOOT_CMAKE_RESULT) + if(NOT MCUBOOT_CMAKE_RESULT EQUAL 0) + message(FATAL_ERROR "Configuring MCUboot failed") + endif() + + add_custom_target(mcuboot COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_CURRENT_BINARY_DIR}/mcuboot") + + # NOTE: This is largely copied from CMakeLists.txt in Zephyr itself. + # However, using that would require setting HEX_FILES_TO_MERGE *before* + # find_package(Zephyr), and we can't do that because we need to examine + # Kconfig variables, which are populated by find_package(Zephyr). + # That's why we do it ourselves here... + set(MERGED_HEX_NAME "${CMAKE_CURRENT_BINARY_DIR}/zephyr/merged.hex") + # In Zephyr 3.2, mergehex.py has been moved + # from scripts/mergehex.py to scripts/build/mergehex.py + set(MERGEHEX_SCRIPT "${ZEPHYR_BASE}/scripts/build/mergehex.py") + if(NOT EXISTS "${MERGEHEX_SCRIPT}") + set(MERGEHEX_SCRIPT "${ZEPHYR_BASE}/scripts/mergehex.py") + endif() + add_custom_command(OUTPUT "${MERGED_HEX_NAME}" + COMMAND "${PYTHON_EXECUTABLE}" + "${MERGEHEX_SCRIPT}" + -o "${MERGED_HEX_NAME}" + "${CMAKE_CURRENT_BINARY_DIR}/mcuboot/zephyr/zephyr.hex" + "${CMAKE_CURRENT_BINARY_DIR}/zephyr/zephyr.signed.hex" + DEPENDS mcuboot zephyr_final) + add_custom_target(mergehex ALL DEPENDS "${MERGED_HEX_NAME}") + + if(TARGET runners_yaml_props_target) + get_target_property(RUNNERS_YAML_CONTENTS runners_yaml_props_target yaml_contents) + string(REGEX REPLACE "zephyr[a-zA-Z0-9._-]*[.]hex" "merged.hex" RUNNERS_YAML_CONTENTS "${RUNNERS_YAML_CONTENTS}") + set_property(TARGET runners_yaml_props_target PROPERTY yaml_contents "${RUNNERS_YAML_CONTENTS}") + endif() +endif() + +set(app_sources + src/main_app.c + src/sensors.c + src/sensors.h + src/status_led.c + src/status_led.h + src/peripherals.h + src/bubblemaker.c + src/bubblemaker.h + src/led_strip.c + src/led_strip.h + src/water_meter.c + src/water_meter.h + src/water_pump.c + src/water_pump.h) + +target_sources(app PRIVATE + ${app_sources}) diff --git a/bubblemaker/Kconfig b/bubblemaker/Kconfig new file mode 100644 index 0000000..1e2f645 --- /dev/null +++ b/bubblemaker/Kconfig @@ -0,0 +1,12 @@ +menu "anjay-zephyr-client-app" + +config ANJAY_CLIENT_BUILD_MCUBOOT_AUTOMATICALLY + bool "Build MCUboot as part of the main build process" + depends on BOOTLOADER_MCUBOOT + depends on !PARTITION_MANAGER_ENABLED + select MCUBOOT_GENERATE_CONFIRMED_IMAGE + default y + +endmenu + +source "Kconfig.zephyr" diff --git a/bubblemaker/README.md b/bubblemaker/README.md new file mode 100644 index 0000000..e90726d --- /dev/null +++ b/bubblemaker/README.md @@ -0,0 +1,55 @@ +# Bubblemaker [](http://www.avsystem.com/) + +## Supported hardware and overview +This folder contains support for the following targets: + - [nrf9160dk_nrf9160_ns](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/ug_nrf9160.html) + - [nrf7002dk_nrf5340_cpuapp](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/device_guides/working_with_nrf/nrf70/gs.html) + + The following LwM2M Objects are supported: + - Security (/0) + - Server (/1) + - Device (/3) + - Connectivity Monitoring (/4) + - Firmware Update (/5) + - Sensors: + - Temperature (/3303) with multiple DS18B20 sensors on single 1Wire line + - Pressure (/3323) + - Acidity (/3326) + - On/Off switch (/3342) + - Push button (/3347) + - Water meter (/3424) + +The Bubblemaker contains an example smart water meter demo with basic IPSO +sensor support. It is possible to use a single water meter with an electrical or +hand pump as well as two water meters with two hand pumps. When using two water +meters, two players can compete and the winner is indicated through led strip +light. + +To exclude sensor in a build, it is enough to comment or delete it's name in +the `aliases` section or it's name and corresponding io-channel in the +`zephyr,user` section in the `boards/.overlay` file. For example, +if the final configuration for `nRF9160 DK` should not contain temperature +sensor #1, water pump and pressure sensors, the +`boards/nrf9160dk_nrf9160_ns.overlay` file should look like this: +``` +/ { + aliases { + push-button-0 = &button0; + push-button-1 = &button1; + switch-0 = &button2; + switch-1 = &button3; + status-led = &led0; + temperature-0 = &ds18b200; + //temperature-1 = &ds18b201; + led-strip = &led_strip; + water-meter-0 = &water_meter0; + water-meter-1 = &water_meter1; + //water-pump-0 = &water_pump0; + }; + zephyr,user { + /* these settings act as aliases for ADC sensors */ + io-channels = <&adc 2>, <&adc 3>; + io-channel-names = "acidity0", "acidity1"; + }; +/* rest of the file */ +``` diff --git a/bubblemaker/boards/nrf7002dk_nrf5340_cpuapp.conf b/bubblemaker/boards/nrf7002dk_nrf5340_cpuapp.conf new file mode 100644 index 0000000..7967d02 --- /dev/null +++ b/bubblemaker/boards/nrf7002dk_nrf5340_cpuapp.conf @@ -0,0 +1,57 @@ +# anjay-zephyr-client +CONFIG_ANJAY_ZEPHYR_DEVICE_MANUFACTURER="Nordic Semiconductor" +CONFIG_ANJAY_ZEPHYR_MODEL_NUMBER="nRF7002DK" + +# Anjay Settings +CONFIG_ANJAY_COMPAT_MBEDTLS=y +CONFIG_ANJAY_COMPAT_NET=y +CONFIG_ANJAY_COMPAT_TIME=y +CONFIG_ANJAY_LOG_LEVEL_INF=y +CONFIG_ANJAY_WITH_NET_STATS=n +CONFIG_ANJAY_WITH_SENML_JSON=y +CONFIG_ANJAY_WITH_TRACE_LOGS=n +CONFIG_ANJAY_WITH_ACCESS_CONTROL=n + +# General Settings +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 +CONFIG_LOG_MAX_LEVEL=3 +CONFIG_POSIX_MAX_FDS=16 + +# Networking +CONFIG_NET_NATIVE=y +CONFIG_NET_DHCPV4=y +CONFIG_DNS_RESOLVER=y + +# WiFi +CONFIG_WIFI=y +CONFIG_WIFI_NRF700X=y +CONFIG_WPA_SUPP=y +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_NET_L2_WIFI_MGMT_LOG_LEVEL_ERR=y +CONFIG_NET_L2_ETHERNET=y + +# Clock synchronization +CONFIG_DATE_TIME=y +CONFIG_DATE_TIME_UPDATE_INTERVAL_SECONDS=600 +CONFIG_DATE_TIME_TOO_OLD_SECONDS=600 +CONFIG_DATE_TIME_NTP_QUERY_TIME_SECONDS=5 +CONFIG_DATE_TIME_THREAD_STACK_SIZE=16384 + +# File system +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_MPU_ALLOW_FLASH_WRITE=y + +# Heap +CONFIG_HEAP_MEM_POOL_SIZE=200000 + +# Bubblemaker +CONFIG_SENSOR=y +CONFIG_ADC=y +CONFIG_W1=y + +CONFIG_LED_STRIP=y +CONFIG_WS2812_STRIP=y +CONFIG_SPI=y + +CONFIG_GPIO=y diff --git a/bubblemaker/boards/nrf7002dk_nrf5340_cpuapp.overlay b/bubblemaker/boards/nrf7002dk_nrf5340_cpuapp.overlay new file mode 100644 index 0000000..a68c4ba --- /dev/null +++ b/bubblemaker/boards/nrf7002dk_nrf5340_cpuapp.overlay @@ -0,0 +1,139 @@ +#include +#include "nrf_led_strip_bindings.h" + +/ { + aliases { + push-button-0 = &button0; + push-button-1 = &button1; + status-led = &led0; + light-control-0 = &led1; + temperature-0 = &ds18b200; + temperature-1 = &ds18b201; + led-strip = &led_strip; + water-meter-0 = &water_meter0; + water-meter-1 = &water_meter1; + //water-pump-0 = &water_pump0; + }; + zephyr,user { + /* these settings act as aliases for ADC sensors */ + io-channels = <&adc 0>, <&adc 1>, <&adc 2>, <&adc 3>; + io-channel-names = "pressure0", "pressure1", "acidity0", "acidity1"; + }; + water_meters { + compatible = "gpio-keys"; + water_meter0: water_meter_0 { + gpios = <&gpio0 27 GPIO_ACTIVE_LOW>; + }; + water_meter1: water_meter_1 { + gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; + }; + }; + water_pumps { + compatible = "gpio-keys"; + water_pump0: water_pump_0 { + gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&arduino_spi { + status = "okay"; + compatible = "nordic,nrf-spim"; + led_strip: ws2812@0 { + compatible = "worldsemi,ws2812-spi"; + + /* SPI */ + reg = <0>; /* ignored, but necessary for SPI bindings */ + spi-max-frequency = ; + + /* WS2812 */ + chain-length = <32>; /* arbitrary; change at will */ + color-mapping = ; + spi-one-frame = ; + spi-zero-frame = ; + }; +}; + +&adc { + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,input-positive = ; + zephyr,oversampling = <8>; + }; + + channel@1 { + reg = <1>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,input-positive = ; + zephyr,oversampling = <8>; + }; + + channel@2 { + reg = <2>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,input-positive = ; + zephyr,oversampling = <8>; + }; + + channel@3 { + reg = <3>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,input-positive = ; + zephyr,oversampling = <8>; + }; + }; + + &uart1 { + status = "okay"; + + w1: w1-zephyr-serial-0 { + compatible = "zephyr,w1-serial"; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + ds18b200: ds18b20_0 { + compatible = "maxim,ds18b20"; + family-code = <0x28>; + resolution = <12>; + status = "okay"; + }; + ds18b201: ds18b20_1 { + compatible = "maxim,ds18b20"; + family-code = <0x28>; + resolution = <12>; + status = "okay"; + }; + }; +}; + +&pinctrl { + uart1_default: uart1_default { + group1 { + psels = ; + nordic,drive-mode = ; + }; + group2 { + psels = ; + bias-pull-up; + }; + }; +}; diff --git a/bubblemaker/boards/nrf9160dk_nrf9160_ns.conf b/bubblemaker/boards/nrf9160dk_nrf9160_ns.conf new file mode 100644 index 0000000..88452eb --- /dev/null +++ b/bubblemaker/boards/nrf9160dk_nrf9160_ns.conf @@ -0,0 +1,87 @@ +# anjay-zephyr-client +CONFIG_ANJAY_ZEPHYR_DEVICE_MANUFACTURER="AVSystem" +CONFIG_ANJAY_ZEPHYR_MODEL_NUMBER="Bubblemaker" +CONFIG_ANJAY_ZEPHYR_GPS=n +CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS=n + +CONFIG_ANJAY_ZEPHYR_FOTA=y + +# Anjay Settings +CONFIG_ANJAY_COMPAT_ZEPHYR_TLS=y +CONFIG_ANJAY_COMPAT_NET=y +CONFIG_ANJAY_COMPAT_TIME=y +CONFIG_ANJAY_LOG_LEVEL_INF=y +CONFIG_ANJAY_WITH_NET_STATS=n +CONFIG_ANJAY_WITH_SENML_JSON=y +CONFIG_ANJAY_WITH_TRACE_LOGS=n +CONFIG_ANJAY_WITH_ACCESS_CONTROL=n + +# General Settings +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 +CONFIG_LOG_RUNTIME_FILTERING=n +CONFIG_LOG_CMDS=n +CONFIG_LOG_MAX_LEVEL=3 +CONFIG_HWINFO=n + +# Networking +CONFIG_NET_NATIVE=n +CONFIG_NET_IF_MAX_IPV4_COUNT=2 +CONFIG_NET_TCP_ISN_RFC6528=n +CONFIG_NET_LOG=n + +# nRF LTE +CONFIG_NRF_MODEM_LIB=y +CONFIG_MODEM_INFO=y +CONFIG_LTE_LINK_CONTROL=y +CONFIG_LTE_NETWORK_MODE_LTE_M_GPS=y +CONFIG_LTE_AUTO_INIT_AND_CONNECT=n +CONFIG_LTE_NETWORK_TIMEOUT=600 +CONFIG_NRF_CLOUD_AGPS=y +CONFIG_NRF_CLOUD_GPS_LOG_LEVEL_INF=y + +# Clock synchronization +CONFIG_DATE_TIME=y +CONFIG_DATE_TIME_UPDATE_INTERVAL_SECONDS=600 +CONFIG_DATE_TIME_TOO_OLD_SECONDS=600 +CONFIG_DATE_TIME_NTP_QUERY_TIME_SECONDS=5 + +# File system +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_MPU_ALLOW_FLASH_WRITE=y + +# Heap +CONFIG_HEAP_MEM_POOL_SIZE=2048 + +# MCUboot +CONFIG_BOOTLOADER_MCUBOOT=y +CONFIG_IMG_MANAGER=y +CONFIG_IMG_ERASE_PROGRESSIVELY=y +CONFIG_MCUBOOT_IMAGE_VERSION="23.9.0" + +# Shell settings +CONFIG_SHELL_MINIMAL=y +CONFIG_SHELL_WILDCARD=n +CONFIG_SHELL_VT100_COMMANDS=y +CONFIG_SHELL_VT100_COLORS=n +CONFIG_SHELL_STATS=n +CONFIG_SHELL_CMDS=n +CONFIG_SHELL_TAB=y +CONFIG_SHELL_TAB_AUTOCOMPLETION=y +CONFIG_SHELL_CMDS_RESIZE=n +CONFIG_DEVICE_SHELL=n +CONFIG_DATE_SHELL=n +CONFIG_DEVMEM_SHELL=n +CONFIG_MCUBOOT_SHELL=n +CONFIG_KERNEL_SHELL=y + +# Bubblemaker +CONFIG_SENSOR=y +CONFIG_ADC=y +CONFIG_W1=y + +CONFIG_LED_STRIP=y +CONFIG_WS2812_STRIP=y +CONFIG_SPI=y + +CONFIG_GPIO=y diff --git a/bubblemaker/boards/nrf9160dk_nrf9160_ns.overlay b/bubblemaker/boards/nrf9160dk_nrf9160_ns.overlay new file mode 100644 index 0000000..1227113 --- /dev/null +++ b/bubblemaker/boards/nrf9160dk_nrf9160_ns.overlay @@ -0,0 +1,141 @@ +#include +#include "nrf_led_strip_bindings.h" + +/ { + aliases { + push-button-0 = &button0; + push-button-1 = &button1; + switch-0 = &button2; + switch-1 = &button3; + status-led = &led0; + temperature-0 = &ds18b200; + temperature-1 = &ds18b201; + led-strip = &led_strip; + water-meter-0 = &water_meter0; + water-meter-1 = &water_meter1; + //water-pump-0 = &water_pump0; + }; + zephyr,user { + /* these settings act as aliases for ADC sensors */ + io-channels = <&adc 0>, <&adc 1>, <&adc 2>, <&adc 3>; + io-channel-names = "pressure0", "pressure1", "acidity0", "acidity1"; + }; + water_meters { + compatible = "gpio-keys"; + water_meter0: water_meter_0 { + gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; + }; + water_meter1: water_meter_1 { + gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; + }; + }; + water_pumps { + compatible = "gpio-keys"; + water_pump0: water_pump_0 { + gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&arduino_spi { + compatible = "nordic,nrf-spim"; + led_strip: ws2812@0 { + compatible = "worldsemi,ws2812-spi"; + + /* SPI */ + reg = <0>; /* ignored, but necessary for SPI bindings */ + spi-max-frequency = ; + + /* WS2812 */ + chain-length = <32>; /* arbitrary; change at will */ + color-mapping = ; + spi-one-frame = ; + spi-zero-frame = ; + }; +}; + +&adc { + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,input-positive = ; + zephyr,oversampling = <8>; + }; + + channel@1 { + reg = <1>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,input-positive = ; + zephyr,oversampling = <8>; + }; + + channel@2 { + reg = <2>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,input-positive = ; + zephyr,oversampling = <8>; + }; + + channel@3 { + reg = <3>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,input-positive = ; + zephyr,oversampling = <8>; + }; +}; + +&uart1 { + status = "okay"; + + w1: w1-zephyr-serial-0 { + compatible = "zephyr,w1-serial"; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + ds18b200: ds18b20_0 { + compatible = "maxim,ds18b20"; + family-code = <0x28>; + resolution = <12>; + status = "okay"; + }; + ds18b201: ds18b20_1 { + compatible = "maxim,ds18b20"; + family-code = <0x28>; + resolution = <12>; + status = "okay"; + }; + }; +}; + +&pinctrl { + uart1_default: uart1_default { + group1 { + psels = , + ; + nordic,drive-mode = ; + }; + group2 { + psels = , + ; + bias-pull-up; + }; + }; +}; diff --git a/bubblemaker/boards/nrf_led_strip_bindings.h b/bubblemaker/boards/nrf_led_strip_bindings.h new file mode 100644 index 0000000..37905ab --- /dev/null +++ b/bubblemaker/boards/nrf_led_strip_bindings.h @@ -0,0 +1,14 @@ +#ifndef ZEPHYR_SAMPLES_DRIVERS_LED_WS2812_H_ +#define ZEPHYR_SAMPLES_DRIVERS_LED_WS2812_H_ + +/* + * At 4 MHz, 1 bit is 250 ns, so 3 bits is 750 ns. + * + * That's cutting it a bit close to the edge of the timing parameters, + * but it seems to work on AdaFruit LED rings. + */ +#define SPI_FREQ 4000000 +#define ZERO_FRAME 0x40 +#define ONE_FRAME 0x70 + +#endif diff --git a/bubblemaker/pm_static_nrf9160dk_nrf9160_ns.yml b/bubblemaker/pm_static_nrf9160dk_nrf9160_ns.yml new file mode 100644 index 0000000..46f9fda --- /dev/null +++ b/bubblemaker/pm_static_nrf9160dk_nrf9160_ns.yml @@ -0,0 +1,121 @@ +EMPTY_0: + address: 0xf8000 + end_address: 0xfe000 + placement: + after: + - mcuboot_scratch + region: flash_primary + size: 0x6000 +EMPTY_1: + address: 0xe0000 + end_address: 0xe8000 + placement: + after: + - mcuboot_secondary + region: flash_primary + size: 0x8000 +app: + address: 0x18000 + end_address: 0x78000 + region: flash_primary + size: 0x60000 +mcuboot: + address: 0x0 + end_address: 0x10000 + placement: + before: + - mcuboot_primary + region: flash_primary + size: 0x10000 +mcuboot_pad: + address: 0x10000 + end_address: 0x10200 + placement: + align: + start: 0x8000 + before: + - mcuboot_primary_app + region: flash_primary + size: 0x200 +mcuboot_primary: + address: 0x10000 + end_address: 0x78000 + orig_span: &id001 + - mcuboot_pad + - app + - tfm + region: flash_primary + sharers: 0x1 + size: 0x68000 + span: *id001 +mcuboot_primary_app: + address: 0x10200 + end_address: 0x78000 + orig_span: &id002 + - app + - tfm + region: flash_primary + size: 0x67e00 + span: *id002 +mcuboot_scratch: + address: 0xe8000 + end_address: 0xf8000 + placement: + after: + - app + align: + start: 0x8000 + region: flash_primary + size: 0x10000 +mcuboot_secondary: + address: 0x78000 + end_address: 0xe0000 + placement: + after: + - mcuboot_primary + align: + start: 0x8000 + region: flash_primary + share_size: + - mcuboot_primary + size: 0x68000 +nonsecure_storage: + address: 0xfe000 + end_address: 0x100000 + orig_span: &id003 + - settings_storage + placement: + before: + - end + region: flash_primary + size: 0x2000 + span: *id003 +settings_storage: + address: 0xfe000 + end_address: 0x100000 + inside: + - nonsecure_storage + placement: + before: + - end + region: flash_primary + size: 0x2000 +tfm: + address: 0x10200 + end_address: 0x18000 + inside: + - mcuboot_primary_app + placement: + before: + - app + region: flash_primary + size: 0x7e00 +tfm_secure: + address: 0x10000 + end_address: 0x18000 + orig_span: &id004 + - mcuboot_pad + - tfm + region: flash_primary + size: 0x8000 + span: *id004 diff --git a/bubblemaker/prj.conf b/bubblemaker/prj.conf new file mode 100644 index 0000000..c74a6a6 --- /dev/null +++ b/bubblemaker/prj.conf @@ -0,0 +1,39 @@ +# Anjay Settings +CONFIG_ANJAY=y + +# Anjay Zephyr Settings +CONFIG_ANJAY_ZEPHYR_SHELL=y + +# General settings +CONFIG_NEWLIB_LIBC=y +CONFIG_NEWLIB_LIBC_NANO=y +CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y +CONFIG_LOG=y +CONFIG_REBOOT=y +CONFIG_FPU=y +CONFIG_PRINTK=y +CONFIG_SHELL=y + +# Networking +CONFIG_NETWORKING=y +CONFIG_NET_MGMT=y +CONFIG_NET_MGMT_EVENT=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=n +CONFIG_NET_TCP=n +CONFIG_NET_LOG=y + +# Logging +CONFIG_LOG_MODE_DEFERRED=y +CONFIG_LOG_BLOCK_IN_THREAD=y + +# Config menu +CONFIG_HWINFO=y + +# Storage +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_NVS=y +CONFIG_SETTINGS=y +CONFIG_FLASH_LOG_LEVEL_ERR=y diff --git a/bubblemaker/src/bubblemaker.c b/bubblemaker/src/bubblemaker.c new file mode 100644 index 0000000..3839e4e --- /dev/null +++ b/bubblemaker/src/bubblemaker.c @@ -0,0 +1,116 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 "bubblemaker.h" +#include "water_pump.h" +#include "led_strip.h" +#include "water_meter.h" + +LOG_MODULE_REGISTER(bubblemaker); + +#define IDLE_STATE_DURATION K_MSEC(500) +#define RED_LIGHT_DURATION K_SECONDS(2) +#define YELLOW_LIGHT_DURATION K_SECONDS(1) +#define MEASURE_STATE_DURATION K_SECONDS(10) +#define END_STATE_DURATION K_SECONDS(3) + +static struct k_thread bubblemaker_thread; +static K_THREAD_STACK_DEFINE(bubblemaker_stack, 1024); + +enum bubblemaker_state bm_state = BUBBLEMAKER_IDLE; + +static void run_bubblemaker(void *arg1, void *arg2, void *arg3) +{ + LOG_INF("Waiting for Water meter instances to initialize..."); + + while (water_meter_is_null()) { + k_sleep(K_SECONDS(1)); + } + + while (1) { + switch (bm_state) { + case BUBBLEMAKER_IDLE: + k_sleep(IDLE_STATE_DURATION); + break; + case BUBBLEMAKER_START_RED_LIGHT: + k_sleep(RED_LIGHT_DURATION); + bm_state = BUBBLEMAKER_START_YELLOW_LIGHT; + break; + case BUBBLEMAKER_START_YELLOW_LIGHT: + k_sleep(YELLOW_LIGHT_DURATION); + bm_state = BUBBLEMAKER_MEASURE; + break; + case BUBBLEMAKER_MEASURE: + water_meter_instances_reset(); + k_sleep(MEASURE_STATE_DURATION); +#if WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + double out_volumes[2]; + + water_meter_get_cumulated_volumes(out_volumes); + bm_state = out_volumes[0] > out_volumes[1] ? BUBBLEMAKER_END_P1_WON : + BUBBLEMAKER_END_P2_WON; +#else // WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + bm_state = BUBBLEMAKER_END; +#endif // WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + break; +#if WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + case BUBBLEMAKER_END_P1_WON: + case BUBBLEMAKER_END_P2_WON: + water_meter_instances_reset(); + k_sleep(END_STATE_DURATION); + bm_state = BUBBLEMAKER_IDLE; + break; +#else // WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + case BUBBLEMAKER_END: + k_sleep(END_STATE_DURATION); + bm_state = BUBBLEMAKER_IDLE; + break; +#endif // WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + default: + AVS_UNREACHABLE("Invalid enum value"); + break; + } + } +} + +int bubblemaker_init(void) +{ + LOG_INF("Initializing Bubblemaker"); + + if (led_strip_init() || water_meter_init() +#if WATER_PUMP_0_AVAILABLE + || water_pump_initialize() +#endif // WATER_PUMP_0_AVAILABLE + ) { + return -1; + } + + if (!k_thread_create(&bubblemaker_thread, bubblemaker_stack, + K_THREAD_STACK_SIZEOF(bubblemaker_stack), run_bubblemaker, NULL, NULL, + NULL, 2, 0, K_NO_WAIT)) { + LOG_ERR("Failed to create bubblemaker thread"); + return -1; + } + + return 0; +} diff --git a/bubblemaker/src/bubblemaker.h b/bubblemaker/src/bubblemaker.h new file mode 100644 index 0000000..8ae9bb6 --- /dev/null +++ b/bubblemaker/src/bubblemaker.h @@ -0,0 +1,36 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 "water_meter.h" + +enum bubblemaker_state { + BUBBLEMAKER_IDLE, + BUBBLEMAKER_START_RED_LIGHT, + BUBBLEMAKER_START_YELLOW_LIGHT, + BUBBLEMAKER_MEASURE, +#if WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + BUBBLEMAKER_END_P1_WON, + BUBBLEMAKER_END_P2_WON +#else // WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + BUBBLEMAKER_END +#endif // WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE +}; + +extern enum bubblemaker_state bm_state; + +int bubblemaker_init(void); diff --git a/bubblemaker/src/led_strip.c b/bubblemaker/src/led_strip.c new file mode 100644 index 0000000..cc0bb70 --- /dev/null +++ b/bubblemaker/src/led_strip.c @@ -0,0 +1,195 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 "led_strip.h" +#include "bubblemaker.h" +#include "water_meter.h" + +LOG_MODULE_REGISTER(led_strip); + +#define STRIP_NODE DT_ALIAS(led_strip) +#define STRIP_NUM_PIXELS DT_PROP(STRIP_NODE, chain_length) +#define RGB(_r, _g, _b) \ + { \ + .r = (_r), .g = (_g), .b = (_b) \ + } + +static struct k_thread led_strip_thread; +static K_THREAD_STACK_DEFINE(led_strip_stack, 1024); +static const struct device *const strip = DEVICE_DT_GET(STRIP_NODE); +struct led_rgb pixels[STRIP_NUM_PIXELS]; + +enum uniform_colors { + STRIP_COLOR_RED, + STRIP_COLOR_GREEN, + STRIP_COLOR_BLUE, + STRIP_COLOR_YELLOW, +#if WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + STRIP_COLOR_P1, + STRIP_COLOR_P2, +#endif // WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + STRIP_COLOR_NONE +}; + +static const struct led_rgb colors[] = { + [STRIP_COLOR_RED] = RGB(0xff, 0x00, 0x00), [STRIP_COLOR_GREEN] = RGB(0x00, 0xff, 0x00), + [STRIP_COLOR_BLUE] = RGB(0x00, 0x00, 0xff), [STRIP_COLOR_YELLOW] = RGB(0xff, 0xff, 0x00), +#if WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + [STRIP_COLOR_P1] = RGB(0x62, 0x09, 0xff), [STRIP_COLOR_P2] = RGB(0x00, 0xb9, 0xe2), +#endif // WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + [STRIP_COLOR_NONE] = RGB(0x00, 0x00, 0x00), +}; + +static void ws2812_strip_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint8_t *r, uint8_t *g, + uint8_t *b) +{ + h %= 360; + uint32_t rgb_max = v * 255 / 100; + uint32_t rgb_min = rgb_max * (100 - s) / 100; + + uint32_t i = h / 60; + uint32_t diff = h % 60; + + uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60; + + switch (i) { + case 0: + *r = rgb_max; + *g = rgb_min + rgb_adj; + *b = rgb_min; + break; + case 1: + *r = rgb_max - rgb_adj; + *g = rgb_max; + *b = rgb_min; + break; + case 2: + *r = rgb_min; + *g = rgb_max; + *b = rgb_min + rgb_adj; + break; + case 3: + *r = rgb_min; + *g = rgb_max - rgb_adj; + *b = rgb_max; + break; + case 4: + *r = rgb_min + rgb_adj; + *g = rgb_min; + *b = rgb_max; + break; + case 5: + *r = rgb_max; + *g = rgb_min; + *b = rgb_max - rgb_adj; + break; + default: + AVS_UNREACHABLE("Invalid value"); + break; + } +} + +static void ws2812_strip_update(void) +{ + led_strip_update_rgb(strip, pixels, STRIP_NUM_PIXELS); +} + +static void ws2812_strip_set_color(enum uniform_colors color) +{ + for (size_t i = 0; i < STRIP_NUM_PIXELS; i++) { + pixels[i] = colors[color]; + } + ws2812_strip_update(); +} + +static void ws2812_strip_display_rainbow(void) +{ + static uint32_t rgb_increase; + + for (int i = 0; i < 3; i++) { + for (int j = i; j < STRIP_NUM_PIXELS; j++) { + uint32_t hue = j * 360 / STRIP_NUM_PIXELS + rgb_increase; + + ws2812_strip_hsv2rgb(hue, 100, 100, &pixels[j].r, &pixels[j].g, + &pixels[j].b); + } + ws2812_strip_update(); + k_sleep(K_MSEC(10)); + } + rgb_increase += 5; +} + +static void led_strip_task(void *arg1, void *arg2, void *arg3) +{ + ws2812_strip_set_color(STRIP_COLOR_NONE); + + while (1) { + switch (bm_state) { + case BUBBLEMAKER_IDLE: + ws2812_strip_display_rainbow(); + break; + case BUBBLEMAKER_START_RED_LIGHT: + ws2812_strip_set_color(STRIP_COLOR_RED); + break; + case BUBBLEMAKER_START_YELLOW_LIGHT: + ws2812_strip_set_color(STRIP_COLOR_YELLOW); + break; + case BUBBLEMAKER_MEASURE: + ws2812_strip_set_color(STRIP_COLOR_GREEN); + break; +#if WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + case BUBBLEMAKER_END_P1_WON: + ws2812_strip_set_color(STRIP_COLOR_P1); + break; + case BUBBLEMAKER_END_P2_WON: + ws2812_strip_set_color(STRIP_COLOR_P2); + break; +#else // WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + case BUBBLEMAKER_END: + ws2812_strip_set_color(STRIP_COLOR_RED); + break; +#endif // WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + default: + AVS_UNREACHABLE("Invalid enum value"); + } + } +} + +int led_strip_init(void) +{ + LOG_INF("Initializing led_strip"); + + if (device_is_ready(strip)) { + LOG_INF("Found LED strip device %s", strip->name); + } else { + LOG_ERR("LED strip device %s is not ready", strip->name); + return -1; + } + + if (!k_thread_create(&led_strip_thread, led_strip_stack, + K_THREAD_STACK_SIZEOF(led_strip_stack), led_strip_task, NULL, NULL, + NULL, 2, 0, K_NO_WAIT)) { + LOG_ERR("Failed to create led_strip thread"); + return -1; + } + + return 0; +} diff --git a/bubblemaker/src/led_strip.h b/bubblemaker/src/led_strip.h new file mode 100644 index 0000000..f74327b --- /dev/null +++ b/bubblemaker/src/led_strip.h @@ -0,0 +1,19 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 + +int led_strip_init(void); diff --git a/bubblemaker/src/main_app.c b/bubblemaker/src/main_app.c new file mode 100644 index 0000000..5fc3de0 --- /dev/null +++ b/bubblemaker/src/main_app.c @@ -0,0 +1,200 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 "peripherals.h" +#include "status_led.h" +#include "sensors.h" +#include "bubblemaker.h" +#include "water_pump.h" + +LOG_MODULE_REGISTER(main_app); + +static const anjay_dm_object_def_t **water_meter_obj; +#if WATER_PUMP_0_AVAILABLE +static const anjay_dm_object_def_t **power_control_obj; +#endif // WATER_PUMP_0_AVAILABLE +#if LED_COLOR_LIGHT_AVAILABLE +static const anjay_dm_object_def_t **led_color_light_obj; +#endif // LED_COLOR_LIGHT_AVAILABLE +#if SWITCH_AVAILABLE_ANY +static const anjay_dm_object_def_t **switch_obj; +#endif // SWITCH_AVAILABLE_ANY +static avs_sched_handle_t update_objects_handle; + +#if PUSH_BUTTON_AVAILABLE_ANY +static struct anjay_zephyr_ipso_button_instance buttons[] = { +#if PUSH_BUTTON_AVAILABLE(0) && !WATER_PUMP_0_AVAILABLE + PUSH_BUTTON_GLUE_ITEM(0), +#endif // PUSH_BUTTON_AVAILABLE(0) && !WATER_PUMP_0_AVAILABLE +#if PUSH_BUTTON_AVAILABLE(1) + PUSH_BUTTON_GLUE_ITEM(1), +#endif // PUSH_BUTTON_AVAILABLE(1) +#if PUSH_BUTTON_AVAILABLE(2) + PUSH_BUTTON_GLUE_ITEM(2), +#endif // PUSH_BUTTON_AVAILABLE(2) +#if PUSH_BUTTON_AVAILABLE(3) + PUSH_BUTTON_GLUE_ITEM(3), +#endif // PUSH_BUTTON_AVAILABLE(3) +}; +#endif // PUSH_BUTTON_AVAILABLE_ANY + +#if SWITCH_AVAILABLE_ANY +static struct anjay_zephyr_switch_instance switches[] = { +#if SWITCH_AVAILABLE(0) + SWITCH_BUTTON_GLUE_ITEM(0), +#endif // SWITCH_AVAILABLE(0) +#if SWITCH_AVAILABLE(1) + SWITCH_BUTTON_GLUE_ITEM(1), +#endif // SWITCH_AVAILABLE(1) +#if SWITCH_AVAILABLE(2) + SWITCH_BUTTON_GLUE_ITEM(2), +#endif // SWITCH_AVAILABLE(2) +}; +#endif // SWITCH_AVAILABLE_ANY + +static int register_objects(anjay_t *anjay) +{ + water_meter_obj = water_meter_object_create(); + if (water_meter_obj) { + anjay_register_object(anjay, water_meter_obj); + } else { + LOG_ERR("water_meter object could not be created"); + return -1; + } + +#if WATER_PUMP_0_AVAILABLE + power_control_obj = power_control_object_create(); + if (power_control_obj) { + anjay_register_object(anjay, power_control_obj); + } +#endif // WATER_PUMP_0_AVAILABLE + + basic_sensor_objects_install(anjay); +#if PUSH_BUTTON_AVAILABLE_ANY + anjay_zephyr_ipso_push_button_object_install(anjay, buttons, AVS_ARRAY_SIZE(buttons)); +#endif // PUSH_BUTTON_AVAILABLE_ANY + +#if LED_COLOR_LIGHT_AVAILABLE + led_color_light_obj = anjay_zephyr_led_color_light_object_create(DEVICE_DT_GET(RGB_NODE)); + if (led_color_light_obj) { + anjay_register_object(anjay, led_color_light_obj); + } +#endif // LED_COLOR_LIGHT_AVAILABLE + +#if SWITCH_AVAILABLE_ANY + switch_obj = anjay_zephyr_switch_object_create(switches, AVS_ARRAY_SIZE(switches)); + if (switch_obj) { + anjay_register_object(anjay, switch_obj); + } +#endif // SWITCH_AVAILABLE_ANY + return 0; +} + +static void update_objects_frequent(anjay_t *anjay) +{ +#if SWITCH_AVAILABLE_ANY + anjay_zephyr_switch_object_update(anjay, switch_obj); +#endif // SWITCH_AVAILABLE_ANY + basic_sensor_objects_update(anjay); +} + +static void update_objects(avs_sched_t *sched, const void *anjay_ptr) +{ + anjay_t *anjay = *(anjay_t *const *)anjay_ptr; + + update_objects_frequent(anjay); + + status_led_toggle(); + + AVS_SCHED_DELAYED(sched, &update_objects_handle, + avs_time_duration_from_scalar(1, AVS_TIME_S), update_objects, &anjay, + sizeof(anjay)); +} + +static int init_update_objects(anjay_t *anjay) +{ + avs_sched_t *sched = anjay_get_scheduler(anjay); + + update_objects(sched, &anjay); + + status_led_init(); + + return 0; +} + +static int clean_before_anjay_destroy(anjay_t *anjay) +{ + avs_sched_del(&update_objects_handle); + + return 0; +} + +static int release_objects(void) +{ + water_meter_object_release(water_meter_obj); +#if WATER_PUMP_0_AVAILABLE + power_control_object_release(power_control_obj); +#endif // WATER_PUMP_0_AVAILABLE +#if SWITCH_AVAILABLE_ANY + anjay_zephyr_switch_object_release(&switch_obj); +#endif // SWITCH_AVAILABLE_ANY +#if LED_COLOR_LIGHT_AVAILABLE + anjay_zephyr_led_color_light_object_release(&led_color_light_obj); +#endif // LED_COLOR_LIGHT_AVAILABLE + + return 0; +} + +int lwm2m_callback(anjay_t *anjay, enum anjay_zephyr_lwm2m_callback_reasons reason) +{ + switch (reason) { + case ANJAY_ZEPHYR_LWM2M_CALLBACK_REASON_INIT: + return register_objects(anjay); + case ANJAY_ZEPHYR_LWM2M_CALLBACK_REASON_ANJAY_READY: + return init_update_objects(anjay); + case ANJAY_ZEPHYR_LWM2M_CALLBACK_REASON_ANJAY_SHUTTING_DOWN: + return clean_before_anjay_destroy(anjay); + case ANJAY_ZEPHYR_LWM2M_CALLBACK_REASON_CLEANUP: + return release_objects(); + default: + return -1; + } +} + +void main(void) +{ + LOG_INF("Initializing Anjay-zephyr-client Bubblemaker " CONFIG_ANJAY_ZEPHYR_VERSION); + + anjay_zephyr_lwm2m_set_user_callback(lwm2m_callback); + anjay_zephyr_lwm2m_init_from_settings(); + anjay_zephyr_lwm2m_start(); + bubblemaker_init(); + + // Anjay runs in a separate thread and preceding function doesn't block + // add your own code here +} diff --git a/bubblemaker/src/peripherals.h b/bubblemaker/src/peripherals.h new file mode 100644 index 0000000..f625c49 --- /dev/null +++ b/bubblemaker/src/peripherals.h @@ -0,0 +1,53 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 + +#define RGB_NODE DT_ALIAS(rgb_pwm) +#define LED_COLOR_LIGHT_AVAILABLE DT_NODE_HAS_STATUS(RGB_NODE, okay) + +#define PUSH_BUTTON_NODE(idx) DT_ALIAS(push_button_##idx) +#define PUSH_BUTTON_AVAILABLE(idx) DT_NODE_HAS_STATUS(PUSH_BUTTON_NODE(idx), okay) +#define PUSH_BUTTON_AVAILABLE_ANY \ + (PUSH_BUTTON_AVAILABLE(0) || PUSH_BUTTON_AVAILABLE(1) || PUSH_BUTTON_AVAILABLE(2) || \ + PUSH_BUTTON_AVAILABLE(3)) + +#define SWITCH_NODE(idx) DT_ALIAS(switch_##idx) +#define SWITCH_AVAILABLE(idx) DT_NODE_HAS_STATUS(SWITCH_NODE(idx), okay) +#define SWITCH_AVAILABLE_ANY (SWITCH_AVAILABLE(0) || SWITCH_AVAILABLE(1) || SWITCH_AVAILABLE(2)) + +#define PUSH_BUTTON_GLUE_ITEM(num) \ + { \ + .device = DEVICE_DT_GET(DT_GPIO_CTLR(PUSH_BUTTON_NODE(num), gpios)), \ + .gpio_pin = DT_GPIO_PIN(PUSH_BUTTON_NODE(num), gpios), \ + .gpio_flags = (GPIO_INPUT | DT_GPIO_FLAGS(PUSH_BUTTON_NODE(num), gpios)) \ + } + +#define SWITCH_BUTTON_GLUE_ITEM(num) \ + { \ + .device = DEVICE_DT_GET(DT_GPIO_CTLR(SWITCH_NODE(num), gpios)), \ + .gpio_pin = DT_GPIO_PIN(SWITCH_NODE(num), gpios), \ + .gpio_flags = (GPIO_INPUT | DT_GPIO_FLAGS(SWITCH_NODE(num), gpios)) \ + } + +#define TEMPERATURE_0_NODE DT_ALIAS(temperature_0) +#define TEMPERATURE_0_AVAILABLE DT_NODE_HAS_STATUS(TEMPERATURE_0_NODE, okay) + +#define TEMPERATURE_1_NODE DT_ALIAS(temperature_1) +#define TEMPERATURE_1_AVAILABLE DT_NODE_HAS_STATUS(TEMPERATURE_1_NODE, okay) diff --git a/bubblemaker/src/sensors.c b/bubblemaker/src/sensors.c new file mode 100644 index 0000000..91a1af2 --- /dev/null +++ b/bubblemaker/src/sensors.c @@ -0,0 +1,469 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 "sensors.h" +#include "peripherals.h" + +#if TEMPERATURE_0_AVAILABLE && TEMPERATURE_1_AVAILABLE +#include +#endif // TEMPERATURE_0_AVAILABLE && TEMPERATURE_1_AVAILABLE + +LOG_MODULE_REGISTER(sensor); + +#define ADC_HAS_SENSOR(Sensor) DT_PROP_HAS_NAME(DT_PATH(zephyr_user), io_channels, Sensor) + +#define PRESSURE_0_AVAILABLE ADC_HAS_SENSOR(pressure0) +#define PRESSURE_1_AVAILABLE ADC_HAS_SENSOR(pressure1) +#define ACIDITY_0_AVAILABLE ADC_HAS_SENSOR(acidity0) +#define ACIDITY_1_AVAILABLE ADC_HAS_SENSOR(acidity1) + +#define ADC_DT_SPEC_GET_BY_NAME(NodeId, Name) \ + ADC_DT_SPEC_STRUCT(DT_IO_CHANNELS_CTLR_BY_NAME(NodeId, Name), \ + DT_IO_CHANNELS_INPUT_BY_NAME(NodeId, Name)) + +enum adc_channels { +#if PRESSURE_0_AVAILABLE + ADC_CHANNEL_PRESSURE_0, +#endif // PRESSURE_0_AVAILABLE +#if PRESSURE_1_AVAILABLE + ADC_CHANNEL_PRESSURE_1, +#endif // PRESSURE_1_AVAILABLE +#if ACIDITY_0_AVAILABLE + ADC_CHANNEL_ACIDITY_0, +#endif // ACIDITY_0_AVAILABLE +#if ACIDITY_1_AVAILABLE + ADC_CHANNEL_ACIDITY_1, +#endif // ACIDITY_1_AVAILABLE + _ADC_CHANNEL_NONE +}; + +static const struct adc_dt_spec available_adc_channels[] = { +#if PRESSURE_0_AVAILABLE + ADC_DT_SPEC_GET_BY_NAME(DT_PATH(zephyr_user), pressure0), +#endif // PRESSURE_0_AVAILABLE +#if PRESSURE_1_AVAILABLE + ADC_DT_SPEC_GET_BY_NAME(DT_PATH(zephyr_user), pressure1), +#endif // PRESSURE_1_AVAILABLE +#if ACIDITY_0_AVAILABLE + ADC_DT_SPEC_GET_BY_NAME(DT_PATH(zephyr_user), acidity0), +#endif // ACIDITY_0_AVAILABLE +#if ACIDITY_1_AVAILABLE + ADC_DT_SPEC_GET_BY_NAME(DT_PATH(zephyr_user), acidity1), +#endif // ACIDITY_1_AVAILABLE +}; + +#if TEMPERATURE_0_AVAILABLE && TEMPERATURE_1_AVAILABLE +static avs_init_once_handle_t ds18b20_init_handle; +static uint64_t ds18b20_rom[2]; +#endif // TEMPERATURE_0_AVAILABLE && TEMPERATURE_1_AVAILABLE +#if TEMPERATURE_0_AVAILABLE +const struct device *const temperature_dev_0 = DEVICE_DT_GET(TEMPERATURE_0_NODE); +#endif // TEMPERATURE_0_AVAILABLE +#if TEMPERATURE_1_AVAILABLE +const struct device *const temperature_dev_1 = DEVICE_DT_GET(TEMPERATURE_1_NODE); +#endif // TEMPERATURE_1_AVAILABLE + +struct basic_sensor_driver { + int (*init)(void); + int (*read)(double *out_value); + bool installed; +}; + +struct sensor_context { + anjay_oid_t oid; + size_t instances_count; + struct basic_sensor_driver *drivers; + const char *unit; +}; + +static int32_t adc_max_possible_value(enum adc_channels channel) +{ + return (1 << available_adc_channels[channel].resolution) - 1; +} + +static int32_t adc_get_raw_value(enum adc_channels channel) +{ + union { + int16_t i16; + uint16_t u16; + } buf; + + struct adc_sequence sequence = { + .buffer = &buf, + .buffer_size = sizeof(buf), + }; + int32_t ret_val; + int err; + + (void)adc_sequence_init_dt(&available_adc_channels[channel], &sequence); + err = adc_read(available_adc_channels[channel].dev, &sequence); + if (err < 0) { + LOG_ERR("Could not read %s (%d)", available_adc_channels[channel].dev->name, err); + return -1; + } + ret_val = available_adc_channels[channel].channel_cfg.differential ? buf.i16 : buf.u16; + + int32_t max_possible_value = adc_max_possible_value(channel); + + if (ret_val > max_possible_value) { + ret_val = -1; + } + + return ret_val; +} + +static int adc_channel_init(enum adc_channels channel) +{ + int err; + + if (!device_is_ready(available_adc_channels[channel].dev)) { + LOG_ERR("ADC controller device %s not ready", + available_adc_channels[channel].dev->name); + return -1; + } + err = adc_channel_setup_dt(&available_adc_channels[channel]); + if (err < 0) { + LOG_ERR("Could not setup channel #%d, (%d)", channel, err); + return -1; + } + + return 0; +} + +#if PRESSURE_0_AVAILABLE || PRESSURE_1_AVAILABLE +static int pressure_get(enum adc_channels channel, double *out_pressure) +{ + int32_t val_raw = adc_get_raw_value(channel); + + if (val_raw == -1) { + return -1; + } + + int32_t val_mv = val_raw; + + adc_raw_to_millivolts_dt(&available_adc_channels[channel], &val_mv); + + // sensor output pressure range: 0-30 psi + static const double SENSOR_PRESSURE_RANGE_PSI = 30.; + // sensor output voltage: 0.5-4.5 V with 5 V source + static const double SENSOR_VOLTAGE_MIN = 0.5; + static const double SENSOR_VOLTAGE_MAX = 4.5; + // 1 psi = 6.895 kPa + static const double SENSOR_PSI_TO_KPA = 6.895; + // 1 atm = 101.325 kPa + static const double SENSOR_ATM_IN_KPA = 101.325; + + *out_pressure = + ((double)val_mv / 1000. - SENSOR_VOLTAGE_MIN) * + (SENSOR_PRESSURE_RANGE_PSI / (SENSOR_VOLTAGE_MAX - SENSOR_VOLTAGE_MIN)) * + SENSOR_PSI_TO_KPA + + SENSOR_ATM_IN_KPA; // kPa + *out_pressure = *out_pressure * 1000.; // Pa + + return 0; +} +#endif // PRESSURE_0_AVAILABLE || PRESSURE_1_AVAILABLE + +#if ACIDITY_0_AVAILABLE || ACIDITY_1_AVAILABLE +static int acidity_get(enum adc_channels channel, double *out_acidity) +{ + int32_t val_raw = adc_get_raw_value(channel); + + if (val_raw == -1) { + return -1; + } + + int32_t val_mv = val_raw; + + adc_raw_to_millivolts_dt(&available_adc_channels[channel], &val_mv); + + // based on: https://wiki.dfrobot.com/PH_meter_SKU__SEN0161_ + *out_acidity = (double)val_mv / 1000.; + *out_acidity *= 3.5; + + return 0; +} +#endif // ACIDITY_0_AVAILABLE || ACIDITY_1_AVAILABLE + +#if PRESSURE_0_AVAILABLE +static int pressure_0_init(void) +{ + return adc_channel_init(ADC_CHANNEL_PRESSURE_0); +} + +static int pressure_0_get(double *out_pressure) +{ + return pressure_get(ADC_CHANNEL_PRESSURE_0, out_pressure); +} +#endif // PRESSURE_0_AVAILABLE + +#if ACIDITY_0_AVAILABLE +static int acidity_0_init(void) +{ + return adc_channel_init(ADC_CHANNEL_ACIDITY_0); +} + +static int acidity_0_get(double *out_acidity) +{ + return acidity_get(ADC_CHANNEL_ACIDITY_0, out_acidity); +} +#endif // ACIDITY_0_AVAILABLE + +#if PRESSURE_1_AVAILABLE +static int pressure_1_init(void) +{ + return adc_channel_init(ADC_CHANNEL_PRESSURE_1); +} + +static int pressure_1_get(double *out_pressure) +{ + return pressure_get(ADC_CHANNEL_PRESSURE_1, out_pressure); +} +#endif // PRESSURE_1_AVAILABLE + +#if ACIDITY_1_AVAILABLE +static int acidity_1_init(void) +{ + return adc_channel_init(ADC_CHANNEL_ACIDITY_1); +} + +static int acidity_1_get(double *out_acidity) +{ + return acidity_get(ADC_CHANNEL_ACIDITY_1, out_acidity); +} +#endif // ACIDITY_1_AVAILABLE + +#if TEMPERATURE_0_AVAILABLE && TEMPERATURE_1_AVAILABLE +static int lower_rom_index(void) +{ + return ds18b20_rom[0] < ds18b20_rom[1] ? 0 : 1; +} + +static int higher_rom_index(void) +{ + return ds18b20_rom[0] > ds18b20_rom[1] ? 0 : 1; +} + +void w1_search_callback(struct w1_rom rom, void *user_data) +{ + static int i; + + AVS_ASSERT( + i <= 1, + "Found two ds18b20 sensors in devicetree but at least three exist on 1Wire line"); + + ds18b20_rom[i++] = w1_rom_to_uint64(&rom); +} + +static int ds18b20_init(void *dummy) +{ + const struct device *const w1_dev = DEVICE_DT_GET(DT_NODELABEL(w1)); + + if (!device_is_ready(w1_dev)) { + LOG_ERR("W1 device not ready"); + return -1; + } + w1_search_rom(w1_dev, w1_search_callback, NULL); + + // w1_search_callback is not executed immediately, wait 1 second for its execution + k_sleep(K_SECONDS(1)); + + AVS_ASSERT( + ds18b20_rom[0] != 0 && ds18b20_rom[1] != 0, + "Found two ds18b20 sensors in devicetree but at least one haven't been found on 1Wire line"); + + return 0; +} +#endif // TEMPERATURE_0_AVAILABLE && TEMPERATURE_1_AVAILABLE + +#if TEMPERATURE_0_AVAILABLE +static int temperature_0_init(void) +{ + if (temperature_dev_0 == NULL) { + LOG_ERR("temperature_dev_0 not found"); + return -1; + } + if (!device_is_ready(temperature_dev_0)) { + LOG_ERR("temperature_dev_0 device not ready"); + } +#if TEMPERATURE_0_AVAILABLE && TEMPERATURE_1_AVAILABLE + if (avs_init_once(&ds18b20_init_handle, ds18b20_init, NULL)) { + return -1; + } + + struct sensor_value romval; + struct w1_rom w1rom; + + w1_uint64_to_rom(ds18b20_rom[lower_rom_index()], &w1rom); + w1_rom_to_sensor_value(&w1rom, &romval); + sensor_attr_set(temperature_dev_0, 0, SENSOR_ATTR_W1_ROM, &romval); +#endif // TEMPERATURE_0_AVAILABLE && TEMPERATURE_1_AVAILABLE + return 0; +} + +static int temperature_0_get(double *out_temperature) +{ + struct sensor_value temperature; + + sensor_sample_fetch(temperature_dev_0); + sensor_channel_get(temperature_dev_0, SENSOR_CHAN_AMBIENT_TEMP, &temperature); + *out_temperature = sensor_value_to_double(&temperature); + return 0; +} +#endif // TEMPERATURE_0_AVAILABLE + +#if TEMPERATURE_1_AVAILABLE +static int temperature_1_init(void) +{ + if (temperature_dev_1 == NULL) { + LOG_ERR("temperature_dev_1 not found"); + return -1; + } + if (!device_is_ready(temperature_dev_1)) { + LOG_ERR("temperature_dev_1 device not ready"); + } +#if TEMPERATURE_0_AVAILABLE && TEMPERATURE_1_AVAILABLE + if (avs_init_once(&ds18b20_init_handle, ds18b20_init, NULL)) { + return -1; + } + + struct sensor_value romval; + struct w1_rom w1rom; + + w1_uint64_to_rom(ds18b20_rom[higher_rom_index()], &w1rom); + w1_rom_to_sensor_value(&w1rom, &romval); + sensor_attr_set(temperature_dev_1, 0, SENSOR_ATTR_W1_ROM, &romval); +#endif // TEMPERATURE_0_AVAILABLE && TEMPERATURE_1_AVAILABLE + return 0; +} + +static int temperature_1_get(double *out_temperature) +{ + struct sensor_value temperature; + + sensor_sample_fetch(temperature_dev_1); + sensor_channel_get(temperature_dev_1, SENSOR_CHAN_AMBIENT_TEMP, &temperature); + *out_temperature = sensor_value_to_double(&temperature); + return 0; +} +#endif // TEMPERATURE_1_AVAILABLE + +struct basic_sensor_driver PRESSURE_DRIVER[] = { +#if PRESSURE_0_AVAILABLE + { .init = pressure_0_init, .read = pressure_0_get }, +#endif // PRESSURE_0_AVAILABLE +#if PRESSURE_1_AVAILABLE + { .init = pressure_1_init, .read = pressure_1_get }, +#endif // PRESSURE_1_AVAILABLE +}; +struct basic_sensor_driver ACIDITY_DRIVER[] = { +#if ACIDITY_0_AVAILABLE + { .init = acidity_0_init, .read = acidity_0_get }, +#endif // ACIDITY_0_AVAILABLE +#if ACIDITY_1_AVAILABLE + { .init = acidity_1_init, .read = acidity_1_get }, +#endif // ACIDITY_1_AVAILABLE +}; +struct basic_sensor_driver TEMPERATURE_DRIVER[] = { +#if TEMPERATURE_0_AVAILABLE + { .init = temperature_0_init, .read = temperature_0_get }, +#endif // TEMPERATURE_0_AVAILABLE +#if TEMPERATURE_1_AVAILABLE + { .init = temperature_1_init, .read = temperature_1_get }, +#endif // TEMPERATURE_1_AVAILABLE +}; + +static const struct sensor_context temperature_sensors_def = { .oid = 3303, + .instances_count = AVS_ARRAY_SIZE( + TEMPERATURE_DRIVER), + .drivers = TEMPERATURE_DRIVER, + .unit = "Cel" }; + +static const struct sensor_context pressure_sensors_def = { .oid = 3323, + .instances_count = + AVS_ARRAY_SIZE(PRESSURE_DRIVER), + .drivers = PRESSURE_DRIVER, + .unit = "Pa" }; + +static const struct sensor_context acidity_sensors_def = { .oid = 3326, + .instances_count = + AVS_ARRAY_SIZE(ACIDITY_DRIVER), + .drivers = ACIDITY_DRIVER, + .unit = "-" }; + +static struct sensor_context basic_sensors_def[] = { temperature_sensors_def, pressure_sensors_def, + acidity_sensors_def }; + +static int read_value(anjay_iid_t iid, void *_ctx, double *out_value) +{ + const struct basic_sensor_driver *driver = + &(((const struct sensor_context *)_ctx)->drivers[iid]); + + if (driver->read(out_value)) { + return -1; + } + + return 0; +} + +void basic_sensor_objects_install(anjay_t *anjay) +{ + for (int i = 0; i < AVS_ARRAY_SIZE(basic_sensors_def); i++) { + struct sensor_context *ctx = &basic_sensors_def[i]; + + if (anjay_ipso_basic_sensor_install(anjay, ctx->oid, ctx->instances_count)) { + continue; + } + + for (int j = 0; j < ctx->instances_count; j++) { + struct basic_sensor_driver *driver = &ctx->drivers[j]; + + if (driver->init()) { + driver->installed = false; + continue; + } + + driver->installed = !anjay_ipso_basic_sensor_instance_add( + anjay, ctx->oid, j, + (anjay_ipso_basic_sensor_impl_t){ .unit = ctx->unit, + .user_context = ctx, + .min_range_value = NAN, + .max_range_value = NAN, + .get_value = read_value }); + } + } +} + +void basic_sensor_objects_update(anjay_t *anjay) +{ + for (int i = 0; i < AVS_ARRAY_SIZE(basic_sensors_def); i++) { + struct sensor_context *ctx = &basic_sensors_def[i]; + + for (int j = 0; j < ctx->instances_count; j++) { + if (ctx->drivers[j].installed) { + anjay_ipso_basic_sensor_update(anjay, ctx->oid, j); + } + } + } +} diff --git a/bubblemaker/src/sensors.h b/bubblemaker/src/sensors.h new file mode 100644 index 0000000..555a82c --- /dev/null +++ b/bubblemaker/src/sensors.h @@ -0,0 +1,23 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 + +void basic_sensor_objects_install(anjay_t *anjay); +void basic_sensor_objects_update(anjay_t *anjay); diff --git a/bubblemaker/src/status_led.c b/bubblemaker/src/status_led.c new file mode 100644 index 0000000..061ff30 --- /dev/null +++ b/bubblemaker/src/status_led.c @@ -0,0 +1,77 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 "status_led.h" + +#if STATUS_LED_AVAILABLE + +#include +#include +#include + +LOG_MODULE_REGISTER(status_led); + +static const struct gpio_dt_spec status_led_spec = GPIO_DT_SPEC_GET(STATUS_LED_NODE, gpios); + +void status_led_init(void) +{ + if (!device_is_ready(status_led_spec.port) || + gpio_pin_configure_dt(&status_led_spec, GPIO_OUTPUT_INACTIVE)) { + LOG_WRN("failed to initialize status led"); + } +} + +static void status_led_set(int value) +{ + if (device_is_ready(status_led_spec.port)) { + gpio_pin_set_dt(&status_led_spec, value); + } +} + +void status_led_on(void) +{ + status_led_set(1); +} + +void status_led_off(void) +{ + status_led_set(0); +} + +void status_led_toggle(void) +{ + if (device_is_ready(status_led_spec.port)) { + gpio_pin_toggle_dt(&status_led_spec); + } +} +#else // STATUS_LED_AVAILABLE + +void status_led_init(void) +{ +} + +void status_led_on(void) +{ +} + +void status_led_off(void) +{ +} + +void status_led_toggle(void) +{ +} +#endif // STATUS_LED_AVAILABLE diff --git a/bubblemaker/src/status_led.h b/bubblemaker/src/status_led.h new file mode 100644 index 0000000..e9b3ebf --- /dev/null +++ b/bubblemaker/src/status_led.h @@ -0,0 +1,30 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 + +#define STATUS_LED_NODE DT_ALIAS(status_led) +#define STATUS_LED_AVAILABLE DT_NODE_HAS_STATUS(STATUS_LED_NODE, okay) + +void status_led_init(void); + +void status_led_on(void); + +void status_led_off(void); + +void status_led_toggle(void); diff --git a/bubblemaker/src/water_meter.c b/bubblemaker/src/water_meter.c new file mode 100644 index 0000000..90bdb6f --- /dev/null +++ b/bubblemaker/src/water_meter.c @@ -0,0 +1,467 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 "water_meter.h" +#include "bubblemaker.h" + +LOG_MODULE_REGISTER(water_meter); + +/** + * Cumulated water volume: R, Single, Mandatory + * type: float, range: N/A, unit: m3 + * Number of cubic meters of water distributed through the meter since + * last reset. + */ +#define RID_CUMULATED_WATER_VOLUME 1 + +/** + * Cumulated water meter value reset: E, Single, Optional + * type: N/A, range: N/A, unit: N/A + * Reset the cumulated meter value. + */ +#define RID_CUMULATED_WATER_METER_VALUE_RESET 2 + +/** + * Current flow: R, Single, Mandatory + * type: float, range: N/A, unit: m3 + * Current flow rate calculated in one second + */ +#define RID_CURRENT_FLOW 7 + +/** + * Maximum flow rate: R, Single, Optional + * type: float, range: N/A, unit: m3/s + * Maximum flow rate since last metering value. + */ +#define RID_MAXIMUM_FLOW_RATE 8 + +#define SYNCHRONIZED(Mtx) \ + for (int _synchronized_exit = k_mutex_lock(&(Mtx), K_FOREVER); !_synchronized_exit; \ + _synchronized_exit = -1, k_mutex_unlock(&(Mtx))) + +#define WM_TIMER_CYCLE K_SECONDS(1) + +static struct k_thread water_meter_thread; +static K_THREAD_STACK_DEFINE(water_meter_stack, 1024); +static K_MUTEX_DEFINE(water_meter_mutex); + +struct water_meter_instance { + anjay_iid_t iid; + double cumulated_volume; + double temp_volume; + double curr_flow; + double max_flow; +}; + +struct water_meter_object { + const anjay_dm_object_def_t *def; + + AVS_LIST(struct water_meter_instance) instances; +}; + +#if WATER_METER_0_AVAILABLE +struct water_meter_instance *wm_inst_0_ptr; +atomic_t water_meter_0_irq_count = ATOMIC_INIT(0); +#endif // WATER_METER_0_AVAILABLE +#if WATER_METER_1_AVAILABLE +struct water_meter_instance *wm_inst_1_ptr; +atomic_t water_meter_1_irq_count = ATOMIC_INIT(0); +#endif // WATER_METER_1_AVAILABLE + +void water_meter_instances_reset(void) +{ + SYNCHRONIZED(water_meter_mutex) + { +#if WATER_METER_0_AVAILABLE + wm_inst_0_ptr->cumulated_volume = 0; + wm_inst_0_ptr->max_flow = 0; + wm_inst_0_ptr->curr_flow = 0; +#endif // WATER_METER_0_AVAILABLE + +#if WATER_METER_1_AVAILABLE + wm_inst_1_ptr->cumulated_volume = 0; + wm_inst_1_ptr->max_flow = 0; + wm_inst_1_ptr->curr_flow = 0; +#endif // WATER_METER_1_AVAILABLE + } +} + +bool water_meter_is_null(void) +{ +#if WATER_METER_0_AVAILABLE + if (!wm_inst_0_ptr) { + return true; + } +#endif // WATER_METER_0_AVAILABLE +#if WATER_METER_1_AVAILABLE + if (!wm_inst_1_ptr) { + return true; + } +#endif // WATER_METER_1_AVAILABLE + + return false; +} + +#if WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE +void water_meter_get_cumulated_volumes(double *out_result) +{ + SYNCHRONIZED(water_meter_mutex) + { + out_result[0] = wm_inst_0_ptr->cumulated_volume; + out_result[1] = wm_inst_1_ptr->cumulated_volume; + } +} +#endif // WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + +static inline struct water_meter_object *get_obj(const anjay_dm_object_def_t *const *obj_ptr) +{ + assert(obj_ptr); + return AVS_CONTAINER_OF(obj_ptr, struct water_meter_object, def); +} + +static struct water_meter_instance *find_instance(const struct water_meter_object *obj, + anjay_iid_t iid) +{ + AVS_LIST(struct water_meter_instance) it; + AVS_LIST_FOREACH(it, obj->instances) + { + if (it->iid == iid) { + return it; + } else if (it->iid > iid) { + break; + } + } + + return NULL; +} + +static int list_instances(anjay_t *anjay, const anjay_dm_object_def_t *const *obj_ptr, + anjay_dm_list_ctx_t *ctx) +{ + (void)anjay; + + AVS_LIST(struct water_meter_instance) it; + AVS_LIST_FOREACH(it, get_obj(obj_ptr)->instances) + { + anjay_dm_emit(ctx, it->iid); + } + + return 0; +} + +static int init_instance(struct water_meter_instance *inst, anjay_iid_t iid) +{ + assert(iid != ANJAY_ID_INVALID); + + SYNCHRONIZED(water_meter_mutex) + { + inst->iid = iid; + inst->cumulated_volume = 0; + inst->temp_volume = 0; + inst->curr_flow = 0; + inst->max_flow = 0; + } + return 0; +} + +static void release_instance(struct water_meter_instance *inst) +{ + (void)inst; +} + +static struct water_meter_instance *add_instance(struct water_meter_object *obj, anjay_iid_t iid) +{ + assert(find_instance(obj, iid) == NULL); + + AVS_LIST(struct water_meter_instance) + created = AVS_LIST_NEW_ELEMENT(struct water_meter_instance); + if (!created) { + return NULL; + } + + int result; + + result = init_instance(created, iid); + if (result) { + AVS_LIST_CLEAR(&created); + return NULL; + } + + AVS_LIST(struct water_meter_instance) * ptr; + AVS_LIST_FOREACH_PTR(ptr, &obj->instances) + { + if ((*ptr)->iid > created->iid) { + break; + } + } + + AVS_LIST_INSERT(ptr, created); + return created; +} + +static int instance_reset(anjay_t *anjay, const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid) +{ + (void)anjay; + + struct water_meter_object *obj = get_obj(obj_ptr); + + assert(obj); + struct water_meter_instance *inst = find_instance(obj, iid); + + assert(inst); + SYNCHRONIZED(water_meter_mutex) + { + inst->cumulated_volume = 0; + inst->max_flow = 0; + inst->temp_volume = 0; + inst->curr_flow = 0; + } + return 0; +} + +static int list_resources(anjay_t *anjay, const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, anjay_dm_resource_list_ctx_t *ctx) +{ + (void)anjay; + (void)obj_ptr; + (void)iid; + + anjay_dm_emit_res(ctx, RID_CUMULATED_WATER_VOLUME, ANJAY_DM_RES_R, ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_CUMULATED_WATER_METER_VALUE_RESET, ANJAY_DM_RES_E, + ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_CURRENT_FLOW, ANJAY_DM_RES_R, ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_MAXIMUM_FLOW_RATE, ANJAY_DM_RES_R, ANJAY_DM_RES_PRESENT); + return 0; +} + +static int resource_read(anjay_t *anjay, const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, anjay_rid_t rid, anjay_riid_t riid, + anjay_output_ctx_t *ctx) +{ + (void)anjay; + + struct water_meter_object *obj = get_obj(obj_ptr); + + assert(obj); + struct water_meter_instance *inst = find_instance(obj, iid); + + assert(inst); + + switch (rid) { + case RID_CUMULATED_WATER_VOLUME: + assert(riid == ANJAY_ID_INVALID); + return anjay_ret_double(ctx, inst->cumulated_volume); + + case RID_CURRENT_FLOW: + assert(riid == ANJAY_ID_INVALID); + return anjay_ret_double(ctx, inst->curr_flow); + + case RID_MAXIMUM_FLOW_RATE: + assert(riid == ANJAY_ID_INVALID); + return anjay_ret_double(ctx, inst->max_flow); + + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static int resource_execute(anjay_t *anjay, const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, anjay_rid_t rid, anjay_execute_ctx_t *arg_ctx) +{ + (void)anjay; + (void)arg_ctx; + + struct water_meter_object *obj = get_obj(obj_ptr); + + assert(obj); + struct water_meter_instance *inst = find_instance(obj, iid); + + assert(inst); + + switch (rid) { + case RID_CUMULATED_WATER_METER_VALUE_RESET: + if (bm_state == BUBBLEMAKER_IDLE) { + water_meter_instances_reset(); + bm_state = BUBBLEMAKER_START_RED_LIGHT; + } + return 0; + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static const anjay_dm_object_def_t OBJ_DEF = { .oid = 3424, + .handlers = { .list_instances = list_instances, + .instance_reset = instance_reset, + + .list_resources = list_resources, + .resource_read = resource_read, + .resource_execute = + resource_execute } }; + +const anjay_dm_object_def_t **water_meter_object_create(void) +{ + struct water_meter_object *obj = + (struct water_meter_object *)avs_calloc(1, sizeof(struct water_meter_object)); + if (!obj) { + return NULL; + } + obj->def = &OBJ_DEF; + + static int instance_counter; + +#if WATER_METER_0_AVAILABLE + wm_inst_0_ptr = add_instance(obj, instance_counter++); + + if (!wm_inst_0_ptr) { + avs_free(obj); + return NULL; + } +#endif // WATER_METER_0_AVAILABLE + +#if WATER_METER_1_AVAILABLE + wm_inst_1_ptr = add_instance(obj, instance_counter++); + + if (!wm_inst_1_ptr) { + avs_free(obj); + return NULL; + } +#endif // WATER_METER_1_AVAILABLE + + return &obj->def; +} + +void water_meter_object_release(const anjay_dm_object_def_t **def) +{ + if (def) { + struct water_meter_object *obj = get_obj(def); + + AVS_LIST_CLEAR(&obj->instances) + { + release_instance(obj->instances); + } + avs_free(obj); + } +} + +#if WATER_METER_0_AVAILABLE +static void water_meter_0_callback_handler(const struct device *port, struct gpio_callback *cb, + gpio_port_pins_t pins) +{ + atomic_inc(&water_meter_0_irq_count); +} +#endif // WATER_METER_0_AVAILABLE + +#if WATER_METER_1_AVAILABLE +static void water_meter_1_callback_handler(const struct device *port, struct gpio_callback *cb, + gpio_port_pins_t pins) +{ + atomic_inc(&water_meter_1_irq_count); +} +#endif // WATER_METER_1_AVAILABLE + +static void water_meter_update_values(struct water_meter_instance *wm_instance, + atomic_val_t *multiplier) +{ + // based on: https://forum.seeedstudio.com/t/tutorial-reading-water-flow-rate-with-water-flow-sensor/647 + SYNCHRONIZED(water_meter_mutex) + { + wm_instance->curr_flow = (float)*multiplier * 60.0f / 7.5f; // L/h + wm_instance->curr_flow = wm_instance->curr_flow / 3600.0f / 1000.0f; // m^3/s + wm_instance->temp_volume = wm_instance->curr_flow; // readings every second + wm_instance->cumulated_volume += wm_instance->temp_volume; + if (wm_instance->max_flow < wm_instance->curr_flow) { + wm_instance->max_flow = wm_instance->curr_flow; + } + } +} + +static void water_meter_periodic(void *arg1, void *arg2, void *arg3) +{ + while (water_meter_is_null()) { + k_sleep(K_SECONDS(1)); + } + + while (1) { +#if WATER_METER_0_AVAILABLE + atomic_val_t water_meter_0_multiplier = atomic_get(&water_meter_0_irq_count); + + atomic_clear(&water_meter_0_irq_count); + water_meter_update_values(wm_inst_0_ptr, &water_meter_0_multiplier); +#endif // WATER_METER_0_AVAILABLE + +#if WATER_METER_1_AVAILABLE + atomic_val_t water_meter_1_multiplier = atomic_get(&water_meter_1_irq_count); + + atomic_clear(&water_meter_1_irq_count); + water_meter_update_values(wm_inst_1_ptr, &water_meter_1_multiplier); +#endif // WATER_METER_1_AVAILABLE + k_sleep(WM_TIMER_CYCLE); + } +} + +int water_meter_init(void) +{ +#if WATER_METER_0_AVAILABLE + static const struct gpio_dt_spec water_meter_0_spec = + GPIO_DT_SPEC_GET(WATER_METER_0_NODE, gpios); + + if (!device_is_ready(water_meter_0_spec.port)) { + LOG_ERR("Water meter 0 is not ready"); + return -1; + } + + static struct gpio_callback water_meter_0_callback; + + gpio_pin_configure_dt(&water_meter_0_spec, GPIO_INPUT); + gpio_init_callback(&water_meter_0_callback, water_meter_0_callback_handler, + BIT(water_meter_0_spec.pin)); + gpio_add_callback(water_meter_0_spec.port, &water_meter_0_callback); + gpio_pin_interrupt_configure_dt(&water_meter_0_spec, GPIO_INT_EDGE_RISING); +#endif // WATER_METER_0_AVAILABLE + +#if WATER_METER_1_AVAILABLE + static const struct gpio_dt_spec water_meter_1_spec = + GPIO_DT_SPEC_GET(WATER_METER_1_NODE, gpios); + + if (!device_is_ready(water_meter_1_spec.port)) { + LOG_ERR("Water meter 1 is not ready"); + return -1; + } + + static struct gpio_callback water_meter_1_callback; + + gpio_pin_configure_dt(&water_meter_1_spec, GPIO_INPUT); + gpio_init_callback(&water_meter_1_callback, water_meter_1_callback_handler, + BIT(water_meter_1_spec.pin)); + gpio_add_callback(water_meter_1_spec.port, &water_meter_1_callback); + gpio_pin_interrupt_configure_dt(&water_meter_1_spec, GPIO_INT_EDGE_RISING); +#endif // WATER_METER_1_AVAILABLE + + if (!k_thread_create(&water_meter_thread, water_meter_stack, + K_THREAD_STACK_SIZEOF(water_meter_stack), water_meter_periodic, NULL, + NULL, NULL, 1, 0, K_NO_WAIT)) { + LOG_ERR("Failed to create water_meter thread"); + return -1; + } + + return 0; +} diff --git a/bubblemaker/src/water_meter.h b/bubblemaker/src/water_meter.h new file mode 100644 index 0000000..5e05c73 --- /dev/null +++ b/bubblemaker/src/water_meter.h @@ -0,0 +1,39 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 + +#define WATER_METER_0_NODE DT_ALIAS(water_meter_0) +#define WATER_METER_1_NODE DT_ALIAS(water_meter_1) + +#define WATER_METER_0_AVAILABLE DT_NODE_HAS_STATUS(WATER_METER_0_NODE, okay) +#define WATER_METER_1_AVAILABLE DT_NODE_HAS_STATUS(WATER_METER_1_NODE, okay) + +#if !WATER_METER_0_AVAILABLE && !WATER_METER_1_AVAILABLE +#error "No water meter has been found in the devicetree" +#endif // !WATER_METER_0_AVAILABLE && !WATER_METER_1_AVAILABLE + +int water_meter_init(void); +void water_meter_instances_reset(void); +bool water_meter_is_null(void); +#if WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE +void water_meter_get_cumulated_volumes(double *out_result); +#endif // WATER_METER_0_AVAILABLE && WATER_METER_1_AVAILABLE + +const anjay_dm_object_def_t **water_meter_object_create(void); +void water_meter_object_release(const anjay_dm_object_def_t **def); diff --git a/bubblemaker/src/water_pump.c b/bubblemaker/src/water_pump.c new file mode 100644 index 0000000..257880b --- /dev/null +++ b/bubblemaker/src/water_pump.c @@ -0,0 +1,391 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 "water_pump.h" + +#if WATER_PUMP_0_AVAILABLE +#include +#include +#include + +LOG_MODULE_REGISTER(water_pump); + +/** + * Application Type: RW, Single, Optional + * type: string, range: N/A, unit: N/A + * The application type of the sensor or actuator as a string depending + * on the use case. + */ +#define RID_APPLICATION_TYPE 5750 + +/** + * Cumulative active power: R, Single, Optional + * type: float, range: N/A, unit: Wh + * The cumulative active power since the last cumulative energy reset or + * device start. + */ +#define RID_CUMULATIVE_ACTIVE_POWER 5805 + +/** + * Power factor: R, Single, Optional + * type: float, range: N/A, unit: N/A + * If applicable, the power factor of the current consumption. + */ +#define RID_POWER_FACTOR 5820 + +/** + * On/Off: RW, Single, Mandatory + * type: boolean, range: N/A, unit: N/A + * On/off control. Boolean value where True is On and False is Off. + */ +#define RID_ON_OFF 5850 + +/** + * Dimmer: RW, Single, Optional + * type: integer, range: 0..100, unit: /100 + * This resource represents a dimmer setting, which has an Integer value + * between 0 and 100 as a percentage. + */ +#define RID_DIMMER 5851 + +/** + * On time: RW, Single, Optional + * type: integer, range: N/A, unit: s + * The time in seconds that the device has been on. Writing a value of 0 + * resets the counter. + */ +#define RID_ON_TIME 5852 + +#define SYNCHRONIZED(Mtx) \ + for (int _synchronized_exit = k_mutex_lock(&(Mtx), K_FOREVER); !_synchronized_exit; \ + _synchronized_exit = -1, k_mutex_unlock(&(Mtx))) + +static K_MUTEX_DEFINE(water_pump_mutex); + +static struct k_work gpio_toggle_work; + +static const struct gpio_dt_spec water_pump_0_spec = GPIO_DT_SPEC_GET(WATER_PUMP_0_NODE, gpios); +static const struct gpio_dt_spec button_0_spec = GPIO_DT_SPEC_GET(BUTTON_0_NODE, gpios); + +static struct gpio_callback button_0_callback; + +struct power_control_instance { + anjay_iid_t iid; + char application_type[64]; + bool state; +}; + +struct power_control_object { + const anjay_dm_object_def_t *def; + + AVS_LIST(struct power_control_instance) instances; +}; + +static inline struct power_control_object *get_obj(const anjay_dm_object_def_t *const *obj_ptr) +{ + assert(obj_ptr); + return AVS_CONTAINER_OF(obj_ptr, struct power_control_object, def); +} + +static struct power_control_instance *find_instance(const struct power_control_object *obj, + anjay_iid_t iid) +{ + AVS_LIST(struct power_control_instance) it; + AVS_LIST_FOREACH(it, obj->instances) + { + if (it->iid == iid) { + return it; + } else if (it->iid > iid) { + break; + } + } + + return NULL; +} + +static int list_instances(anjay_t *anjay, const anjay_dm_object_def_t *const *obj_ptr, + anjay_dm_list_ctx_t *ctx) +{ + (void)anjay; + + AVS_LIST(struct power_control_instance) it; + AVS_LIST_FOREACH(it, get_obj(obj_ptr)->instances) + { + anjay_dm_emit(ctx, it->iid); + } + + return 0; +} + +static int init_instance(struct power_control_instance *inst, anjay_iid_t iid) +{ + assert(iid != ANJAY_ID_INVALID); + + inst->iid = iid; + strncpy(inst->application_type, "Water pump", sizeof(inst->application_type)); + SYNCHRONIZED(water_pump_mutex) + { + inst->state = gpio_pin_get_dt(&water_pump_0_spec); + } + + return 0; +} + +static void release_instance(struct power_control_instance *inst) +{ + (void)inst; +} + +static struct power_control_instance *add_instance(struct power_control_object *obj, + anjay_iid_t iid) +{ + assert(find_instance(obj, iid) == NULL); + + AVS_LIST(struct power_control_instance) + created = AVS_LIST_NEW_ELEMENT(struct power_control_instance); + if (!created) { + return NULL; + } + + int result = init_instance(created, iid); + + if (result) { + AVS_LIST_CLEAR(&created); + return NULL; + } + + AVS_LIST(struct power_control_instance) * ptr; + AVS_LIST_FOREACH_PTR(ptr, &obj->instances) + { + if ((*ptr)->iid > created->iid) { + break; + } + } + + AVS_LIST_INSERT(ptr, created); + return created; +} + +static int instance_create(anjay_t *anjay, const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid) +{ + (void)anjay; + struct power_control_object *obj = get_obj(obj_ptr); + + return add_instance(obj, iid) ? 0 : ANJAY_ERR_INTERNAL; +} + +static int instance_remove(anjay_t *anjay, const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid) +{ + (void)anjay; + struct power_control_object *obj = get_obj(obj_ptr); + + AVS_LIST(struct power_control_instance) * it; + AVS_LIST_FOREACH_PTR(it, &obj->instances) + { + if ((*it)->iid == iid) { + release_instance(*it); + AVS_LIST_DELETE(it); + return 0; + } else if ((*it)->iid > iid) { + break; + } + } + + assert(0); + return ANJAY_ERR_NOT_FOUND; +} + +static int instance_reset(anjay_t *anjay, const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid) +{ + (void)anjay; + + struct power_control_object *obj = get_obj(obj_ptr); + struct power_control_instance *inst = find_instance(obj, iid); + + assert(inst); + + return 0; +} + +static int list_resources(anjay_t *anjay, const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, anjay_dm_resource_list_ctx_t *ctx) +{ + (void)anjay; + (void)obj_ptr; + (void)iid; + + anjay_dm_emit_res(ctx, RID_APPLICATION_TYPE, ANJAY_DM_RES_RW, ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_CUMULATIVE_ACTIVE_POWER, ANJAY_DM_RES_R, ANJAY_DM_RES_ABSENT); + anjay_dm_emit_res(ctx, RID_POWER_FACTOR, ANJAY_DM_RES_R, ANJAY_DM_RES_ABSENT); + anjay_dm_emit_res(ctx, RID_ON_OFF, ANJAY_DM_RES_RW, ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_DIMMER, ANJAY_DM_RES_RW, ANJAY_DM_RES_ABSENT); + anjay_dm_emit_res(ctx, RID_ON_TIME, ANJAY_DM_RES_RW, ANJAY_DM_RES_ABSENT); + return 0; +} + +static int resource_read(anjay_t *anjay, const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, anjay_rid_t rid, anjay_riid_t riid, + anjay_output_ctx_t *ctx) +{ + (void)anjay; + + struct power_control_object *obj = get_obj(obj_ptr); + struct power_control_instance *inst = find_instance(obj, iid); + + assert(inst); + + switch (rid) { + case RID_APPLICATION_TYPE: + assert(riid == ANJAY_ID_INVALID); + return anjay_ret_string(ctx, inst->application_type); + + case RID_ON_OFF: + assert(riid == ANJAY_ID_INVALID); + SYNCHRONIZED(water_pump_mutex) + { + inst->state = gpio_pin_get_dt(&water_pump_0_spec); + } + return anjay_ret_bool(ctx, inst->state); + + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static int resource_write(anjay_t *anjay, const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, anjay_rid_t rid, anjay_riid_t riid, + anjay_input_ctx_t *ctx) +{ + (void)anjay; + + struct power_control_object *obj = get_obj(obj_ptr); + struct power_control_instance *inst = find_instance(obj, iid); + + assert(inst); + + switch (rid) { + case RID_APPLICATION_TYPE: { + assert(riid == ANJAY_ID_INVALID); + return anjay_get_string(ctx, inst->application_type, + sizeof(inst->application_type)); + } + + case RID_ON_OFF: { + assert(riid == ANJAY_ID_INVALID); + bool value; + int ret = anjay_get_bool(ctx, &value); + + SYNCHRONIZED(water_pump_mutex) + { + inst->state = value; + gpio_pin_set_dt(&water_pump_0_spec, value); + } + return ret; + } + + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static const anjay_dm_object_def_t OBJ_DEF = { + .oid = 3312, + .handlers = { .list_instances = list_instances, + .instance_create = instance_create, + .instance_remove = instance_remove, + .instance_reset = instance_reset, + + .list_resources = list_resources, + .resource_read = resource_read, + .resource_write = resource_write, + + .transaction_begin = anjay_dm_transaction_NOOP, + .transaction_validate = anjay_dm_transaction_NOOP, + .transaction_commit = anjay_dm_transaction_NOOP, + .transaction_rollback = anjay_dm_transaction_NOOP } +}; + +const anjay_dm_object_def_t **power_control_object_create(void) +{ + struct power_control_object *obj = + (struct power_control_object *)avs_calloc(1, sizeof(struct power_control_object)); + if (!obj) { + return NULL; + } + obj->def = &OBJ_DEF; + + if (!add_instance(obj, 0)) { + avs_free(obj); + return NULL; + } + + return &obj->def; +} + +void power_control_object_release(const anjay_dm_object_def_t **def) +{ + if (def) { + struct power_control_object *obj = get_obj(def); + + AVS_LIST_CLEAR(&obj->instances) + { + release_instance(obj->instances); + } + avs_free(obj); + } +} + +static void button_0_callback_handler(const struct device *port, struct gpio_callback *cb, + gpio_port_pins_t pins) +{ + k_work_submit(&gpio_toggle_work); +} + +void gpio_toggle_work_handler(struct k_work *work) +{ + SYNCHRONIZED(water_pump_mutex) + { + gpio_pin_toggle_dt(&water_pump_0_spec); + } +} + +int water_pump_initialize(void) +{ + if (!device_is_ready(water_pump_0_spec.port)) { + LOG_ERR("Water Pump 0 is not ready"); + return -1; + } + + gpio_pin_configure_dt(&water_pump_0_spec, GPIO_OUTPUT_ACTIVE | GPIO_INPUT); + + if (!device_is_ready(button_0_spec.port)) { + LOG_ERR("Button 0 is not ready"); + return -1; + } + + gpio_pin_configure_dt(&button_0_spec, GPIO_INPUT); + gpio_init_callback(&button_0_callback, button_0_callback_handler, BIT(button_0_spec.pin)); + gpio_add_callback(button_0_spec.port, &button_0_callback); + gpio_pin_interrupt_configure_dt(&button_0_spec, GPIO_INT_EDGE_RISING); + + k_work_init(&gpio_toggle_work, gpio_toggle_work_handler); + + return 0; +} +#endif // WATER_PUMP_0_AVAILABLE diff --git a/bubblemaker/src/water_pump.h b/bubblemaker/src/water_pump.h new file mode 100644 index 0000000..79e4199 --- /dev/null +++ b/bubblemaker/src/water_pump.h @@ -0,0 +1,35 @@ +/* + * Copyright 2020-2023 AVSystem + * + * 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 + +#define WATER_PUMP_0_NODE DT_ALIAS(water_pump_0) +#define WATER_PUMP_0_AVAILABLE DT_NODE_HAS_STATUS(WATER_PUMP_0_NODE, okay) + +#define BUTTON_0_NODE DT_ALIAS(push_button_0) +#define BUTTON_0_AVAILABLE DT_NODE_HAS_STATUS(BUTTON_0_NODE, okay) + +#if WATER_PUMP_0_AVAILABLE +int water_pump_initialize(void); + +const anjay_dm_object_def_t **power_control_object_create(void); +void power_control_object_release(const anjay_dm_object_def_t **def); + +#endif // WATER_PUMP_0_AVAILABLE diff --git a/bubblemaker/west-nrf.yml b/bubblemaker/west-nrf.yml new file mode 100644 index 0000000..ac2741a --- /dev/null +++ b/bubblemaker/west-nrf.yml @@ -0,0 +1,32 @@ +# Copyright 2020-2023 AVSystem +# +# 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. + +# Manifest for sdk-nrf based samples +manifest: + remotes: + - name: anjay + url-base: https://github.com/AVSystem + - name: ncs + url-base: https://github.com/nrfconnect + projects: + - name: sdk-nrf + path: nrf + remote: ncs + revision: v2.3.0 + import: true + - name: Anjay-zephyr + submodules: true + remote: anjay + revision: 5.4.0 + path: modules/lib/anjay diff --git a/demo/README.md b/demo/README.md index 4e794aa..789e289 100644 --- a/demo/README.md +++ b/demo/README.md @@ -4,7 +4,7 @@ Project containing all implemented features, intended to be a showcase. ## Supported hardware and overview This folder contains LwM2M Client application example, which targets -[B-L475E-IOT01A Discovery kit](https://www.st.com/en/evaluation-tools/b-l475e-iot01a.html), [nRF9160 Development kit](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF9160-DK), [Nordic Thingy:91 Prototyping kit](https://www.nordicsemi.com/Products/Development-hardware/Nordic-Thingy-91), [ESP32-DevKitC](https://www.espressif.com/en/products/devkits/esp32-devkitc), [nRF52840 Development kit](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dk), [nRF7002 Development kit](https://www.nordicsemi.com/Products/Development-hardware/nRF7002-DK) and [Arduino Nano 33 BLE Sense Lite](https://store.arduino.cc/products/arduino-nano-33-ble-sense). +[B-L475E-IOT01A Discovery kit](https://www.st.com/en/evaluation-tools/b-l475e-iot01a.html), [nRF9160 Development kit](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF9160-DK), [Nordic Thingy:91 Prototyping kit](https://www.nordicsemi.com/Products/Development-hardware/Nordic-Thingy-91), [ESP32-DevKitC](https://www.espressif.com/en/products/devkits/esp32-devkitc), [nRF52840 Development kit](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dk), [nRF7002 Development kit](https://www.nordicsemi.com/Products/Development-hardware/nRF7002-DK), [Arduino Nano 33 BLE Sense Lite](https://store.arduino.cc/products/arduino-nano-33-ble-sense) and [DevEdge](https://devedge.t-mobile.com/solutions/iotdevkit). There's an alternative configuration for nRF9160DK, revisions 0.14.0 and up, which utilizes external flash chip to perform firmware updates. @@ -18,6 +18,7 @@ The following LwM2M Objects are supported by default: | B-L475E-IOT01A | **Firmware Update (/5)** (experimental)
Temperature (/3303)
Humidity (/3304)
Accelerometer (/3313)
Magnetometer (/3314)
Barometer (/3315)
Distance (/3330)
Gyrometer (/3334)
Push button (/3347) | | nRF9160DK | Connectivity Monitoring (/4)
**Firmware Update (/5)**
Location (/6)
On/Off switch (/3342)
Push button (/3347)
ECID-Signal Measurement Information (/10256)
GNSS Assistance (/33625) (experimental)
Ground Fix Location (/33626) (experimental, to enable in Kconfig)
Advanced Firmware Update (/33629) (experimental, to enable in Kconfig) | | Thingy:91 | Connectivity Monitoring (/4)
**Firmware Update (/5)**
Location (/6)
Temperature (/3303)
Humidity (/3304)
Accelerometer (/3313)
Barometer (/3315)
Buzzer (/3338)
Push button (/3347)
LED color light (/3420)
ECID-Signal Measurement Information (/10256)
GNSS Assistance (/33625) (experimental)
Ground Fix Location (/33626) (experimental, to enable in Kconfig)
Advanced Firmware Update (/33629) (experimental, to enable in Kconfig) | +| DevEdge | **Firmware Update (/5)**
Location (/6)
Illuminance (/3301)
Temperature (/3303)
Accelerometer (/3313)
Barometer (/3315)
Push button (/3347) | | nRF52840DK | Push button (/3347) | | nRF7002DK | Light Control (/3311)
Push button (/3347) | | Arduino Nano 33 BLE Sense Lite | Temperature (/3303)
Barometer (/3315) | @@ -46,6 +47,16 @@ west update ``` Now you can compile the project using `west build -b nrf9160dk_nrf9160_ns`, `west build -b thingy91_nrf9160_ns`, `west build -b nrf7002dk_nrf5340_cpuapp`, `west build -b nrf52840dk_nrf52840` or `west build -b arduino_nano_33_ble_sense` in `demo` directory, respectively. The last two commands compiles project for use with the OpenThread network, more about this can be found in the section [Connecting to the LwM2M Server with OpenThread](#connecting-to-the-lwm2m-server-with-openthread). +### Compilation guide for T-Mobile DevEdge + +Because T-Mobile DevEdge board uses fork of Zephyr, it is necessary to change our Zephyr workspace, it is handled by using different manifest file. +Set West manifest path to `Anjay-zephyr-client/demo`, and manifest file to `west-t-mobile.yml` and do `west update`. +``` +west config manifest.path Anjay-zephyr-client/demo +west config manifest.file west-t-mobile.yml +west update +``` +Now you can compile the project using `west build -b tmo_dev_edge` in `demo` directory. > **__NOTE:__** > To switch back to mainstream zephyr version, change manifest file to `west.yml` and do `west update`. @@ -55,6 +66,52 @@ Now you can compile the project using `west build -b nrf9160dk_nrf9160_ns`, `wes > ``` > [More about managing west workspace through manifests can be found here.](https://docs.zephyrproject.org/latest/guides/west/manifest.html) +> **__NOTE:__** +> For proper support of the cellular connection, you may need to set the right value of CONFIG_MODEM_MURATA_1SC_APN in `tmo_dev_edge.conf` file beforehand. + + +> **__NOTE:__** +> On the DevEdge board, you may need to perform a full chip erase for the firmware update (which is implemented using the external flash) to work properly. This will require flashing the UART bootloader again. You can download it from the Silicon Labs site: go to http://www.silabs.com/32bit-appnotes, search for "AN0003: UART Bootloader" and download the "Example Code" resource, and flash the `an/an0003_efm32_uart_bootloader/binaries/bl-usart-geckoS1C2-v2.07.hex` file using JLink Programmer. + +#### GPS/GNSS location support on T-Mobile DevEdge + +Some T-Mobile DevEdge board units may be delivered without any firmware installed on the CXD5605AGF chip that is used as GPS/GNSS receiver. + +If you have one of those units, you need to flash the firmware manually using the applications from the T-Mobile DevEdge SDK. + +1. Navigate to the `samples/cxd5605_test` application within the T-Mobile DevEdge SDK. + + * Build and flash it using `west build -b tmo_dev_edge && west flash` + * Observe the serial port output. + + * If it regularly shows NMEA sequences such as `$GPGGA,000001.00,,,,,0,00,,,,,,,*49`, *the firmware is OK and the Location object should work in the Anjay client.* + * Otherwise, if the output stops at `Reading NMEA sentences`, the firmware is not present in the chip. If you wish the Location object to work, please follow the rest of the steps. + +2. Navigate to the `samples/cxd5605_load_fw_to_flash` application within the T-Mobile DevEdge SDK. + + * Build and flash it using `west build -b tmo_dev_edge && west flash` + * *NOTE: This will overwrite any non-volatile configuration of the Anjay client.* + * Observe the serial port output. Wait until the application prints `Verification of flash done`. This should take about 30 seconds since boot. + * The CXD5605AGF firmware is now loaded onto the external flash. You can proceed with the next step to flash it onto the actual chip. + +3. Navigate to the `samples/cxd5605_update_fw` application within the T-Mobile DevEdge SDK. + + * Build and flash it using `west build -b tmo_dev_edge && west flash` + * The firmware consists of 4 files. Wait until the application flashes all of them. + * Observe the serial port output. When the whole procedure is finished, the application will print the firmware version number (e.g. `Ver: 20047,2f945cd,136E`). This should take about 3-4 minutes since boot. + * The CXD5605AGF firmware is now flashed. + +4. Repeat step 1 to verify that the receiver is working properly. + +5. You can now proceed with building and flashing the Anjay client. The Location object shall now work properly, provided that the GPS/GNSS signal is strong enough. Note that this may be impossible when the board is used inside a building. + +#### Switching the preferred network bearer + +On the T-Mobile DevEdge board, both WiFi and cellular connectivity is supported and can be switched at runtime. + +To select the currently active connection, please type `anjay config set preferred_bearer wifi` or `anjay config set preferred_bearer cellular`, respectively, on the serial console. The change will take effect immediately - although it is recommended to disable the LwM2M client first (`anjay stop`). Changing the network bearer while connected may result in spurious error messages printed on the console. + +The preferred bearer can be saved onto persistent storage (among other settings) by typing `anjay config save`. ### Compiling for external flash usage @@ -195,11 +252,10 @@ west build -b nrf9160dk_nrf9160_ns@0.14.0 -p -- -DCONF_FILE=prj_extflash.conf -D After that, certificate and private key based on SECP256R1 curve can be provided through shell interface in PEM format. To generate them use following commands (to use certificate and private key with Coiote DM you must specify a common name that is the same as the client endpoint name): ``` openssl ecparam -name secp256r1 -out ecparam.der -openssl req -new -x509 -nodes -newkey ec:ecparam.der -keyout demo-cert.key -out demo-cert.crt -days 3650 -openssl x509 -in demo-cert.crt -outform pem -out cert.pem +openssl req -new -x509 -nodes -newkey ec:ecparam.der -keyout demo-cert.key -out cert.pem -days 3650 openssl ec -in demo-cert.key -outform pem -out key.pem ``` -Then provide the generated certificate and private key through the shell with the following commands respectively: +Then provide the generated certificate (cert.pem) and private key (key.pem) through the shell with the following commands respectively: ``` anjay config set public_cert anjay config set private_key diff --git a/demo/boards/disco_l475_iot1.conf b/demo/boards/disco_l475_iot1.conf index c12b4c6..30b46e3 100644 --- a/demo/boards/disco_l475_iot1.conf +++ b/demo/boards/disco_l475_iot1.conf @@ -45,4 +45,4 @@ CONFIG_BOOTLOADER_MCUBOOT=y # Note: if relative paths are used here, it is treated as # relative to $WEST_TOPDIR (typically ~/zephyrproject) CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="bootloader/mcuboot/root-rsa-2048.pem" -CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS="--version 23.6.0" +CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS="--version 23.9.0" diff --git a/demo/boards/nrf9160dk_nrf9160_ns.conf b/demo/boards/nrf9160dk_nrf9160_ns.conf index 308d704..64017f1 100644 --- a/demo/boards/nrf9160dk_nrf9160_ns.conf +++ b/demo/boards/nrf9160dk_nrf9160_ns.conf @@ -1,7 +1,7 @@ # anjay-zephyr-client CONFIG_ANJAY_ZEPHYR_DEVICE_MANUFACTURER="Nordic Semiconductor" CONFIG_ANJAY_ZEPHYR_MODEL_NUMBER="nRF9160DK" -CONFIG_ANJAY_ZEPHYR_GPS_NRF=y +CONFIG_ANJAY_ZEPHYR_GPS=y CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS=y CONFIG_ANJAY_ZEPHYR_FOTA=y @@ -57,7 +57,7 @@ CONFIG_HEAP_MEM_POOL_SIZE=2048 CONFIG_BOOTLOADER_MCUBOOT=y CONFIG_IMG_MANAGER=y CONFIG_IMG_ERASE_PROGRESSIVELY=y -CONFIG_MCUBOOT_IMAGE_VERSION="23.6.0" +CONFIG_MCUBOOT_IMAGE_VERSION="23.9.0" # Shell settings CONFIG_SHELL_MINIMAL=y diff --git a/demo/boards/nrf9160dk_nrf9160_ns_extflash.conf b/demo/boards/nrf9160dk_nrf9160_ns_extflash.conf index 3be5452..d78a4e1 100644 --- a/demo/boards/nrf9160dk_nrf9160_ns_extflash.conf +++ b/demo/boards/nrf9160dk_nrf9160_ns_extflash.conf @@ -1,7 +1,7 @@ # anjay-zephyr-client CONFIG_ANJAY_ZEPHYR_DEVICE_MANUFACTURER="Nordic Semiconductor" CONFIG_ANJAY_ZEPHYR_MODEL_NUMBER="nRF9160DK" -CONFIG_ANJAY_ZEPHYR_GPS_NRF=y +CONFIG_ANJAY_ZEPHYR_GPS=y CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS=y CONFIG_ANJAY_ZEPHYR_FOTA=y @@ -45,7 +45,7 @@ CONFIG_HEAP_MEM_POOL_SIZE=2048 CONFIG_BOOTLOADER_MCUBOOT=y CONFIG_IMG_MANAGER=y CONFIG_IMG_ERASE_PROGRESSIVELY=y -CONFIG_MCUBOOT_IMAGE_VERSION="23.6.0" +CONFIG_MCUBOOT_IMAGE_VERSION="23.9.0" # External flash CONFIG_SPI=y diff --git a/demo/boards/thingy91_nrf9160_ns.conf b/demo/boards/thingy91_nrf9160_ns.conf index e806efb..90c74cc 100644 --- a/demo/boards/thingy91_nrf9160_ns.conf +++ b/demo/boards/thingy91_nrf9160_ns.conf @@ -1,7 +1,7 @@ # anjay-zephyr-client CONFIG_ANJAY_ZEPHYR_DEVICE_MANUFACTURER="Nordic Semiconductor" CONFIG_ANJAY_ZEPHYR_MODEL_NUMBER="Thingy:91" -CONFIG_ANJAY_ZEPHYR_GPS_NRF=y +CONFIG_ANJAY_ZEPHYR_GPS=y CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS=y CONFIG_ANJAY_ZEPHYR_FOTA=y @@ -46,6 +46,7 @@ CONFIG_NRF_CLOUD_AGPS=y CONFIG_DATE_TIME=y CONFIG_DATE_TIME_UPDATE_INTERVAL_SECONDS=600 CONFIG_DATE_TIME_TOO_OLD_SECONDS=600 +CONFIG_DATE_TIME_MODEM=n CONFIG_DATE_TIME_NTP_QUERY_TIME_SECONDS=5 # File system @@ -68,7 +69,7 @@ CONFIG_ADXL362_ACCEL_RANGE_2G=y CONFIG_BOOTLOADER_MCUBOOT=y CONFIG_IMG_MANAGER=y CONFIG_IMG_ERASE_PROGRESSIVELY=y -CONFIG_MCUBOOT_IMAGE_VERSION="23.6.0" +CONFIG_MCUBOOT_IMAGE_VERSION="23.9.0" # Shell settings CONFIG_SHELL_MINIMAL=y diff --git a/demo/boards/tmo_dev_edge.conf b/demo/boards/tmo_dev_edge.conf new file mode 100644 index 0000000..4ad8cb2 --- /dev/null +++ b/demo/boards/tmo_dev_edge.conf @@ -0,0 +1,101 @@ +# anjay-zephyr-client +CONFIG_ANJAY_ZEPHYR_DEVICE_MANUFACTURER="T-Mobile" +CONFIG_ANJAY_ZEPHYR_MODEL_NUMBER="DevEdge" +CONFIG_ANJAY_ZEPHYR_FOTA=y +CONFIG_ANJAY_ZEPHYR_GPS=y + +# Anjay Settings +CONFIG_ANJAY_COMPAT_MBEDTLS=y +CONFIG_ANJAY_COMPAT_NET=y +CONFIG_ANJAY_COMPAT_TIME=y + +# Allow runtime certificate configuration +CONFIG_ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=y + +# Monitoring +CONFIG_THREAD_ANALYZER=y +CONFIG_THREAD_ANALYZER_USE_LOG=y +CONFIG_THREAD_NAME=y + +# MbedTLS and security +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +CONFIG_MBEDTLS_DTLS=y +CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED=y +CONFIG_MBEDTLS_CIPHER_CCM_ENABLED=y +CONFIG_MBEDTLS_ENTROPY_ENABLED=y + +# Hardware Configuration +CONFIG_SOC_GECKO_EMU_DCDC=y +CONFIG_SOC_GECKO_EMU_DCDC_MODE_OFF=y +CONFIG_CMU_HFCLK_HFXO=y + +# General settings +CONFIG_MAIN_STACK_SIZE=16384 + +# Hardware class support +CONFIG_I2C=y +CONFIG_LED=y +CONFIG_LED_PWM=y +CONFIG_PWM=y +CONFIG_SENSOR=y +CONFIG_SPI=y +CONFIG_SPI_NOR=y +CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_STREAM_FLASH_ERASE=y +CONFIG_WIFI=y + +# Device Drivers +CONFIG_PWM_GECKO=y +CONFIG_LIS2DW12=y +CONFIG_LPS22HH=y +CONFIG_TMP108=y +CONFIG_TSL2540=y +CONFIG_COUNTER=y +CONFIG_WIFI_RS9116W=y + +CONFIG_MODEM=y +CONFIG_MODEM_SHELL=y +CONFIG_MODEM_MURATA_1SC=y +CONFIG_MODEM_MURATA_1SC_APN="iot.t-mobile.com" + +# OS Support library +CONFIG_REBOOT=y +CONFIG_POSIX_CLOCK=y + +# Logging +CONFIG_LOG_MODE_DEFERRED=y +CONFIG_LOG_BLOCK_IN_THREAD=y + +CONFIG_CONSOLE_SUBSYS=y +CONFIG_CONSOLE_GETLINE=y + +# Networking +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_NET_SOCKETS_POSIX_NAMES=y +CONFIG_NET_TCP=y + +# Offload TCP/IP stack to a co-processor +CONFIG_RS9116W_TLS_OFFLOAD=y +CONFIG_NET_NATIVE=n +CONFIG_NET_OFFLOAD=y +CONFIG_NET_SOCKETS_OFFLOAD=y + +# Clock synchronization +CONFIG_SNTP=y + +# FOTA support +CONFIG_STREAM_FLASH=y + +# Boot options +CONFIG_BOOTLOADER_MCUBOOT=y +# Note: if relative paths are used here, it is treated as +# relative to $WEST_TOPDIR (typically ~/zephyrproject) +CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="bootloader/mcuboot/root-rsa-2048.pem" +CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS="--version 23.9.0" diff --git a/demo/boards/tmo_dev_edge.overlay b/demo/boards/tmo_dev_edge.overlay new file mode 100644 index 0000000..a9ebbcf --- /dev/null +++ b/demo/boards/tmo_dev_edge.overlay @@ -0,0 +1,17 @@ +/ { + aliases { + temperature = &as6212; + accelerometer = &lis2dw12; + barometer = &lps22hh; + push-button-0 = &button0; + status-led = &led1; + illuminance = &tsl2540; + }; + chosen { + zephyr,code-partition = &slot0_partition; + zephyr,console = &usart0; + zephyr,shell-uart = &usart0; + }; +}; + +#include "tmo_dev_edge_partitions.overlay" diff --git a/demo/boards/tmo_dev_edge_partitions.overlay b/demo/boards/tmo_dev_edge_partitions.overlay new file mode 100644 index 0000000..e856e19 --- /dev/null +++ b/demo/boards/tmo_dev_edge_partitions.overlay @@ -0,0 +1,30 @@ +&flash0 { + partitions { + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 DT_SIZE_K(48)>; + }; + slot0_partition: partition@C000 { + label = "image-0"; + reg = <0x0000C000 DT_SIZE_K(936)>; + }; + scratch_partition: partition@F6000 { + label = "image-scratch"; + reg = <0x000F6000 DT_SIZE_K(32)>; + }; + }; +}; + +&w25q64 { + partitions { + // This overrides the default configuration which configures it as 8MiB + storage_partition1: partition@0 { + label = "storage"; + reg = <0x00000000 DT_SIZE_K(7256)>; + }; + slot1_partition: partition@716000 { + label = "image-1"; + reg = <0x00716000 DT_SIZE_K(936)>; + }; + }; +}; diff --git a/demo/child_image/mcuboot/boards/tmo_dev_edge.conf b/demo/child_image/mcuboot/boards/tmo_dev_edge.conf new file mode 100644 index 0000000..a4c8840 --- /dev/null +++ b/demo/child_image/mcuboot/boards/tmo_dev_edge.conf @@ -0,0 +1,6 @@ +CONFIG_SPI=y +CONFIG_SPI_NOR=y +CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_STREAM_FLASH_ERASE=y + +CONFIG_BOOT_MAX_IMG_SECTORS=512 diff --git a/demo/child_image/mcuboot/boards/tmo_dev_edge.overlay b/demo/child_image/mcuboot/boards/tmo_dev_edge.overlay new file mode 100644 index 0000000..44c50c9 --- /dev/null +++ b/demo/child_image/mcuboot/boards/tmo_dev_edge.overlay @@ -0,0 +1,7 @@ +/ { + chosen { + zephyr,code-partition = &boot_partition; + }; +}; + +#include "../../../boards/tmo_dev_edge_partitions.overlay" diff --git a/demo/runtime_cert.conf b/demo/runtime_cert.conf new file mode 100644 index 0000000..7d11804 --- /dev/null +++ b/demo/runtime_cert.conf @@ -0,0 +1,4 @@ +CONFIG_ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y diff --git a/demo/src/status_led.c b/demo/src/status_led.c index 20b71e8..061ff30 100644 --- a/demo/src/status_led.c +++ b/demo/src/status_led.c @@ -16,32 +16,28 @@ #include "status_led.h" +#if STATUS_LED_AVAILABLE + #include #include #include LOG_MODULE_REGISTER(status_led); -#if STATUS_LED_AVAILABLE -#define STATUS_LED_GPIO_PORT DT_GPIO_CTLR(STATUS_LED_NODE, gpios) -#define STATUS_LED_GPIO_PIN DT_GPIO_PIN(STATUS_LED_NODE, gpios) -#define STATUS_LED_GPIO_FLAGS (GPIO_OUTPUT_INACTIVE | DT_GPIO_FLAGS(STATUS_LED_NODE, gpios)) - -static const struct device *status_led_device = DEVICE_DT_GET(STATUS_LED_GPIO_PORT); +static const struct gpio_dt_spec status_led_spec = GPIO_DT_SPEC_GET(STATUS_LED_NODE, gpios); void status_led_init(void) { - if (!device_is_ready(status_led_device) || - gpio_pin_configure(status_led_device, STATUS_LED_GPIO_PIN, STATUS_LED_GPIO_FLAGS)) { - status_led_device = NULL; + if (!device_is_ready(status_led_spec.port) || + gpio_pin_configure_dt(&status_led_spec, GPIO_OUTPUT_INACTIVE)) { LOG_WRN("failed to initialize status led"); } } static void status_led_set(int value) { - if (status_led_device) { - gpio_pin_set(status_led_device, STATUS_LED_GPIO_PIN, value); + if (device_is_ready(status_led_spec.port)) { + gpio_pin_set_dt(&status_led_spec, value); } } @@ -57,11 +53,12 @@ void status_led_off(void) void status_led_toggle(void) { - if (status_led_device) { - gpio_pin_toggle(status_led_device, STATUS_LED_GPIO_PIN); + if (device_is_ready(status_led_spec.port)) { + gpio_pin_toggle_dt(&status_led_spec); } } #else // STATUS_LED_AVAILABLE + void status_led_init(void) { } diff --git a/demo/west-nrf.yml b/demo/west-nrf.yml index 6a52543..ac2741a 100644 --- a/demo/west-nrf.yml +++ b/demo/west-nrf.yml @@ -28,5 +28,5 @@ manifest: - name: Anjay-zephyr submodules: true remote: anjay - revision: 3.4.1 + revision: 5.4.0 path: modules/lib/anjay diff --git a/demo/west-t-mobile.yml b/demo/west-t-mobile.yml new file mode 100644 index 0000000..103e2ef --- /dev/null +++ b/demo/west-t-mobile.yml @@ -0,0 +1,36 @@ +# Copyright 2020-2023 AVSystem +# +# 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. + +# Manifest for zephyr based samples +manifest: + remotes: + - name: anjay + url-base: https://github.com/AVSystem + - name: t-mobile + url-base: https://github.com/tmobile + projects: + - name: DevEdge-IoTDevKit-ZephyrRTOS + path: zephyr + remote: t-mobile + revision: tmz-1.19.2-qa + import: true + - name: Anjay-zephyr + submodules: true + remote: anjay + revision: 5.4.0 + path: modules/lib/anjay + - name: DevEdge-IoTDevKit-ZephyrSDK + path: modules/lib/t-mobile-sdk + remote: t-mobile + revision: tmz-sdk-1.19.2-qa diff --git a/demo/west.yml b/demo/west.yml index d133476..648c45a 100644 --- a/demo/west.yml +++ b/demo/west.yml @@ -28,7 +28,7 @@ manifest: - name: Anjay-zephyr submodules: true remote: anjay - revision: 3.4.1 + revision: 5.4.0 path: modules/lib/anjay - name: hal_espressif path: modules/hal/espressif diff --git a/ei_demo/boards/thingy91_nrf9160_ns.conf b/ei_demo/boards/thingy91_nrf9160_ns.conf index 1caf189..12f9876 100644 --- a/ei_demo/boards/thingy91_nrf9160_ns.conf +++ b/ei_demo/boards/thingy91_nrf9160_ns.conf @@ -27,6 +27,7 @@ CONFIG_LTE_NETWORK_TIMEOUT=600 CONFIG_DATE_TIME=y CONFIG_DATE_TIME_UPDATE_INTERVAL_SECONDS=600 CONFIG_DATE_TIME_TOO_OLD_SECONDS=600 +CONFIG_DATE_TIME_MODEM=n CONFIG_DATE_TIME_NTP_QUERY_TIME_SECONDS=5 # File system diff --git a/ei_demo/west-nrf.yml b/ei_demo/west-nrf.yml index 6a52543..ac2741a 100644 --- a/ei_demo/west-nrf.yml +++ b/ei_demo/west-nrf.yml @@ -28,5 +28,5 @@ manifest: - name: Anjay-zephyr submodules: true remote: anjay - revision: 3.4.1 + revision: 5.4.0 path: modules/lib/anjay diff --git a/minimal/README.md b/minimal/README.md index 5045f47..d34eba5 100644 --- a/minimal/README.md +++ b/minimal/README.md @@ -13,6 +13,7 @@ This folder contains LwM2M Client minimal application example for following targ - [nrf52840dk_nrf52840](https://docs.zephyrproject.org/latest/boards/arm/nrf52840dk_nrf52840/doc/index.html) - [nRF7002 Development kit](https://www.nordicsemi.com/Products/Development-hardware/nRF7002-DK) - [Arduino Nano 33 BLE Sense Lite](https://store.arduino.cc/products/arduino-nano-33-ble-sense) + - [DevEdge](https://devedge.t-mobile.com/solutions/iotdevkit) The following LwM2M Objects are supported: - Security (/0) @@ -41,6 +42,20 @@ west update ``` Now you can compile the project using `west build -b nrf9160dk_nrf9160_ns`, `west build -b thingy91_nrf9160_ns`, `west build -b nrf7002dk_nrf5340_cpuapp`, `west build -b nrf52840dk_nrf52840` or `west build -b arduino_nano_33_ble_sense` in `minimal` directory, respectively. The last two commands compiles project for use with the OpenThread network, more about this can be found in the section [Connecting to the LwM2M Server with OpenThread](#connecting-to-the-lwm2m-server-with-openthread). +### Compilation guide for T-Mobile DevEdge + +Because T-Mobile DevEdge board uses fork of Zephyr, it is necessary to change our Zephyr workspace, it is handled by using different manifest file. +Set West manifest path to `Anjay-zephyr-client/minimal`, and manifest file to `west-t-mobile.yml` and do `west update`. +``` +west config manifest.path Anjay-zephyr-client/minimal +west config manifest.file west-t-mobile.yml +west update +``` +Now you can compile the project using `west build -b tmo_dev_edge` in `minimal` directory. + +> **__NOTE:__** +> For proper support of the cellular connection, you may need to set the right value of CONFIG_MODEM_MURATA_1SC_APN in `tmo_dev_edge.conf` file beforehand. + > **__NOTE:__** > To switch back to mainstream zephyr version, change manifest file to `west.yml` and do `west update`. diff --git a/minimal/boards/tmo_dev_edge.conf b/minimal/boards/tmo_dev_edge.conf new file mode 100644 index 0000000..3a1fa12 --- /dev/null +++ b/minimal/boards/tmo_dev_edge.conf @@ -0,0 +1,70 @@ +# anjay-zephyr-client +CONFIG_ANJAY_ZEPHYR_DEVICE_MANUFACTURER="T-Mobile" +CONFIG_ANJAY_ZEPHYR_MODEL_NUMBER="DevEdge" + +# Anjay Settings +CONFIG_ANJAY_COMPAT_MBEDTLS=y +CONFIG_ANJAY_COMPAT_NET=y +CONFIG_ANJAY_COMPAT_TIME=y + +# MbedTLS and security +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_DTLS=y +CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED=y +CONFIG_MBEDTLS_CIPHER_CCM_ENABLED=y +CONFIG_MBEDTLS_ENTROPY_ENABLED=y + +# Hardware Configuration +CONFIG_SOC_GECKO_EMU_DCDC=y +CONFIG_SOC_GECKO_EMU_DCDC_MODE_OFF=y +CONFIG_CMU_HFCLK_HFXO=y + +# General settings +CONFIG_MAIN_STACK_SIZE=16384 + +# Hardware class support +CONFIG_I2C=y +CONFIG_LED=y +CONFIG_LED_PWM=y +CONFIG_PWM=y +CONFIG_SENSOR=y +CONFIG_SPI=y +CONFIG_SPI_NOR=y +CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_STREAM_FLASH_ERASE=y +CONFIG_WIFI=y + +# Device Drivers +CONFIG_PWM_GECKO=y +CONFIG_LIS2DW12=y +CONFIG_LPS22HH=y +CONFIG_TMP108=y +CONFIG_TSL2540=y +CONFIG_COUNTER=y +CONFIG_WIFI_RS9116W=y + +CONFIG_MODEM=y +CONFIG_MODEM_SHELL=y +CONFIG_MODEM_MURATA_1SC=y +CONFIG_MODEM_MURATA_1SC_APN="iot.t-mobile.com" + +# OS Support library +CONFIG_REBOOT=y +CONFIG_POSIX_CLOCK=y + +# Logging +CONFIG_LOG_MODE_DEFERRED=y +CONFIG_LOG_BLOCK_IN_THREAD=y + +# Networking +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_NET_SOCKETS_POSIX_NAMES=y + +# Offload TCP/IP stack to a co-processor +CONFIG_RS9116W_TLS_OFFLOAD=y +CONFIG_NET_NATIVE=n +CONFIG_NET_OFFLOAD=y +CONFIG_NET_SOCKETS_OFFLOAD=y + +# Clock synchronization +CONFIG_SNTP=y diff --git a/minimal/west-nrf.yml b/minimal/west-nrf.yml index 6a52543..ac2741a 100644 --- a/minimal/west-nrf.yml +++ b/minimal/west-nrf.yml @@ -28,5 +28,5 @@ manifest: - name: Anjay-zephyr submodules: true remote: anjay - revision: 3.4.1 + revision: 5.4.0 path: modules/lib/anjay diff --git a/minimal/west-t-mobile.yml b/minimal/west-t-mobile.yml new file mode 100644 index 0000000..103e2ef --- /dev/null +++ b/minimal/west-t-mobile.yml @@ -0,0 +1,36 @@ +# Copyright 2020-2023 AVSystem +# +# 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. + +# Manifest for zephyr based samples +manifest: + remotes: + - name: anjay + url-base: https://github.com/AVSystem + - name: t-mobile + url-base: https://github.com/tmobile + projects: + - name: DevEdge-IoTDevKit-ZephyrRTOS + path: zephyr + remote: t-mobile + revision: tmz-1.19.2-qa + import: true + - name: Anjay-zephyr + submodules: true + remote: anjay + revision: 5.4.0 + path: modules/lib/anjay + - name: DevEdge-IoTDevKit-ZephyrSDK + path: modules/lib/t-mobile-sdk + remote: t-mobile + revision: tmz-sdk-1.19.2-qa diff --git a/minimal/west.yml b/minimal/west.yml index d133476..648c45a 100644 --- a/minimal/west.yml +++ b/minimal/west.yml @@ -28,7 +28,7 @@ manifest: - name: Anjay-zephyr submodules: true remote: anjay - revision: 3.4.1 + revision: 5.4.0 path: modules/lib/anjay - name: hal_espressif path: modules/hal/espressif