diff --git a/.gitignore b/.gitignore
index 5acb669..399cf84 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
build
.vscode
+__pycache__
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 109147a..101b3bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,23 @@
# Changelog
-## yy.mm (Mon dd{st/nd/rd/th}, yyyy)
+## 22.12 (Dec 13th, 2022)
+
+### Features
+- Added provisioning script for `demo` examples
+- Added nRF52840dk_nRF52840 with OpenThread support
+- Added automatic handling of connection link state
+- Added option for use of non-secure WiFi networks
+- Added configuration for software-based security on nRF9160DK
+
+### Improvements
+- Updated Zephyr to 3.2.0
+- Updated sdk-nrf to 2.1.1
+- Refactored network connection handling
+- Firmware update success status can be now persisted across reboots until actual delivery
+
+### Bugfixes
+- Fixed critical memory corruption bug in the factory provisioning app
+- Fixed problem with push buttons IID
## 22.08.1 (Aug 31st, 2022)
diff --git a/README.md b/README.md
index 2cb36ab..991c50d 100644
--- a/README.md
+++ b/README.md
@@ -138,7 +138,7 @@ LwM2M Server, please register at https://eu.iot.avsystem.cloud/. Then have
a look at the example configuration to configure security credentials and other
necessary settings (like Wi-Fi SSID etc.).
-[Guide showing basic usage of Coiote DM](https://iotdevzone.avsystem.com/docs/Coiote_DM_Device_Onboarding/Quick_start/)
+[Guide showing basic usage of Coiote DM](https://iotdevzone.avsystem.com/docs/IoT_quick_start/Device_onboarding/)
is available on IoT Developer Zone.
> **__NOTE:__**
diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt
index c34ebf6..9819579 100644
--- a/demo/CMakeLists.txt
+++ b/demo/CMakeLists.txt
@@ -68,9 +68,15 @@ elseif(CONFIG_ANJAY_CLIENT_BUILD_MCUBOOT_AUTOMATICALLY)
# 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}"
- "${ZEPHYR_BASE}/scripts/mergehex.py"
+ "${MERGEHEX_SCRIPT}"
-o "${MERGED_HEX_NAME}"
"${CMAKE_CURRENT_BINARY_DIR}/mcuboot/zephyr/zephyr.hex"
"${CMAKE_CURRENT_BINARY_DIR}/zephyr/zephyr.signed.hex"
@@ -110,6 +116,9 @@ else()
src/status_led.h
src/utils.c
src/utils.h
+ src/network/network.c
+ src/network/network.h
+ src/network/network_internal.h
src/objects/basic_sensors.c
src/objects/buzzer.c
src/objects/device.c
@@ -147,12 +156,31 @@ else()
list(APPEND app_sources
src/firmware_update.c)
endif()
+
+ if(CONFIG_LTE_LINK_CONTROL)
+ list(APPEND app_sources
+ src/network/network_nrf91.c)
+ elseif(CONFIG_NET_L2_OPENTHREAD)
+ list(APPEND app_sources
+ src/network/network_openthread.c)
+ elseif(CONFIG_WIFI_ESP32)
+ list(APPEND app_sources
+ src/network/network_esp32.c)
+ elseif(CONFIG_WIFI_ESWIFI)
+ list(APPEND app_sources
+ src/network/network_eswifi.c)
+ elseif(CONFIG_WIFI)
+ list(APPEND app_sources
+ src/network/network_wifi.c)
+ else()
+ message(FATAL_ERROR "Neither CONFIG_LTE_LINK_CONTROL nor CONFIG_WIFI is enabled")
+ endif()
endif()
target_sources(app PRIVATE
${app_sources})
-if(CONF_FILE_BUILD_TYPE STREQUAL "extflash" AND CONFIG_BOOTLOADER_MCUBOOT)
+if(CONF_FILE_BUILD_TYPE MATCHES ".*extflash.*" AND CONFIG_BOOTLOADER_MCUBOOT)
# This is a workaround for a bug in nCS' integration with TF-M.
#
# When MCUboot and TF-M are both in use, the CONFIG_BOOTLOADER_MCUBOOT
diff --git a/demo/Kconfig b/demo/Kconfig
index 3d69a20..1b5f665 100644
--- a/demo/Kconfig
+++ b/demo/Kconfig
@@ -87,6 +87,12 @@ config ANJAY_CLIENT_NRF_LC_INFO_CELL_POLL_RATE
range 1 2147483647
depends on ANJAY_CLIENT_NRF_LC_INFO
+config ANJAY_CLIENT_NETWORK_KEEPALIVE_RATE
+ int "Rate of checking whether the network connection is still alive [seconds]"
+ default 60
+ range 1 2147483647
+ depends on WIFI_ESWIFI
+
config ANJAY_CLIENT_FOTA
bool "Enable the Firmware Update object"
depends on BOOTLOADER_MCUBOOT
diff --git a/demo/README.md b/demo/README.md
index 046a464..340c358 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) and [ESP32-DevKitC](https://www.espressif.com/en/products/devkits/esp32-devkitc).
+[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) and [nRF52840 Development kit](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dk).
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:
| 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, configurable in Kconfig)
On/Off switch (/3342)
Push button (/3347)
ECID-Signal Measurement Information (/10256)
Location Assistance (/50001, experimental) |
| Thingy:91 | Connectivity Monitoring (/4)
**Firmware Update (/5)**
Location (/6, configurable in Kconfig)
Temperature (/3303)
Humidity (/3304)
Accelerometer (/3313)
Barometer (/3315)
Buzzer (/3338)
Push button (/3347)
LED color light (/3420)
ECID-Signal Measurement Information (/10256)
Location Assistance (/50001, experimental) |
+| nRF52840DK | Push button (/3347) |
## Compilation
@@ -30,7 +31,7 @@ west update
You can now compile the project for B-L475E-IOT01A using `west build -b disco_l475_iot1` in `demo` directory.
-### Compilation guide for nRF9160DK and Thingy:91
+### Compilation guide for nRF9160DK, Thingy:91 and nRF52840DK
Because NCS uses different Zephyr version, 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-nrf.yml` and do `west update`.
@@ -39,7 +40,7 @@ west config manifest.path Anjay-zephyr-client/demo
west config manifest.file west-nrf.yml
west update
```
-Now you can compile the project using `west build -b nrf9160dk_nrf9160_ns` or `west build -b thingy91_nrf9160_ns` in `demo` directory, respectively.
+Now you can compile the project using `west build -b nrf9160dk_nrf9160_ns`, `west build -b thingy91_nrf9160_ns` or `west build -b nrf52840dk_nrf52840` in `demo` directory, respectively. The last command compiles project for use with the OpenThread network, more about this can be found in the section `Connecting to the LwM2M Server with OpenThread`.
> **__NOTE:__**
@@ -57,6 +58,14 @@ For nRF9160DK hardware revisions 0.14.0 and up, an alternate configuration that
To compile in this configuration, use `west build -b nrf9160dk_nrf9160_ns@0.14.0 -- -DCONF_FILE=prj_extflash.conf`.
+### Compiling with software-based cryptography
+
+On Nordic boards, security is provided using the (D)TLS sockets implemented in modem firmware and provided by nrfxlib.
+
+However, on nRF9160DK revisions 0.14.0 and up, it is possible to switch to software-based implementation based on Mbed TLS instead. This is not recommended due to lowered security and performance, but may be desirable if you require some specific (D)TLS features (e.g. ciphersuites) that are not supported by the modem.
+
+To compile in this configuration, use `west build -b nrf9160dk_nrf9160_ns@0.14.0 -- -DCONF_FILE=prj_extflash.conf -DOVERLAY_CONFIG=overlay_nrf_mbedtls.conf`.
+
## Flashing the target
After successful build you can flash the target using `west flash`.
@@ -98,12 +107,22 @@ LwM2M Server, please register at https://eu.iot.avsystem.cloud/. Then have
a look at the Configuration menu to configure security credentials and other
necessary settings (like Wi-Fi SSID etc.).
-[Guide showing basic usage of Coiote DM](https://iotdevzone.avsystem.com/docs/Coiote_DM_Device_Onboarding/Quick_start/)
+[Guide showing basic usage of Coiote DM](https://iotdevzone.avsystem.com/docs/IoT_quick_start/Device_onboarding/)
is available on IoT Developer Zone.
NOTE: You may use any LwM2M Server compliant with LwM2M 1.0 TS. The server URI
can be changed in the Configuration menu.
+## Connecting to the LwM2M Server with OpenThread
+
+To use this project on the nRF52840dk board, in addition to the configuration shown in the previous paragraph, you will need to configure the OpenThread Border Router and Commissioner as described in the guides from the links below.
+You can change default `CONFIG_OPENTHREAD_JOINER_PSKD` value in the `boards/nrf52840dk_nrf52840.conf`. In same file, replace `CONFIG_OPENTHREAD_FTD=y` with `CONFIG_OPENTHREAD_MTD=y` if you want your device to run as an MTD.
+
+Resources:
+- [Introduction to OpenThread](https://openthread.io/guides)
+- [Border Router guide](https://openthread.io/guides/border-router)
+- [Commissioner guide](https://openthread.io/guides/commissioner)
+
## Configuration menu
Using serial port terminal, you can manage Anjay client using built-in Zephyr shell. Use `anjay` command to list possible options.
@@ -142,5 +161,73 @@ to which the user is able to pre-provision credentials to the device using a spe
tailored version of the application. This feature allows to easily pre-provision large
quantities of devices in a semi-automatic manner.
-To use this feature, generate a special file containing the credentials using our [Factory Provisioning Tool](https://avsystem.github.io/Anjay-doc/Tools/FactoryProvisioning.html).
-Then, follow the flow described in `src/factory_provisioning/factory_flash.c` file to finish the process.
+To use this feature, one can use a script `tools/provisioning-tools/ptool.py`.
+It might be used in the similar manner as the script of the same name described in the documentation:
+[Factory Provisioning Tool](https://avsystem.github.io/Anjay-doc/Tools/FactoryProvisioning.html).
+There are a few new and important command-line arguments:
+
+* `--board` (`-b`) - the board for which the images should be built,
+* `--image_dir` (`-i`) - directory for the cached Zephyr hex images,
+* `--serial` (`-s`) - serial number of the device to be used,
+* `--baudrate` (`-B`) - baudrate for the used serial port, when it is not provided the default value is 115200.
+
+If the image `initial.hex` exists in the given `image_dir` the initial provisioning image won't be built and the same works for
+final image and `final.hex`. When `image_dir` path is provided, but some images are missing, they will be built in the given directory.
+If `image_dir` is not provided then the images will be built in `$(pwd)/provisioning_builds`.
+
+Before using the script make sure that in the shell in which you run it the `west build` command would work and
+that all of the configs passed to the script are valid - in particular, make sure that you changed `` in `lwm2m_server.json`
+config file to your actual domain in EU cloud Coiote installation (or fill the whole file with some different valid server configuration).
+
+Currently the script is designed only for Nordic boards, and it was tested with nRF 9160DK.
+
+Example script invocation from the `demo` for provisioning some nRF 9160DK board directory may look like:
+
+```bash
+../tools/provisioning-tool/ptool.py -b nrf9160dk_nrf9160_ns -s -c ../tools/provisioning-tool/configs/endpoint_cfg -t -S ../tools/provisioning-tool/configs/lwm2m_server.json
+```
+
+where `` should be replaced by our board's serial number and `` should be replaced by some valid authentication token for the Coiote server provided in the `lwm2m_server.json` file.
+
+### Using Certificate Mode with factory provisioning
+
+If supported by the underlying (D)TLS backend (if using Mbed TLS, make sure that
+it is configured appropriately), the application can authenticate with the
+server using certificate mode.
+
+You will need to download the server certificate first. One possible way to do
+it is with `openssl`:
+
+```bash
+openssl s_client -showcerts -connect eu.iot.avsystem.cloud:5684 | openssl x509 -outform der -out eu-cloud-cert.der
+```
+
+> **__NOTE:__**
+> Only servers that use self-signed certificates are reliably supported by
+> default. You can change this behavior by setting the Certificate Usage
+> resource in the endpoint configuration file. However, this might not be
+> supported by all (D)TLS backends.
+>
+> In particular, when `CONFIG_ANJAY_COMPAT_ZEPHYR_TLS` is enabled (which is the
+> default for Nordic boards), the Certificate Usage are only approximated by
+> adding the server certificate to traditional PKIX trust store if Certificate
+> Usage is set to 2 or 3 (note that 3 is the default) and ignoring it otherwise.
+
+You should then modify the `cert_info.json` file that's located in
+`tools/provisioning-tool/configs` for the desired self-signed certificate
+configuration.
+
+Once you have the server certificate, you can now provision the board. Example
+script invocation may look like:
+
+```bash
+../tools/provisioning-tool/ptool.py -b nrf9160dk_nrf9160_ns -s -c ../tools/provisioning-tool/configs/endpoint_cfg_cert -p eu-cloud-cert.der -C ../tools/provisioning-tool/configs/cert_info.json
+```
+
+> **__NOTE:__**
+> Coiote DM currently does not support registering devices together with
+> uploading dynamically generated self-signed certificates using command-line
+> tools.
+>
+> You will need to manually add the new device on Coiote DM via GUI and upload
+> the certificate during the "Add device credentials" step.
diff --git a/demo/boards/disco_l475_iot1.conf b/demo/boards/disco_l475_iot1.conf
index c48e202..489f888 100644
--- a/demo/boards/disco_l475_iot1.conf
+++ b/demo/boards/disco_l475_iot1.conf
@@ -45,3 +45,6 @@ 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"
+# TODO: consider a deploy script, which will fill in the release version into config variable below
+# Warning: MCUBoot's imgtool doesn't allow leading zeros in version string components, that may be a bug
+CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS="--version 22.8.1"
diff --git a/demo/boards/esp32.overlay b/demo/boards/esp32.overlay
new file mode 100644
index 0000000..c4fa06f
--- /dev/null
+++ b/demo/boards/esp32.overlay
@@ -0,0 +1,3 @@
+&wifi {
+ status = "okay";
+};
diff --git a/demo/boards/nrf52840dk_nrf52840.conf b/demo/boards/nrf52840dk_nrf52840.conf
new file mode 100644
index 0000000..d96fe42
--- /dev/null
+++ b/demo/boards/nrf52840dk_nrf52840.conf
@@ -0,0 +1,66 @@
+# anjay-zephyr-client
+CONFIG_ANJAY_CLIENT_DEVICE_MANUFACTURER="Nordic Semiconductor"
+CONFIG_ANJAY_CLIENT_MODEL_NUMBER="nRF52840DK"
+
+# Anjay Settings
+CONFIG_ANJAY_COMPAT_TIME=y
+CONFIG_ANJAY_COMPAT_MBEDTLS=y
+CONFIG_ANJAY_COMPAT_NET=y
+
+# General Settings
+CONFIG_MAIN_STACK_SIZE=2048
+CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
+
+# Logging
+CONFIG_LOG_BLOCK_IN_THREAD=y
+CONFIG_LOG_MODE_DEFERRED=y
+
+# Clock synchronization
+CONFIG_DATE_TIME=y
+CONFIG_DATE_TIME_AUTO_UPDATE=n
+
+# Networking
+CONFIG_NET_IPV4=n
+CONFIG_NET_IPV6=y
+CONFIG_NET_IPV6_NBR_CACHE=n
+CONFIG_NET_IPV6_MLD=n
+CONFIG_NET_CONFIG_NEED_IPV4=n
+CONFIG_NET_MGMT_EVENT_INFO=y
+CONFIG_NET_L2_OPENTHREAD=y
+
+# DNS
+CONFIG_DNS_RESOLVER=y
+CONFIG_DNS_SERVER_IP_ADDRESSES=y
+CONFIG_DNS_SERVER1="fdaa:bb:1::2"
+
+# OpenThread
+CONFIG_OPENTHREAD_JOINER=y
+CONFIG_OPENTHREAD_JOINER_AUTOSTART=y
+CONFIG_OPENTHREAD_MANUAL_START=y
+CONFIG_OPENTHREAD_SLAAC=y
+CONFIG_OPENTHREAD_JOINER_PSKD="J01NME"
+CONFIG_OPENTHREAD_FTD=y
+
+# MbedTLS and security
+CONFIG_MBEDTLS=y
+CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED=y
+CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=y
+CONFIG_MBEDTLS_DTLS=y
+CONFIG_MBEDTLS_ENTROPY_ENABLED=y
+
+# 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
+CONFIG_OPENTHREAD_SHELL=y
diff --git a/demo/boards/nrf52840dk_nrf52840.overlay b/demo/boards/nrf52840dk_nrf52840.overlay
new file mode 100644
index 0000000..62024b7
--- /dev/null
+++ b/demo/boards/nrf52840dk_nrf52840.overlay
@@ -0,0 +1,9 @@
+/ {
+ aliases {
+ push-button-0 = &button0;
+ push-button-1 = &button1;
+ push-button-2 = &button2;
+ push-button-3 = &button3;
+ status-led = &led0;
+ };
+};
diff --git a/demo/boards/nrf9160dk_nrf9160_ns.conf b/demo/boards/nrf9160dk_nrf9160_ns.conf
index 7762c4e..2934c3c 100644
--- a/demo/boards/nrf9160dk_nrf9160_ns.conf
+++ b/demo/boards/nrf9160dk_nrf9160_ns.conf
@@ -22,10 +22,10 @@ CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_LOG_RUNTIME_FILTERING=n
CONFIG_LOG_CMDS=n
CONFIG_LOG_MAX_LEVEL=3
-CONFIG_LOG_DEFAULT_LEVEL=2
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
@@ -75,3 +75,4 @@ CONFIG_DEVICE_SHELL=n
CONFIG_DATE_SHELL=n
CONFIG_DEVMEM_SHELL=n
CONFIG_MCUBOOT_SHELL=n
+CONFIG_KERNEL_SHELL=y
diff --git a/demo/boards/nrf9160dk_nrf9160_ns_extflash.conf b/demo/boards/nrf9160dk_nrf9160_ns_extflash.conf
index 9f23a5f..e3e971e 100644
--- a/demo/boards/nrf9160dk_nrf9160_ns_extflash.conf
+++ b/demo/boards/nrf9160dk_nrf9160_ns_extflash.conf
@@ -16,6 +16,7 @@ CONFIG_MAIN_STACK_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
# Networking
+CONFIG_NET_NATIVE=n
CONFIG_NET_IF_MAX_IPV4_COUNT=2
CONFIG_NET_TCP_ISN_RFC6528=n
diff --git a/demo/boards/thingy91_nrf9160_ns.conf b/demo/boards/thingy91_nrf9160_ns.conf
index 921d176..5ed558c 100644
--- a/demo/boards/thingy91_nrf9160_ns.conf
+++ b/demo/boards/thingy91_nrf9160_ns.conf
@@ -25,10 +25,10 @@ CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_LOG_RUNTIME_FILTERING=n
CONFIG_LOG_CMDS=n
CONFIG_LOG_MAX_LEVEL=3
-CONFIG_LOG_DEFAULT_LEVEL=2
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
@@ -87,3 +87,4 @@ CONFIG_DEVICE_SHELL=n
CONFIG_DATE_SHELL=n
CONFIG_DEVMEM_SHELL=n
CONFIG_MCUBOOT_SHELL=n
+CONFIG_KERNEL_SHELL=y
diff --git a/demo/overlay_nrf_mbedtls.conf b/demo/overlay_nrf_mbedtls.conf
new file mode 100644
index 0000000..391e47e
--- /dev/null
+++ b/demo/overlay_nrf_mbedtls.conf
@@ -0,0 +1,15 @@
+CONFIG_ANJAY_COMPAT_ZEPHYR_TLS=n
+CONFIG_ANJAY_COMPAT_MBEDTLS=y
+
+CONFIG_NORDIC_SECURITY_BACKEND=n
+CONFIG_OBERON_BACKEND=n
+
+CONFIG_MBEDTLS_TLS_LIBRARY=y
+CONFIG_MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG=n
+CONFIG_MBEDTLS_ENTROPY_C=y
+CONFIG_MBEDTLS_CIPHER_MODE_CBC=y
+CONFIG_MBEDTLS_DHM_C=y
+CONFIG_MBEDTLS_CTR_DRBG_C=y
+CONFIG_MBEDTLS_ECP_C=y
+CONFIG_MBEDTLS_LEGACY_CRYPTO_C=y
+CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y
diff --git a/demo/src/anjay_shell.c b/demo/src/anjay_shell.c
index db81c00..43ae19f 100644
--- a/demo/src/anjay_shell.c
+++ b/demo/src/anjay_shell.c
@@ -19,6 +19,8 @@
#include "utils.h"
#include "persistence.h"
+#include "network/network.h"
+
static int cmd_anjay_start(const struct shell *shell, size_t argc, char **argv)
{
ARG_UNUSED(argc);
@@ -60,7 +62,7 @@ static int cmd_anjay_stop(const struct shell *shell, size_t argc, char **argv)
// change the flag first to interrupt the thread if event loop is not
// running yet
atomic_store(&anjay_running, false);
- interrupt_net_connect_wait_loop();
+ network_interrupt_connect_wait_loop();
SYNCHRONIZED(global_anjay_mutex)
{
@@ -105,6 +107,7 @@ static int cmd_anjay_config_set(const struct shell *shell, size_t argc, char **a
return config_set_option(shell, argc, argv);
}
+
static int cmd_anjay_config_default(const struct shell *shell, size_t argc, char **argv)
{
ARG_UNUSED(argc);
@@ -234,7 +237,8 @@ SHELL_STATIC_SUBCMD_SET_CREATE(
#endif // CONFIG_ANJAY_CLIENT_FACTORY_PROVISIONING
#ifdef CONFIG_WIFI
SHELL_CMD(OPTION_KEY_SSID, NULL, "Wi-Fi SSID", cmd_anjay_config_set),
- SHELL_CMD(OPTION_KEY_PASSWORD, NULL, "Wi-Fi password", cmd_anjay_config_set),
+ SHELL_CMD(OPTION_KEY_PASSWORD, NULL, "Wi-Fi password (empty for no-sec)",
+ cmd_anjay_config_set),
#endif // CONFIG_WIFI
#ifndef CONFIG_ANJAY_CLIENT_FACTORY_PROVISIONING
SHELL_CMD(OPTION_KEY_URI, NULL, "Server URI", cmd_anjay_config_set),
diff --git a/demo/src/common.h b/demo/src/common.h
index d01f6d7..a91b702 100644
--- a/demo/src/common.h
+++ b/demo/src/common.h
@@ -16,14 +16,16 @@
#pragma once
-#include
-#include
-#include
#include
#include
+#include
+#include
+#include
+
#include
+#include "network/network.h"
#include "objects/objects.h"
extern volatile atomic_bool device_initialized;
@@ -39,6 +41,8 @@ extern volatile bool anjay_thread_running;
extern struct k_mutex anjay_thread_running_mutex;
extern struct k_condvar anjay_thread_running_condvar;
+void sched_update_anjay_network_bearer(void);
+
#ifdef CONFIG_ANJAY_CLIENT_LOCATION_SERVICES_MANUAL_CELL_BASED
struct cell_request_job_args {
anjay_t *anjay;
@@ -49,5 +53,3 @@ avs_sched_clb_t cell_request_job;
#ifdef CONFIG_ANJAY_CLIENT_GPS_NRF_A_GPS
avs_sched_clb_t agps_request_job;
#endif // CONFIG_ANJAY_CLIENT_GPS_NRF_A_GPS
-
-void interrupt_net_connect_wait_loop(void);
diff --git a/demo/src/config.c b/demo/src/config.c
index 66d4209..f25059b 100644
--- a/demo/src/config.c
+++ b/demo/src/config.c
@@ -19,16 +19,16 @@
#include
#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
+#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
+#include
+#include
#include "config.h"
#include "default_config.h"
@@ -142,12 +142,15 @@ static int settings_set(const char *key, size_t len, settings_read_cb read_cb, v
if (key) {
for (int i = 0; i < AVS_ARRAY_SIZE(string_options); ++i) {
if (strcmp(key, string_options[i].key) == 0) {
- if (len != string_options[i].value_capacity) {
+ if (len > string_options[i].value_capacity) {
return -EINVAL;
}
- int result = read_cb(cb_arg, string_options[i].value,
- string_options[i].value_capacity);
+ memset(string_options[i].value, 0,
+ string_options[i].value_capacity);
+
+ int result = read_cb(cb_arg, string_options[i].value, len);
+
return result >= 0 ? 0 : result;
}
}
@@ -167,6 +170,10 @@ void config_print_summary(const struct shell *shell)
void config_save(const struct shell *shell)
{
+ // This is a POSIX extension provided by newlib, but only visible with
+ // #define _POSIX_C_SOURCE 200809L. That conflicts with some declarations in Zephyr, though.
+ size_t strnlen(const char *s, size_t maxlen);
+
char key_buf[64];
int result = 0;
@@ -174,8 +181,13 @@ void config_save(const struct shell *shell)
result = avs_simple_snprintf(key_buf, sizeof(key_buf), SETTINGS_ROOT_NAME "/%s",
string_options[i].key);
if (result >= 0) {
- result = settings_save_one(key_buf, string_options[i].value,
- string_options[i].value_capacity);
+ size_t length =
+ strnlen(string_options[i].value, string_options[i].value_capacity);
+ // Allow for storing empty strings '\0'
+ if (length == 0) {
+ length = 1;
+ }
+ result = settings_save_one(key_buf, string_options[i].value, length);
}
}
@@ -242,32 +254,34 @@ void config_init(const struct shell *shell)
int config_set_option(const struct shell *shell, size_t argc, char **argv)
{
- if (argc != 2) {
- shell_error(shell, "Wrong number of arguments.\n");
- return -1;
- }
-
const char *key = argv[0];
+ const char *value = "";
+ struct anjay_client_option *option = NULL;
+ if (argc == 2) {
+ value = argv[1];
+ } else if (argc > 2 || argc == 0) {
+ shell_warn(shell, "Wrong argument number");
+ return -1;
+ }
for (size_t i = 0; i < AVS_ARRAY_SIZE(string_options); ++i) {
if (strcmp(key, string_options[i].key) == 0) {
- const char *value = argv[1];
- size_t value_len = strlen(value) + 1;
-
- assert(string_options[i].validator);
- if (string_options[i].validator(shell, value, value_len,
- &string_options[i])) {
- return -1;
- }
+ option = &string_options[i];
+ break;
+ }
+ }
+ assert(option);
- memcpy(string_options[i].value, value, value_len);
+ size_t value_len = strlen(value) + 1;
- return 0;
- }
+ assert(option->validator);
+ if (option->validator(shell, value, value_len, option)) {
+ return -1;
}
- AVS_UNREACHABLE("Invalid option key");
- return -1;
+ memcpy(option->value, value, value_len);
+
+ return 0;
}
#endif // WITH_ANJAY_CLIENT_CONFIG
@@ -278,6 +292,7 @@ static int parse_uint32(const char *value, uint32_t *out)
}
#endif // defined(CONFIG_ANJAY_CLIENT_GPS_NRF) || !defined(CONFIG_ANJAY_CLIENT_FACTORY_PROVISIONING)
+
#ifndef CONFIG_ANJAY_CLIENT_FACTORY_PROVISIONING
const char *config_get_endpoint_name(void)
{
@@ -295,8 +310,26 @@ const char *config_get_wifi_password(void)
{
return app_config.password;
}
+
+struct wifi_connect_req_params config_get_wifi_params(void)
+{
+ struct wifi_connect_req_params result = { 0 };
+
+ result.ssid = (uint8_t *)config_get_wifi_ssid();
+ result.ssid_length = strlen((const char *)result.ssid);
+ result.psk = (uint8_t *)config_get_wifi_password();
+ result.psk_length = strlen((const char *)result.psk);
+ if (result.psk_length) {
+ result.security = WIFI_SECURITY_TYPE_PSK;
+ } else {
+ result.security = WIFI_SECURITY_TYPE_NONE;
+ }
+
+ return result;
+}
#endif // CONFIG_WIFI
+
#ifndef CONFIG_ANJAY_CLIENT_FACTORY_PROVISIONING
const char *config_get_server_uri(void)
{
@@ -366,6 +399,7 @@ static int string_validate(const struct shell *shell, const char *value, size_t
* !defined(CONFIG_ANJAY_CLIENT_FACTORY_PROVISIONING)
*/
+
#ifndef CONFIG_ANJAY_CLIENT_FACTORY_PROVISIONING
static int flag_validate(const struct shell *shell, const char *value, size_t value_len,
const struct anjay_client_option *option)
diff --git a/demo/src/config.h b/demo/src/config.h
index 1eea5d1..be862b6 100644
--- a/demo/src/config.h
+++ b/demo/src/config.h
@@ -18,7 +18,13 @@
#include
-#include
+#include
+
+#ifdef CONFIG_WIFI
+#include
+#endif // CONFIG_WIFI
+
+#include "network/network.h"
#ifdef CONFIG_WIFI
#define OPTION_KEY_SSID wifi_ssid
@@ -48,6 +54,7 @@
* !defined(CONFIG_ANJAY_CLIENT_FACTORY_PROVISIONING)
*/
+
const char *config_default_ep_name(void);
#ifdef WITH_ANJAY_CLIENT_CONFIG
@@ -67,10 +74,11 @@ const char *config_get_endpoint_name(void);
#ifdef CONFIG_WIFI
const char *config_get_wifi_ssid(void);
-
const char *config_get_wifi_password(void);
+struct wifi_connect_req_params config_get_wifi_params(void);
#endif // CONFIG_WIFI
+
#ifndef CONFIG_ANJAY_CLIENT_FACTORY_PROVISIONING
const char *config_get_server_uri(void);
diff --git a/demo/src/default_config.h b/demo/src/default_config.h
index 708e787..03f600a 100644
--- a/demo/src/default_config.h
+++ b/demo/src/default_config.h
@@ -16,7 +16,9 @@
#pragma once
-#define CLIENT_VERSION "22.08-75-gd102363"
+#include "config.h"
+
+#define CLIENT_VERSION "22.12"
#ifdef CONFIG_WIFI
#define WIFI_SSID "ssid"
@@ -24,6 +26,7 @@
#define WIFI_PASSWORD "password"
#endif // CONFIG_WIFI
+
#define SERVER_URI "coaps://eu.iot.avsystem.cloud:5684"
#define LIFETIME "50"
diff --git a/demo/src/factory_provisioning/factory_flash.c b/demo/src/factory_provisioning/factory_flash.c
index 8e56257..b48a906 100644
--- a/demo/src/factory_provisioning/factory_flash.c
+++ b/demo/src/factory_provisioning/factory_flash.c
@@ -18,17 +18,18 @@
#include
#include
-#include
-#include
-#include
+#include
+#include
+#include
-#include
+#include
#include
#include
#include
#include "factory_flash.h"
+#include "../config.h"
/*
* THE PROCESS FOR FLASHING FACTORY PROVISIONING INFORMATION
@@ -54,7 +55,7 @@
*
* What happens under the hood during steps 3 and 4:
*
- * 1. run_anjay() in main.c calls factory_flash_input_stream_create(). This
+ * 1. run_anjay() in main.c calls factory_flash_input_stream_init(). This
* initializes the fake "provision_fs" file system and mounts it at /factory.
* 2. mcumgr starts writing the /factory/provision.cbor file. This will call:
* - provision_fs_stat() - that will error out; mcumgr code only does this
@@ -96,7 +97,6 @@ static enum {
FACTORY_FLASH_FINISHED
} factory_flash_state;
static char factory_flash_result[AVS_INT_STR_BUF_SIZE(int)];
-static size_t factory_flash_result_offset;
static K_MUTEX_DEFINE(factory_flash_mutex);
static K_CONDVAR_DEFINE(factory_flash_condvar);
@@ -104,66 +104,79 @@ static K_CONDVAR_DEFINE(factory_flash_condvar);
#define PROVISION_FS_TYPE FS_TYPE_EXTERNAL_BASE
#define PROVISION_FS_MOUNT_POINT "/factory"
-#define PROVISION_FS_FLASH_FILE PROVISION_FS_MOUNT_POINT "/provision.cbor"
-#define PROVISION_FS_RESULT_FILE PROVISION_FS_MOUNT_POINT "/result.txt"
+
+enum provision_fs_files { FLASH_FILE, RESULT_FILE, EP_FILE };
+
+static struct provision_fs_file_info {
+ const char *const name;
+ const uint8_t *value;
+ size_t size;
+ size_t offset;
+} files[] = {
+ [FLASH_FILE] = { .name = PROVISION_FS_MOUNT_POINT "/provision.cbor" },
+ [RESULT_FILE] = { .name = PROVISION_FS_MOUNT_POINT "/result.txt" },
+ [EP_FILE] = { .name = PROVISION_FS_MOUNT_POINT "/endpoint.txt" },
+};
// 15 seconds of inactivity is treated as EOF
#define PROVISION_FS_UPLOAD_TIMEOUT_MS 15000
static int provision_fs_open(struct fs_file_t *filp, const char *fs_path, fs_mode_t flags)
{
- (void)filp;
- (void)fs_path;
- (void)flags;
- if (strcmp(fs_path, PROVISION_FS_FLASH_FILE) == 0) {
+ if (strcmp(fs_path, files[FLASH_FILE].name) == 0) {
if ((flags & (FS_O_READ | FS_O_WRITE)) == FS_O_WRITE &&
factory_flash_state == FACTORY_FLASH_INITIAL) {
+ filp->filep = &files[FLASH_FILE];
return 0;
}
- } else if (strcmp(fs_path, PROVISION_FS_RESULT_FILE) == 0) {
+ } else if (strcmp(fs_path, files[RESULT_FILE].name) == 0) {
if ((flags & (FS_O_READ | FS_O_WRITE)) == FS_O_READ) {
- if (factory_flash_state < FACTORY_FLASH_FINISHED) {
- factory_flash_state = FACTORY_FLASH_EOF;
- k_condvar_broadcast(&factory_flash_condvar);
- }
+ filp->filep = &files[RESULT_FILE];
+ return 0;
+ }
+ } else if (strcmp(fs_path, files[EP_FILE].name) == 0) {
+ if ((flags & (FS_O_READ | FS_O_WRITE)) == FS_O_READ) {
+ filp->filep = &files[EP_FILE];
return 0;
}
}
+
return -ENOENT;
}
static ssize_t provision_fs_read(struct fs_file_t *filp, void *dest, size_t nbytes)
{
- // Read provisioning result
- (void)filp;
+ if (!filp->filep) {
+ return -EBADF;
+ }
+
ssize_t result = k_mutex_lock(&factory_flash_mutex, K_FOREVER);
if (result) {
return result;
}
- while (factory_flash_state != FACTORY_FLASH_FINISHED) {
- k_condvar_wait(&factory_flash_condvar, &factory_flash_mutex, K_FOREVER);
- }
+ if (filp->filep == &files[RESULT_FILE] || filp->filep == &files[EP_FILE]) {
+ struct provision_fs_file_info *file_info = filp->filep;
+ const uint8_t *src = file_info->value + file_info->offset;
+ size_t bytes_to_copy = AVS_MIN(file_info->size - file_info->offset, nbytes);
- size_t bytes_to_copy = strlen(factory_flash_result) - factory_flash_result_offset;
+ memcpy(dest, src, bytes_to_copy);
- if (nbytes < bytes_to_copy) {
- bytes_to_copy = nbytes;
+ file_info->offset += bytes_to_copy;
+ result = bytes_to_copy;
+ } else {
+ result = -EBADF;
}
- memcpy((char *)dest, factory_flash_result + factory_flash_result_offset, bytes_to_copy);
-
k_mutex_unlock(&factory_flash_mutex);
- return bytes_to_copy;
+ return result;
}
static ssize_t provision_fs_write(struct fs_file_t *filp, const void *src, size_t nbytes)
{
- // Write the actual provisioning data
- (void)filp;
- if (factory_flash_state != FACTORY_FLASH_INITIAL) {
+ if (factory_flash_state != FACTORY_FLASH_INITIAL || filp->filep != &files[FLASH_FILE]) {
return -EBADF;
}
@@ -209,19 +222,18 @@ static ssize_t provision_fs_write(struct fs_file_t *filp, const void *src, size_
static int provision_fs_lseek(struct fs_file_t *filp, off_t off, int whence)
{
- (void)filp;
- (void)off;
- (void)whence;
assert(whence == FS_SEEK_SET);
if (filp->flags & FS_O_WRITE) {
// The provisioning file
assert(off == (size_t)received_data_total);
} else {
// The status file
- if (off > strlen(factory_flash_result)) {
+ struct provision_fs_file_info *file_info = filp->filep;
+
+ if (off > file_info->size) {
return -ENXIO;
}
- factory_flash_result_offset = off;
+ file_info->offset = off;
}
return 0;
}
@@ -242,16 +254,22 @@ static int provision_fs_unlink(struct fs_mount_t *mountp, const char *name)
static int provision_fs_stat(struct fs_mount_t *mountp, const char *path, struct fs_dirent *entry)
{
(void)mountp;
- (void)path;
(void)entry;
- if (strcmp(path, PROVISION_FS_RESULT_FILE) != 0) {
+
+ if (strcmp(path, files[RESULT_FILE].name) != 0 && strcmp(path, files[EP_FILE].name) != 0) {
return -ENOENT;
}
int result = k_mutex_lock(&factory_flash_mutex, K_FOREVER);
- if (!result) {
- if (factory_flash_state == FACTORY_FLASH_INITIAL) {
+ if (result) {
+ return result;
+ }
+
+ struct provision_fs_file_info *file_info;
+
+ if (strcmp(path, files[RESULT_FILE].name) == 0) {
+ if (factory_flash_state < FACTORY_FLASH_FINISHED) {
factory_flash_state = FACTORY_FLASH_EOF;
k_condvar_broadcast(&factory_flash_condvar);
}
@@ -260,13 +278,21 @@ static int provision_fs_stat(struct fs_mount_t *mountp, const char *path, struct
k_condvar_wait(&factory_flash_condvar, &factory_flash_mutex, K_FOREVER);
}
- entry->type = FS_DIR_ENTRY_FILE;
- entry->size = strlen(factory_flash_result);
-
- k_mutex_unlock(&factory_flash_mutex);
+ file_info = &files[RESULT_FILE];
+ file_info->value = factory_flash_result;
+ } else {
+ file_info = &files[EP_FILE];
+ file_info->value = config_default_ep_name();
}
- return result;
+ file_info->size = strlen(file_info->value);
+
+ entry->size = file_info->size;
+ entry->type = FS_DIR_ENTRY_FILE;
+
+ k_mutex_unlock(&factory_flash_mutex);
+
+ return 0;
}
static const struct fs_file_system_t provision_fs = { .open = provision_fs_open,
@@ -336,7 +362,7 @@ static struct {
const avs_stream_v_table_t *const vtable;
} provision_stream = { .vtable = &provision_stream_vtable };
-avs_stream_t *factory_flash_input_stream_create(void)
+avs_stream_t *factory_flash_input_stream_init(void)
{
if (fs_register(PROVISION_FS_TYPE, &provision_fs) || fs_mount(&provision_fs_mount_point)) {
return NULL;
diff --git a/demo/src/factory_provisioning/factory_flash.h b/demo/src/factory_provisioning/factory_flash.h
index 45a8966..b243b18 100644
--- a/demo/src/factory_provisioning/factory_flash.h
+++ b/demo/src/factory_provisioning/factory_flash.h
@@ -18,5 +18,5 @@
#include
-avs_stream_t *factory_flash_input_stream_create(void);
+avs_stream_t *factory_flash_input_stream_init(void);
void factory_flash_finished(int result);
diff --git a/demo/src/factory_provisioning/provisioning_app.c b/demo/src/factory_provisioning/provisioning_app.c
index 0c9d832..91f5974 100644
--- a/demo/src/factory_provisioning/provisioning_app.c
+++ b/demo/src/factory_provisioning/provisioning_app.c
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#include
-#include
-#include
+#include
+#include
+#include
#include
#include
@@ -63,35 +63,24 @@ static void factory_provision(void)
LOG_INF("Factory provisioning information already present. "
"Please flash production firmware. Halting.");
} else {
+ LOG_WRN("NOTE: No more log messages will be displayed. Please use "
+ "mcumgr to check provisioning results");
+
+ LOG_INF("Device ready for provisioning.");
+
z_shell_log_backend_disable(shell_backend_uart_get_ptr()->log_backend);
- avs_stream_t *stream = factory_flash_input_stream_create();
+ avs_stream_t *stream = factory_flash_input_stream_init();
assert(stream);
avs_error_t err = anjay_factory_provision(anjay, stream);
- avs_error_t cleanup_err = avs_stream_cleanup(&stream);
+ // NOTE: Not calling avs_stream_cleanup() because stream is *NOT* heap-allocated
- if (avs_is_ok(err)) {
- err = cleanup_err;
- }
if (avs_is_ok(err) && persist_factory_provisioning_info(anjay)) {
err = avs_errno(AVS_EIO);
}
factory_flash_finished(avs_is_ok(err) ? 0 : -1);
-
- z_shell_log_backend_enable(shell_backend_uart_get_ptr()->log_backend,
- (struct shell *)(uintptr_t)shell_backend_uart_get_ptr(),
- AVS_MIN(CONFIG_SHELL_BACKEND_SERIAL_LOG_LEVEL,
- CONFIG_LOG_MAX_LEVEL));
-
- if (avs_is_err(err)) {
- LOG_ERR("Could not perform factory provisioning. Rebooting.");
- abort();
- } else {
- LOG_INF("Factory provisioning finished. "
- "Please flash production firmware. Halting.");
- }
}
while (true) {
diff --git a/demo/src/firmware_update.c b/demo/src/firmware_update.c
index be13c0d..27a2634 100644
--- a/demo/src/firmware_update.c
+++ b/demo/src/firmware_update.c
@@ -17,17 +17,21 @@
#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
+#include
+#include
+#include
#include "firmware_update.h"
#include "utils.h"
LOG_MODULE_REGISTER(fw_update);
+#define SETTINGS_ROOT_NAME "anjay_fw_update"
+#define SETTINGS_APP_JUST_UPDATED_KEY "app_just_updated"
+
static bool just_updated;
static bool update_requested;
@@ -70,12 +74,10 @@ static int fw_stream_finish(void *user_ptr)
assert(img_ctx_initialized);
- if (flash_img_buffered_write(&img_ctx, NULL, 0, true)) {
- return -1;
- }
+ int result = flash_img_buffered_write(&img_ctx, NULL, 0, true) ? -1 : 0;
img_ctx_initialized = false;
- return 0;
+ return result;
}
static void fw_reset(void *user_ptr)
@@ -126,11 +128,61 @@ int fw_update_install(anjay_t *anjay)
state.result = ANJAY_FW_UPDATE_INITIAL_SUCCESS;
}
- return anjay_fw_update_install(anjay, &handlers, anjay, &state);
+ int result = anjay_fw_update_install(anjay, &handlers, anjay, &state);
+
+ if (!result && just_updated) {
+ if (settings_delete(SETTINGS_ROOT_NAME "/" SETTINGS_APP_JUST_UPDATED_KEY)) {
+ LOG_ERR("Couldn't delete the just_updated flag");
+ }
+
+ just_updated = false;
+ }
+
+ return result;
+}
+
+static int fw_settings_set(const char *key, size_t len, settings_read_cb read_cb, void *cb_arg)
+{
+ if (strcmp(key, SETTINGS_APP_JUST_UPDATED_KEY) != 0) {
+ return -ENOENT;
+ }
+
+ if (len > 1) {
+ return -EINVAL;
+ }
+
+ char value = 0;
+
+ int result = read_cb(cb_arg, &value, len);
+
+ if (result < 0) {
+ return result;
+ }
+
+ if (value != 0) {
+ just_updated = true;
+ }
+
+ return 0;
}
+SETTINGS_STATIC_HANDLER_DEFINE(anjay_fw_update, SETTINGS_ROOT_NAME, NULL, fw_settings_set, NULL,
+ NULL);
+
void fw_update_apply(void)
{
+ int settings_state = settings_subsys_init();
+
+ if (settings_state) {
+ LOG_ERR("Couldn't init settings subsystem");
+ } else {
+ settings_load_subtree(SETTINGS_ROOT_NAME);
+ }
+
+ if (just_updated) {
+ LOG_INF("Undelivered previous firmware update success");
+ }
+
// Image may be unconfirmed, because:
// - we've just did a FOTA of the device and new
// firmware is being run
@@ -144,9 +196,17 @@ void fw_update_apply(void)
//
// We can differentiate these two situations by taking
// the retval of boot_write_img_confirmed().
- just_updated = !boot_is_img_confirmed() && !boot_write_img_confirmed();
- if (just_updated) {
+ if (!boot_is_img_confirmed() && !boot_write_img_confirmed()) {
LOG_INF("Successfully updated firmware");
+
+ if (!just_updated) {
+ just_updated = true;
+
+ if (settings_save_one(SETTINGS_ROOT_NAME "/" SETTINGS_APP_JUST_UPDATED_KEY,
+ "1", 1)) {
+ LOG_ERR("Couldn't save the just_updated flag");
+ }
+ }
}
}
diff --git a/demo/src/gps.h b/demo/src/gps.h
index fc4187a..d6466fe 100644
--- a/demo/src/gps.h
+++ b/demo/src/gps.h
@@ -16,9 +16,11 @@
#pragma once
-#include
+#include
#include
+#include
+
#ifdef CONFIG_ANJAY_CLIENT_GPS
struct gps_data {
bool valid;
@@ -49,4 +51,8 @@ int initialize_gps(void);
uint32_t gps_fetch_modem_agps_request_mask(void);
#endif // CONFIG_ANJAY_CLIENT_GPS_NRF_A_GPS
+#ifdef CONFIG_ANJAY_CLIENT_GPS_NRF
+extern volatile atomic_bool gps_prio_mode;
+#endif // CONFIG_ANJAY_CLIENT_GPS_NRF
+
#endif // CONFIG_ANJAY_CLIENT_GPS
diff --git a/demo/src/gps_impl/gps_nrf.c b/demo/src/gps_impl/gps_nrf.c
index 7b89f76..346d864 100644
--- a/demo/src/gps_impl/gps_nrf.c
+++ b/demo/src/gps_impl/gps_nrf.c
@@ -18,15 +18,16 @@
#error "This GPS implementation is not supported by selected board"
#endif // !(defined(CONFIG_BOARD_NRF9160DK_NRF9160_NS) || defined(CONFIG_BOARD_THINGY91_NRF9160_NS))
-#include
-#include
+#include
+#include
+#include
+
#include
#include
-#include
-#include
#include "../common.h"
#include "../config.h"
+#include "../network/network_internal.h"
#include "../gps.h"
#include "../utils.h"
@@ -57,6 +58,10 @@ static const char *const init_at_commands[] = {
#else // CONFIG_ANJAY_CLIENT_GPS_NRF_EXTERNAL_ANTENNA
"AT%XCOEX0=1,1,1565,1586",
#endif // CONFIG_ANJAY_CLIENT_GPS_NRF_EXTERNAL_ANTENNA
+
+ // https://infocenter.nordicsemi.com/topic/ref_at_commands/REF/at_commands/mob_termination_ctrl_status/cfun_set.html
+ // 31 - Activates GNSS without changing LTE.
+ "AT+CFUN=31",
};
K_MUTEX_DEFINE(gps_read_last_mtx);
@@ -76,6 +81,8 @@ static void prio_mode_disable_dwork_handler(struct k_work *work);
K_WORK_DEFINE(incoming_pvt_work, incoming_pvt_work_handler);
K_WORK_DELAYABLE_DEFINE(prio_mode_disable_dwork, prio_mode_disable_dwork_handler);
+volatile atomic_bool gps_prio_mode;
+
static int64_t gnss_datetime_to_timestamp(const struct nrf_modem_gnss_datetime *datetime)
{
struct tm broken_down = { .tm_year = datetime->year - 1900,
@@ -92,12 +99,8 @@ void prio_mode_disable(void)
{
LOG_INF("Disabling gnss_prio_mode");
- SYNCHRONIZED(global_anjay_mutex)
- {
- if (global_anjay) {
- anjay_transport_exit_offline(global_anjay, ANJAY_TRANSPORT_SET_IP);
- }
- }
+ atomic_store(&gps_prio_mode, false);
+ network_internal_connection_state_changed();
if (nrf_modem_gnss_prio_mode_disable()) {
LOG_ERR("Couldn't disable gnss_prio_mode");
@@ -161,13 +164,8 @@ static void incoming_pvt_work_handler(struct k_work *work)
return;
}
- SYNCHRONIZED(global_anjay_mutex)
- {
- if (global_anjay) {
- anjay_transport_enter_offline(global_anjay,
- ANJAY_TRANSPORT_SET_IP);
- }
- }
+ atomic_store(&gps_prio_mode, true);
+ network_internal_connection_state_changed();
k_work_schedule(&prio_mode_disable_dwork, K_SECONDS(gps_prio_mode_timeout));
}
diff --git a/demo/src/main_app.c b/demo/src/main_app.c
index 29a2afe..d2b7b04 100644
--- a/demo/src/main_app.c
+++ b/demo/src/main_app.c
@@ -14,17 +14,19 @@
* limitations under the License.
*/
-#include
-#include
#include
-#include
-#include
-#include
+
#include
-#include
-#include
-#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
#include
#include
@@ -43,23 +45,9 @@
#include "utils.h"
#include "persistence.h"
#include "status_led.h"
-#include "objects/objects.h"
-#if defined(CONFIG_WIFI)
-#ifdef CONFIG_WIFI_ESWIFI
-#include
-#endif // CONFIG_WIFI_ESWIFI
-#ifdef CONFIG_WIFI_ESP32
-#include
-#include
-#include
-#endif // CONFIG_WIFI_ESP32
-#include
-#include
-#elif defined(CONFIG_LTE_LINK_CONTROL)
-#include
-#endif /* defined(CONFIG_WIFI) || defined(CONFIG_LTE_LINK_CONTROL)
- */
+#include "network/network.h"
+#include "objects/objects.h"
#ifdef CONFIG_DATE_TIME
#include
@@ -69,6 +57,10 @@
#include "nrf_lc_info.h"
#endif // CONFIG_ANJAY_CLIENT_NRF_LC_INFO
+#ifdef CONFIG_NET_L2_OPENTHREAD
+#define RETRY_SYNC_CLOCK_DELAY_TIME_S 1
+#endif // CONFIG_NET_L2_OPENTHREAD
+
LOG_MODULE_REGISTER(main_app);
static const anjay_dm_object_def_t **buzzer_obj;
static const anjay_dm_object_def_t **device_obj;
@@ -99,13 +91,25 @@ K_MUTEX_DEFINE(anjay_thread_running_mutex);
K_CONDVAR_DEFINE(anjay_thread_running_condvar);
K_THREAD_STACK_DEFINE(anjay_stack, ANJAY_THREAD_STACK_SIZE);
-#if defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
+#ifdef CONFIG_NET_L2_OPENTHREAD
+static struct k_work_delayable sync_clock_work;
+#endif // CONFIG_NET_L2_OPENTHREAD
+
+static bool anjay_online;
+static enum network_bearer_t anjay_last_known_bearer;
+
+static K_SEM_DEFINE(synchronize_clock_sem, 0, 1);
+
+#if defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS) && defined(CONFIG_NRF_MODEM_LIB) && \
+ defined(CONFIG_MODEM_KEY_MGMT)
// The only parameters needed to address a credential stored in the modem
// are its type and its security tag - the type is defined already by
// the proper function being called, so the query contains only a single
// integer - the desired security tag.
static const char *PSK_QUERY = "1";
-#endif // defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
+#endif /* defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS) && defined(CONFIG_NRF_MODEM_LIB) &&
+ * defined(CONFIG_MODEM_KEY_MGMT)
+ */
#ifdef CONFIG_DATE_TIME
static void set_system_time(const struct sntp_time *time)
@@ -131,13 +135,31 @@ void synchronize_clock(void)
struct sntp_time time;
const uint32_t timeout_ms = 5000;
- if (sntp_simple(NTP_SERVER, timeout_ms, &time)) {
- LOG_WRN("Failed to get current time");
- } else {
+ if (false
+#if defined(CONFIG_NET_IPV6)
+ || !sntp_simple_ipv6(NTP_SERVER, timeout_ms, &time)
+#endif
+#if defined(CONFIG_NET_IPV4)
+ || !sntp_simple(NTP_SERVER, timeout_ms, &time)
+#endif
+ ) {
set_system_time(&time);
+ k_sem_give(&synchronize_clock_sem);
+ } else {
+ LOG_WRN("Failed to get current time");
+#ifdef CONFIG_NET_L2_OPENTHREAD
+ k_work_schedule(&sync_clock_work, K_SECONDS(RETRY_SYNC_CLOCK_DELAY_TIME_S));
+#endif // CONFIG_NET_L2_OPENTHREAD
}
}
+#ifdef CONFIG_NET_L2_OPENTHREAD
+static void retry_synchronize_clock_work_handler(struct k_work *work)
+{
+ synchronize_clock();
+}
+#endif // CONFIG_NET_L2_OPENTHREAD
+
static int register_objects(anjay_t *anjay)
{
device_obj = device_object_create();
@@ -302,183 +324,13 @@ static void release_objects(void)
#endif // CONFIG_ANJAY_CLIENT_LOCATION_SERVICES
}
-static K_MUTEX_DEFINE(net_connect_mutex);
-static K_CONDVAR_DEFINE(net_connect_condvar);
-
-#if defined(CONFIG_WIFI)
-static volatile atomic_bool ip_addr_assigned;
-
-static void ip_addr_add_cb(struct net_mgmt_event_callback *cb, uint32_t mgmt_event,
- struct net_if *iface)
-{
- (void)cb;
- (void)mgmt_event;
- (void)iface;
-
- atomic_store(&ip_addr_assigned, true);
- interrupt_net_connect_wait_loop();
-}
-
-static int wait_for_ip_addr_interruptible(void)
-{
- // Note: this mirrors the logic of net_mgmt_event_wait_on_iface(), but there is
- // a nasty bug in that net_mgmt_del_event_callback() is never called by that function.
- struct net_mgmt_event_callback cb;
- int ret = -EAGAIN;
-
- memset(&cb, 0, sizeof(cb));
- net_mgmt_init_event_callback(&cb, ip_addr_add_cb, NET_EVENT_IPV4_ADDR_ADD);
- atomic_store(&ip_addr_assigned, false);
- net_mgmt_add_event_callback(&cb);
-
- SYNCHRONIZED(net_connect_mutex)
- {
- do {
- k_condvar_wait(&net_connect_condvar, &net_connect_mutex, K_FOREVER);
- if (atomic_load(&ip_addr_assigned)) {
- ret = 0;
- } else if (!atomic_load(&anjay_running)) {
- ret = -ETIMEDOUT;
- }
- } while (ret == -EAGAIN);
- }
-
- net_mgmt_del_event_callback(&cb);
-
- return ret;
-}
-#elif defined(CONFIG_LTE_LINK_CONTROL)
-static void lte_connect_handler(const struct lte_lc_evt *const evt)
-{
- if (evt && evt->type == LTE_LC_EVT_NW_REG_STATUS &&
- (evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ||
- evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_ROAMING)) {
- interrupt_net_connect_wait_loop();
- }
-}
-
-static int lte_interruptible_connect(void)
-{
- int ret = -1;
- enum lte_lc_nw_reg_status status;
-
- SYNCHRONIZED(net_connect_mutex)
- {
- // Note: this is supposed to be handled by lte_lc_connect_async(),
- // but there is a nasty bug in in_progress flag handling
- ret = lte_lc_nw_reg_status_get(&status);
- if (!ret && status != LTE_LC_NW_REG_REGISTERED_HOME &&
- status != LTE_LC_NW_REG_REGISTERED_ROAMING) {
- ret = lte_lc_connect_async(lte_connect_handler);
- while (!ret) {
- if (!atomic_load(&anjay_running)) {
- ret = -ETIMEDOUT;
- } else {
- ret = lte_lc_nw_reg_status_get(&status);
- if (!ret && (status == LTE_LC_NW_REG_REGISTERED_HOME ||
- status == LTE_LC_NW_REG_REGISTERED_ROAMING)) {
- break;
- }
- k_condvar_wait(&net_connect_condvar, &net_connect_mutex,
- K_FOREVER);
- }
- }
- }
- }
-
- return ret;
-}
-#endif // defined(CONFIG_WIFI) || defined(CONFIG_LTE_LINK_CONTROL)
-
-void interrupt_net_connect_wait_loop(void)
-{
- SYNCHRONIZED(net_connect_mutex)
- {
- k_condvar_broadcast(&net_connect_condvar);
- }
-}
-
-static int initialize_network(void)
-{
- LOG_INF("Initializing network connection...");
-#if defined(CONFIG_WIFI)
- struct net_if *iface = net_if_get_default();
-
-#ifdef CONFIG_WIFI_ESWIFI
- struct eswifi_dev *eswifi = eswifi_by_iface_idx(0);
-
- assert(eswifi);
- // Set regulatory domain to "World Wide (passive Ch12-14)"; eS-WiFi defaults
- // to "US" which prevents connecting to networks that use channels 12-14.
- if (eswifi_at_cmd(eswifi, "CN=XV\r") < 0) {
- LOG_WRN("Failed to set Wi-Fi regulatory domain");
- }
-#endif // CONFIG_WIFI_ESWIFI
-
-#ifdef CONFIG_WIFI_ESP32
- net_dhcpv4_start(iface);
-
- AVS_STATIC_ASSERT(!IS_ENABLED(CONFIG_ESP32_WIFI_STA_AUTO),
- esp32_wifi_auto_mode_incompatible_with_project);
-
- wifi_config_t wifi_config = { 0 };
-
- // use strncpy with the maximum length of sizeof(wifi_config.sta.{ssid|password}),
- // because ESP32 Wi-Fi buffers don't have to be null-terminated
- strncpy(wifi_config.sta.ssid, config_get_wifi_ssid(), sizeof(wifi_config.sta.ssid));
- strncpy(wifi_config.sta.password, config_get_wifi_password(),
- sizeof(wifi_config.sta.password));
-
- if (esp_wifi_set_mode(WIFI_MODE_STA) ||
- esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) || esp_wifi_connect()) {
- LOG_ERR("connection failed");
- }
-#else // CONFIG_WIFI_ESP32
- struct wifi_connect_req_params wifi_params = { .ssid = (uint8_t *)config_get_wifi_ssid(),
- .ssid_length =
- strlen(config_get_wifi_ssid()),
- .psk = (uint8_t *)config_get_wifi_password(),
- .psk_length =
- strlen(config_get_wifi_password()),
- .security = WIFI_SECURITY_TYPE_PSK };
-
- int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, &wifi_params,
- sizeof(struct wifi_connect_req_params));
-
- if (ret && ret != -EALREADY && ret != -EINPROGRESS) {
- LOG_ERR("Failed to configure Wi-Fi");
- return -1;
- }
-#endif // CONFIG_WIFI_ESP32
-
- if (wait_for_ip_addr_interruptible()) {
- LOG_ERR("Wi-Fi link could not be established.");
- net_mgmt(NET_REQUEST_WIFI_DISCONNECT, iface, NULL, 0);
- return -1;
- }
-#elif defined(CONFIG_LTE_LINK_CONTROL)
- int ret = lte_lc_init();
-
- if (!ret) {
- ret = lte_interruptible_connect();
- }
-
- if (ret < 0 && ret != -EALREADY && ret != -EINPROGRESS) {
- LOG_ERR("LTE link could not be established.");
- return -1;
- }
-#endif /* defined(CONFIG_WIFI) || defined(CONFIG_LTE_LINK_CONTROL)
- */
- LOG_INF("Connected to network");
- return 0;
-}
-
#ifndef CONFIG_ANJAY_CLIENT_FACTORY_PROVISIONING
static int configure_servers_from_config(anjay_t *anjay, const anjay_configuration_t *config)
{
const bool bootstrap = config_is_bootstrap();
-#if defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
+#if defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS) && defined(CONFIG_NRF_MODEM_LIB) && \
+ defined(CONFIG_MODEM_KEY_MGMT)
const char *psk_key = config_get_psk();
avs_crypto_psk_key_info_t psk_key_info =
avs_crypto_psk_key_info_from_buffer(psk_key, strlen(psk_key));
@@ -492,23 +344,30 @@ static int configure_servers_from_config(anjay_t *anjay, const anjay_configurati
LOG_ERR("Storing PSK identity failed");
return -1;
}
-#endif // defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
+#endif /* defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS) && defined(CONFIG_NRF_MODEM_LIB) &&
+ * defined(CONFIG_MODEM_KEY_MGMT)
+ */
const anjay_security_instance_t security_instance = {
.ssid = 1,
.bootstrap_server = bootstrap,
.server_uri = config_get_server_uri(),
.security_mode = ANJAY_SECURITY_PSK,
-#if defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
+#if defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS) && defined(CONFIG_NRF_MODEM_LIB) && \
+ defined(CONFIG_MODEM_KEY_MGMT)
.psk_identity = avs_crypto_psk_identity_info_from_engine(PSK_QUERY),
.psk_key = avs_crypto_psk_key_info_from_engine(PSK_QUERY),
-#else // defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
+#else /* defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS) && defined(CONFIG_NRF_MODEM_LIB) &&
+ * defined(CONFIG_MODEM_KEY_MGMT)
+ */
.public_cert_or_psk_identity = config->endpoint_name,
.public_cert_or_psk_identity_size =
strlen(security_instance.public_cert_or_psk_identity),
.private_cert_or_psk_key = config_get_psk(),
.private_cert_or_psk_key_size = strlen(security_instance.private_cert_or_psk_key)
-#endif // defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
+#endif /* defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS) && defined(CONFIG_NRF_MODEM_LIB) &&
+ * defined(CONFIG_MODEM_KEY_MGMT)
+ */
};
const anjay_server_instance_t server_instance = { .ssid = 1,
@@ -541,17 +400,6 @@ static int configure_servers_from_config(anjay_t *anjay, const anjay_configurati
static anjay_t *initialize_anjay(void)
{
- if (initialize_network()) {
- LOG_ERR("Could not connect to the network");
- return NULL;
- }
-
-#ifdef CONFIG_ANJAY_CLIENT_GPS
- initialize_gps();
-#endif // CONFIG_ANJAY_CLIENT_GPS
-
- synchronize_clock();
-
const anjay_configuration_t config = {
#ifdef CONFIG_ANJAY_CLIENT_FACTORY_PROVISIONING
.endpoint_name = config_default_ep_name(),
@@ -617,72 +465,155 @@ static anjay_t *initialize_anjay(void)
#endif // CONFIG_ANJAY_CLIENT_FACTORY_PROVISIONING
error:
-#if defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
+#if defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS) && defined(CONFIG_NRF_MODEM_LIB) && \
+ defined(CONFIG_MODEM_KEY_MGMT)
if (avs_is_err(avs_crypto_psk_engine_key_rm(PSK_QUERY))) {
LOG_WRN("Removing PSK key failed");
}
if (avs_is_err(avs_crypto_psk_engine_identity_rm(PSK_QUERY))) {
LOG_WRN("Removing PSK identity failed");
}
-#endif // defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
+#endif /* defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS) && defined(CONFIG_NRF_MODEM_LIB) &&
+ * defined(CONFIG_MODEM_KEY_MGMT)
+ */
anjay_delete(anjay);
release_objects();
return NULL;
}
+static void update_anjay_network_bearer_unlocked(anjay_t *anjay, enum network_bearer_t bearer)
+{
+ if (anjay_online && !network_bearer_valid(bearer)) {
+ LOG_INF("Anjay is now offline");
+ if (!anjay_transport_enter_offline(anjay, ANJAY_TRANSPORT_SET_ALL)) {
+ anjay_online = false;
+ }
+ } else if (network_bearer_valid(bearer) &&
+ (!anjay_online || anjay_last_known_bearer != bearer)) {
+ LOG_INF("Anjay is now online on bearer %d", (int)bearer);
+ if (anjay_last_known_bearer != bearer) {
+ if (!anjay_transport_schedule_reconnect(anjay, ANJAY_TRANSPORT_SET_ALL)) {
+ anjay_last_known_bearer = bearer;
+ anjay_online = true;
+ }
+ } else if (!anjay_transport_exit_offline(anjay, ANJAY_TRANSPORT_SET_ALL)) {
+ anjay_online = true;
+ }
+ }
+}
+
+static void update_anjay_network_bearer_job(avs_sched_t *sched, const void *dummy)
+{
+ ARG_UNUSED(dummy);
+
+ SYNCHRONIZED(global_anjay_mutex)
+ {
+ if (global_anjay) {
+ update_anjay_network_bearer_unlocked(global_anjay,
+ network_current_bearer());
+ }
+ }
+}
+
+void sched_update_anjay_network_bearer(void)
+{
+ static avs_sched_handle_t job_handle;
+
+ SYNCHRONIZED(global_anjay_mutex)
+ {
+ if (global_anjay) {
+ AVS_SCHED_NOW(anjay_get_scheduler(global_anjay), &job_handle,
+ update_anjay_network_bearer_job, NULL, 0);
+ }
+ }
+}
+
void run_anjay(void *arg1, void *arg2, void *arg3)
{
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
ARG_UNUSED(arg3);
+ LOG_INF("Connecting to the network...");
+
+ if (network_connect_async()) {
+ LOG_ERR("Could not initiate connection");
+ goto finish;
+ }
+
+ if (network_wait_for_connected_interruptible()) {
+ LOG_ERR("Could not connect to the network");
+ goto disconnect;
+ }
+
+ LOG_INF("Connected to network");
+
+ k_sem_reset(&synchronize_clock_sem);
+ synchronize_clock();
+ if (k_sem_take(&synchronize_clock_sem, K_SECONDS(30))) {
+ LOG_WRN("Could not synchronize system clock within timeout, "
+ "continuing without real time...");
+ }
+
anjay_t *anjay = initialize_anjay();
- if (anjay) {
- LOG_INF("Successfully created thread");
+ if (!anjay) {
+ goto disconnect;
+ }
- SYNCHRONIZED(global_anjay_mutex)
- {
- global_anjay = anjay;
- }
+ LOG_INF("Successfully created thread");
- // anjay stop could be called immediately after anjay start
- if (atomic_load(&anjay_running)) {
- update_objects(anjay_get_scheduler(anjay), &anjay);
- anjay_event_loop_run_with_error_handling(
- anjay, avs_time_duration_from_scalar(1, AVS_TIME_S));
- }
+ SYNCHRONIZED(global_anjay_mutex)
+ {
+ global_anjay = anjay;
- avs_sched_del(&update_objects_handle);
+ anjay_last_known_bearer = (enum network_bearer_t)0;
+ update_anjay_network_bearer_unlocked(anjay, network_current_bearer());
+ }
+
+ // anjay stop could be called immediately after anjay start
+ if (atomic_load(&anjay_running)) {
+ update_objects(anjay_get_scheduler(anjay), &anjay);
+ anjay_event_loop_run_with_error_handling(
+ anjay, avs_time_duration_from_scalar(1, AVS_TIME_S));
+ }
+
+ avs_sched_del(&update_objects_handle);
#ifdef CONFIG_ANJAY_CLIENT_PERSISTENCE
- if (config_is_use_persistence() && persist_anjay_if_required(anjay)) {
- LOG_ERR("Couldn't persist Anjay's state!");
- }
+ if (config_is_use_persistence() && persist_anjay_if_required(anjay)) {
+ LOG_ERR("Couldn't persist Anjay's state!");
+ }
#endif // CONFIG_ANJAY_CLIENT_PERSISTENCE
- SYNCHRONIZED(global_anjay_mutex)
- {
- global_anjay = NULL;
- }
- anjay_delete(anjay);
- release_objects();
+ SYNCHRONIZED(global_anjay_mutex)
+ {
+ global_anjay = NULL;
+ }
+ anjay_delete(anjay);
+ release_objects();
-#if defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
- if (avs_is_err(avs_crypto_psk_engine_key_rm(PSK_QUERY))) {
- LOG_WRN("Removing PSK key failed");
- }
- if (avs_is_err(avs_crypto_psk_engine_identity_rm(PSK_QUERY))) {
- LOG_WRN("Removing PSK identity failed");
- }
-#endif // defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT)
+#if defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS) && defined(CONFIG_NRF_MODEM_LIB) && \
+ defined(CONFIG_MODEM_KEY_MGMT)
+ if (avs_is_err(avs_crypto_psk_engine_key_rm(PSK_QUERY))) {
+ LOG_WRN("Removing PSK key failed");
+ }
+ if (avs_is_err(avs_crypto_psk_engine_identity_rm(PSK_QUERY))) {
+ LOG_WRN("Removing PSK identity failed");
+ }
+#endif /* defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS) && defined(CONFIG_NRF_MODEM_LIB) &&
+ * defined(CONFIG_MODEM_KEY_MGMT)
+ */
#ifdef CONFIG_ANJAY_CLIENT_FOTA
- if (fw_update_requested()) {
- fw_update_reboot();
- }
-#endif // CONFIG_ANJAY_CLIENT_FOTA
+ if (fw_update_requested()) {
+ fw_update_reboot();
}
+#endif // CONFIG_ANJAY_CLIENT_FOTA
+
+disconnect:
+ network_disconnect();
+finish:
SYNCHRONIZED(anjay_thread_running_mutex)
{
anjay_thread_running = false;
@@ -692,6 +623,8 @@ void run_anjay(void *arg1, void *arg2, void *arg3)
void main(void)
{
+ LOG_INF("Initializing Anjay-zephyr-client demo " CLIENT_VERSION);
+
#ifdef WITH_ANJAY_CLIENT_CONFIG
config_init(shell_backend_uart_get_ptr());
#endif // WITH_ANJAY_CLIENT_CONFIG
@@ -704,6 +637,20 @@ void main(void)
status_led_init();
+#ifdef CONFIG_NET_L2_OPENTHREAD
+ k_work_init_delayable(&sync_clock_work, retry_synchronize_clock_work_handler);
+#endif // CONFIG_NET_L2_OPENTHREAD
+
+ if (network_initialize()) {
+ LOG_ERR("Cannot initialize the network");
+ LOG_PANIC();
+ abort();
+ }
+
+#ifdef CONFIG_ANJAY_CLIENT_GPS
+ initialize_gps();
+#endif // CONFIG_ANJAY_CLIENT_GPS
+
#ifdef CONFIG_ANJAY_CLIENT_FOTA
fw_update_apply();
#endif // CONFIG_ANJAY_CLIENT_FOTA
diff --git a/demo/src/network/network.c b/demo/src/network/network.c
new file mode 100644
index 0000000..d186868
--- /dev/null
+++ b/demo/src/network/network.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2020-2022 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 "network.h"
+#include "network_internal.h"
+
+#include "../common.h"
+#include "../utils.h"
+
+K_MUTEX_DEFINE(network_internal_connect_mutex);
+K_CONDVAR_DEFINE(network_internal_connect_condvar);
+
+#ifdef CONFIG_NET_NATIVE_IPV4
+static struct net_mgmt_event_callback ipv4_mgmt_cb;
+#endif // CONFIG_NET_NATIVE_IPV4
+#ifdef CONFIG_NET_NATIVE_IPV6
+static struct net_mgmt_event_callback ipv6_mgmt_cb;
+#endif // CONFIG_NET_NATIVE_IPV6
+
+#if defined(CONFIG_NET_NATIVE_IPV4) || defined(CONFIG_NET_NATIVE_IPV6)
+static void net_mgmt_event_cb(struct net_mgmt_event_callback *cb, uint32_t mgmt_event,
+ struct net_if *iface)
+{
+ network_internal_connection_state_changed();
+}
+
+bool network_internal_has_ip_address(struct net_if *iface)
+{
+#ifdef CONFIG_NET_NATIVE_IPV4
+ if (net_if_ipv4_get_global_addr(iface, NET_ADDR_ANY_STATE) ||
+ net_if_ipv4_get_ll(iface, NET_ADDR_ANY_STATE)) {
+ return true;
+ }
+#endif // CONFIG_NET_NATIVE_IPV4
+
+#ifdef CONFIG_NET_NATIVE_IPV6
+ // Link-local IPv6 addresses are useless, as every interface always has one.
+ // Also, NET_ADDR_ANY_STATE does not work with net_if_ipv6_get_global_addr().
+ if (net_if_ipv6_get_global_addr(NET_ADDR_PREFERRED, &iface)) {
+ return true;
+ }
+#endif // CONFIG_NET_NATIVE_IPV6
+
+ return false;
+}
+
+__weak enum network_bearer_t network_current_bearer(void)
+{
+ bool connected = network_internal_has_ip_address(net_if_get_default());
+
+ return connected ? (enum network_bearer_t)0 : NETWORK_BEARER_LIMIT;
+}
+#endif // defined(CONFIG_NET_NATIVE_IPV4) || defined(CONFIG_NET_NATIVE_IPV6)
+
+void network_interrupt_connect_wait_loop(void)
+{
+ SYNCHRONIZED(network_internal_connect_mutex)
+ {
+ k_condvar_broadcast(&network_internal_connect_condvar);
+ }
+}
+
+void network_internal_connection_state_changed(void)
+{
+ sched_update_anjay_network_bearer();
+ network_interrupt_connect_wait_loop();
+}
+
+int network_initialize(void)
+{
+ int ret = network_internal_platform_initialize();
+
+ if (ret) {
+ return ret;
+ }
+
+#ifdef CONFIG_NET_NATIVE_IPV4
+ net_mgmt_init_event_callback(&ipv4_mgmt_cb, net_mgmt_event_cb,
+ NET_EVENT_IPV4_ADDR_ADD | NET_EVENT_IPV4_ADDR_DEL);
+ net_mgmt_add_event_callback(&ipv4_mgmt_cb);
+#endif // CONFIG_NET_NATIVE_IPV4
+#ifdef CONFIG_NET_NATIVE_IPV6
+ net_mgmt_init_event_callback(&ipv6_mgmt_cb, net_mgmt_event_cb,
+ NET_EVENT_IPV6_ADDR_ADD | NET_EVENT_IPV6_ADDR_DEL);
+ net_mgmt_add_event_callback(&ipv6_mgmt_cb);
+#endif // CONFIG_NET_NATIVE_IPV6
+ return 0;
+}
+
+int network_wait_for_connected_interruptible(void)
+{
+ int ret = -EAGAIN;
+
+ SYNCHRONIZED(network_internal_connect_mutex)
+ {
+ do {
+ if (network_is_connected()) {
+ ret = 0;
+ } else if (!atomic_load(&anjay_running)) {
+ ret = -ETIMEDOUT;
+ } else {
+ k_condvar_wait(&network_internal_connect_condvar,
+ &network_internal_connect_mutex, K_FOREVER);
+ }
+ } while (ret == -EAGAIN);
+ }
+
+ return ret;
+}
diff --git a/demo/src/network/network.h b/demo/src/network/network.h
new file mode 100644
index 0000000..16ae3b4
--- /dev/null
+++ b/demo/src/network/network.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020-2022 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
+
+enum network_bearer_t {
+#ifdef CONFIG_WIFI
+ NETWORK_BEARER_WIFI,
+#endif // CONFIG_WIFI
+#if defined(CONFIG_MODEM) || defined(CONFIG_LTE_LINK_CONTROL)
+ NETWORK_BEARER_CELLULAR,
+#endif // defined(CONFIG_MODEM) || defined(CONFIG_LTE_LINK_CONTROL)
+#ifdef CONFIG_NET_L2_OPENTHREAD
+ NETWORK_BEARER_OPENTHREAD,
+#endif // CONFIG_NET_L2_OPENTHREAD
+ NETWORK_BEARER_LIMIT
+};
+
+AVS_STATIC_ASSERT(NETWORK_BEARER_LIMIT > 0, no_network_bearers_available);
+
+static inline bool network_bearer_valid(enum network_bearer_t bearer)
+{
+ return bearer >= (enum network_bearer_t)0 && bearer < NETWORK_BEARER_LIMIT;
+}
+
+void network_interrupt_connect_wait_loop(void);
+int network_initialize(void);
+int network_connect_async(void);
+enum network_bearer_t network_current_bearer(void);
+int network_wait_for_connected_interruptible(void);
+void network_disconnect(void);
+
+static inline bool network_is_connected(void)
+{
+ return network_bearer_valid(network_current_bearer());
+}
diff --git a/demo/src/network/network_esp32.c b/demo/src/network/network_esp32.c
new file mode 100644
index 0000000..709288e
--- /dev/null
+++ b/demo/src/network/network_esp32.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2020-2022 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 "network.h"
+#include "network_internal.h"
+
+#include "../config.h"
+#include "../utils.h"
+
+LOG_MODULE_REGISTER(network_esp32);
+
+static void esp32_connect_work_cb(struct k_work *work)
+{
+ net_dhcpv4_start(net_if_get_default());
+}
+
+static void esp32_disconnect_work_cb(struct k_work *work)
+{
+ net_dhcpv4_stop(net_if_get_default());
+}
+
+static K_WORK_DEFINE(esp32_connect_work, esp32_connect_work_cb);
+static K_WORK_DEFINE(esp32_disconnect_work, esp32_disconnect_work_cb);
+
+struct net_mgmt_event_callback esp32_netif_updown_cb_obj = { 0 };
+
+static void esp32_netif_updown_cb(struct net_mgmt_event_callback *cb, uint32_t mgmt_event,
+ struct net_if *iface)
+{
+ if (mgmt_event == NET_EVENT_IF_UP) {
+ k_work_cancel(&esp32_disconnect_work);
+ k_work_submit(&esp32_connect_work);
+ } else if (mgmt_event == NET_EVENT_IF_DOWN) {
+ k_work_cancel(&esp32_connect_work);
+ k_work_submit(&esp32_disconnect_work);
+ }
+}
+
+int network_internal_platform_initialize(void)
+{
+ net_mgmt_init_event_callback(&esp32_netif_updown_cb_obj, esp32_netif_updown_cb,
+ NET_EVENT_IF_UP | NET_EVENT_IF_DOWN);
+ net_mgmt_add_event_callback(&esp32_netif_updown_cb_obj);
+
+ AVS_STATIC_ASSERT(!IS_ENABLED(CONFIG_ESP32_WIFI_STA_AUTO),
+ esp32_wifi_auto_mode_incompatible_with_project);
+
+ return 0;
+}
+
+int network_connect_async(void)
+{
+ wifi_config_t wifi_config = { 0 };
+
+ // use strncpy with the maximum length of sizeof(wifi_config.sta.{ssid|password}),
+ // because ESP32 Wi-Fi buffers don't have to be null-terminated
+ strncpy(wifi_config.sta.ssid, config_get_wifi_ssid(), sizeof(wifi_config.sta.ssid));
+ strncpy(wifi_config.sta.password, config_get_wifi_password(),
+ sizeof(wifi_config.sta.password));
+
+ if (esp_wifi_set_mode(ESP32_WIFI_MODE_STA) ||
+ esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) || esp_wifi_connect()) {
+ LOG_ERR("connection failed");
+ return -1;
+ }
+ return 0;
+}
+
+void network_disconnect(void)
+{
+ esp_wifi_disconnect();
+ esp_wifi_set_mode(ESP32_WIFI_MODE_NULL);
+}
diff --git a/demo/src/network/network_eswifi.c b/demo/src/network/network_eswifi.c
new file mode 100644
index 0000000..6a6aa73
--- /dev/null
+++ b/demo/src/network/network_eswifi.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2020-2022 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 "network.h"
+#include "network_internal.h"
+
+#include "../config.h"
+#include "../utils.h"
+
+LOG_MODULE_REGISTER(network_wifi);
+
+static struct wifi_connect_req_params wifi_params;
+
+static void eswifi_reconnect_work_cb(struct k_work *work);
+static void eswifi_keepalive_work_cb(struct k_work *work);
+
+static K_WORK_DELAYABLE_DEFINE(eswifi_reconnect_work, eswifi_reconnect_work_cb);
+static K_WORK_DELAYABLE_DEFINE(eswifi_keepalive_work, eswifi_keepalive_work_cb);
+
+static void eswifi_reconnect_work_cb(struct k_work *work)
+{
+ net_mgmt(NET_REQUEST_WIFI_CONNECT, eswifi_by_iface_idx(0)->iface, &wifi_params,
+ sizeof(struct wifi_connect_req_params));
+}
+
+static K_SEM_DEFINE(eswifi_disconnect_sync_result_cb_sem, 0, 1);
+
+static void eswifi_disconnect_sync_result_cb(struct net_mgmt_event_callback *cb,
+ uint32_t mgmt_event, struct net_if *iface)
+{
+ k_sem_give(&eswifi_disconnect_sync_result_cb_sem);
+}
+
+static void eswifi_disconnect_sync(struct net_if *iface)
+{
+ struct net_mgmt_event_callback cb = { 0 };
+
+ net_mgmt_init_event_callback(&cb, eswifi_disconnect_sync_result_cb,
+ NET_EVENT_WIFI_DISCONNECT_RESULT);
+ net_mgmt_add_event_callback(&cb);
+
+ int ret = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, iface, NULL, 0);
+
+ if (ret >= 0 || ret == -EINPROGRESS) {
+ k_sem_take(&eswifi_disconnect_sync_result_cb_sem, Z_FOREVER);
+ }
+
+ net_mgmt_del_event_callback(&cb);
+}
+
+static void eswifi_keepalive_work_cb(struct k_work *work)
+{
+ char *result = NULL;
+ struct eswifi_dev *eswifi = eswifi_by_iface_idx(0);
+
+ eswifi_lock(eswifi);
+
+ bool connected =
+ (eswifi_at_cmd_rsp(eswifi, "CS\r", &result) >= 0 && result && result[0] == '1');
+
+ eswifi_unlock(eswifi);
+
+ if (!connected) {
+ // Lost connection, let's try reconnecting
+ eswifi_disconnect_sync(eswifi->iface);
+
+ // Issuing NET_REQUEST_WIFI_CONNECT locks the eswifi mutex for 30 seconds.
+ // This blocks e.g. poll() in the event loop - so let's call it via k_work_delayable
+ // to give the event loop a bit of time to perform all the close actions.
+ k_work_schedule(&eswifi_reconnect_work, K_SECONDS(5));
+ } else {
+ k_work_schedule(&eswifi_keepalive_work,
+ K_SECONDS(CONFIG_ANJAY_CLIENT_NETWORK_KEEPALIVE_RATE));
+ }
+}
+
+static void eswifi_disconnect_work_cb(struct k_work *work)
+{
+ struct eswifi_dev *eswifi = eswifi_by_iface_idx(0);
+
+ while (true) {
+ struct in_addr *addr =
+ net_if_ipv4_get_global_addr(eswifi->iface, NET_ADDR_ANY_STATE);
+
+ if (!addr) {
+ break;
+ }
+ net_if_ipv4_addr_rm(eswifi->iface, addr);
+ }
+
+ while (true) {
+ struct in_addr *addr = net_if_ipv4_get_ll(eswifi->iface, NET_ADDR_ANY_STATE);
+
+ if (!addr) {
+ break;
+ }
+ net_if_ipv4_addr_rm(eswifi->iface, addr);
+ }
+}
+
+static K_WORK_DEFINE(eswifi_disconnect_work, eswifi_disconnect_work_cb);
+
+static struct net_mgmt_event_callback eswifi_mgmt_cb_obj;
+
+static void eswifi_mgmt_cb(struct net_mgmt_event_callback *cb, uint32_t mgmt_event,
+ struct net_if *iface)
+{
+ if (mgmt_event == NET_EVENT_WIFI_CONNECT_RESULT) {
+ struct k_work_delayable *dwork = &eswifi_keepalive_work;
+ const struct wifi_status *status = (const struct wifi_status *)cb->info;
+
+ if (status->status < 0) {
+ // Connect error, retry
+ LOG_WRN("Could not connect to WiFi, retrying...");
+ dwork = &eswifi_reconnect_work;
+ }
+
+ k_work_schedule(dwork, K_SECONDS(CONFIG_ANJAY_CLIENT_NETWORK_KEEPALIVE_RATE));
+ } else if (mgmt_event == NET_EVENT_WIFI_DISCONNECT_RESULT) {
+ // eswifi driver doesn't clear IP addrs on disconnect, let's remove them manually
+ // Nested network event handling is explicitly disabled, so let's do that via k_work
+ k_work_submit(&eswifi_disconnect_work);
+ }
+}
+
+int network_internal_platform_initialize(void)
+{
+ struct eswifi_dev *eswifi = eswifi_by_iface_idx(0);
+
+ assert(eswifi);
+ eswifi_lock(eswifi);
+ // Set regulatory domain to "World Wide (passive Ch12-14)"; eS-WiFi defaults
+ // to "US" which prevents connecting to networks that use channels 12-14.
+ if (eswifi_at_cmd(eswifi, "CN=XV\r") < 0) {
+ LOG_WRN("Failed to set Wi-Fi regulatory domain");
+ }
+
+ net_mgmt_init_event_callback(&eswifi_mgmt_cb_obj, eswifi_mgmt_cb,
+ NET_EVENT_WIFI_CONNECT_RESULT |
+ NET_EVENT_WIFI_DISCONNECT_RESULT);
+ net_mgmt_add_event_callback(&eswifi_mgmt_cb_obj);
+
+ eswifi_unlock(eswifi);
+
+ return 0;
+}
+
+int network_connect_async(void)
+{
+ wifi_params = config_get_wifi_params();
+
+ int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, eswifi_by_iface_idx(0)->iface, &wifi_params,
+ sizeof(struct wifi_connect_req_params));
+
+ if (ret > 0 || ret == -EALREADY || ret == -EINPROGRESS) {
+ ret = 0;
+ }
+ if (ret) {
+ LOG_ERR("Failed to configure Wi-Fi");
+ }
+ return ret;
+}
+
+void network_disconnect(void)
+{
+ struct k_work_sync sync;
+
+ k_work_cancel_delayable_sync(&eswifi_reconnect_work, &sync);
+ k_work_cancel_delayable_sync(&eswifi_keepalive_work, &sync);
+
+ net_mgmt(NET_REQUEST_WIFI_DISCONNECT, eswifi_by_iface_idx(0)->iface, NULL, 0);
+}
diff --git a/demo/src/network/network_internal.h b/demo/src/network/network_internal.h
new file mode 100644
index 0000000..4401110
--- /dev/null
+++ b/demo/src/network/network_internal.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2020-2022 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
+
+extern struct k_mutex network_internal_connect_mutex;
+extern struct k_condvar network_internal_connect_condvar;
+
+int network_internal_platform_initialize(void);
+
+#if defined(CONFIG_NET_NATIVE_IPV4) || defined(CONFIG_NET_NATIVE_IPV6)
+bool network_internal_has_ip_address(struct net_if *iface);
+#endif // defined(CONFIG_NET_NATIVE_IPV4) || defined(CONFIG_NET_NATIVE_IPV6)
+
+void network_internal_connection_state_changed(void);
diff --git a/demo/src/network/network_nrf91.c b/demo/src/network/network_nrf91.c
new file mode 100644
index 0000000..a416f3a
--- /dev/null
+++ b/demo/src/network/network_nrf91.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020-2022 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 "network.h"
+#include "network_internal.h"
+
+#include "../common.h"
+#include "../config.h"
+#include "../gps.h"
+#include "../utils.h"
+
+LOG_MODULE_REGISTER(network_nrf91);
+
+static volatile atomic_int lte_nw_reg_status; // enum lte_lc_nw_reg_status
+static volatile atomic_int lte_mode; // enum lte_lc_lte_mode
+
+static void lte_evt_handler(const struct lte_lc_evt *const evt)
+{
+ if (evt) {
+ if (evt->type == LTE_LC_EVT_NW_REG_STATUS) {
+ atomic_store(<e_nw_reg_status, (int)evt->nw_reg_status);
+ } else if (evt->type == LTE_LC_EVT_LTE_MODE_UPDATE) {
+ atomic_store(<e_mode, (int)evt->lte_mode);
+ }
+ }
+ network_internal_connection_state_changed();
+}
+
+int network_internal_platform_initialize(void)
+{
+ int ret = lte_lc_init();
+
+ if (!ret) {
+ lte_lc_register_handler(lte_evt_handler);
+ }
+
+ return ret;
+}
+
+int network_connect_async(void)
+{
+ int ret = 0;
+
+ // Note: this is supposed to be handled by lte_lc_connect_async(),
+ // but there is a nasty bug in in_progress flag handling
+ if (!network_is_connected()) {
+ ret = lte_lc_connect_async(lte_evt_handler);
+ }
+
+ if (ret > 0 || ret == -EALREADY || ret == -EINPROGRESS) {
+ ret = 0;
+ }
+ if (ret) {
+ LOG_ERR("LTE link could not be established.");
+ }
+ return ret;
+}
+
+enum network_bearer_t network_current_bearer(void)
+{
+#ifdef CONFIG_ANJAY_CLIENT_GPS_NRF
+ if (atomic_load(&gps_prio_mode)) {
+ return NETWORK_BEARER_LIMIT;
+ }
+#endif // CONFIG_ANJAY_CLIENT_GPS_NRF
+
+ if (atomic_load(<e_mode) == LTE_LC_LTE_MODE_NONE) {
+ return NETWORK_BEARER_LIMIT;
+ }
+
+ int status = atomic_load(<e_nw_reg_status);
+
+ if (status == LTE_LC_NW_REG_REGISTERED_HOME || status == LTE_LC_NW_REG_REGISTERED_ROAMING) {
+ return NETWORK_BEARER_CELLULAR;
+ } else {
+ return NETWORK_BEARER_LIMIT;
+ }
+}
+
+void network_disconnect(void)
+{
+ int ret = lte_lc_offline();
+
+ if (ret) {
+ LOG_WRN("LTE link could not be disconnected: %d", ret);
+ }
+}
diff --git a/demo/src/network/network_openthread.c b/demo/src/network/network_openthread.c
new file mode 100644
index 0000000..47dd2c2
--- /dev/null
+++ b/demo/src/network/network_openthread.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020-2022 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 "network.h"
+#include "network_internal.h"
+
+#include "../utils.h"
+#include "../default_config.h"
+
+/*
+ * Due to the lack of a function declaration in the openthread.h
+ * file, it has been added here.
+ */
+int openthread_stop(struct openthread_context *ot_context);
+
+LOG_MODULE_REGISTER(network_openthread);
+
+static void ot_state_changed(uint32_t flags, void *context)
+{
+ network_internal_connection_state_changed();
+}
+
+int network_internal_platform_initialize(void)
+{
+ openthread_set_state_changed_cb(ot_state_changed);
+ return 0;
+}
+
+int network_connect_async(void)
+{
+ int ret = openthread_start(openthread_get_default_context());
+
+ if (ret) {
+ LOG_WRN("Failed to start Openthread.");
+ }
+
+ return ret;
+}
+
+enum network_bearer_t network_current_bearer(void)
+{
+ struct openthread_context *ctx = openthread_get_default_context();
+
+ bool connected =
+ otThreadGetDeviceRole(ctx->instance) >= OT_DEVICE_ROLE_CHILD &&
+ net_if_ipv6_get_global_addr(NET_ADDR_PREFERRED, &(struct net_if *){ ctx->iface });
+
+ return connected ? NETWORK_BEARER_OPENTHREAD : NETWORK_BEARER_LIMIT;
+}
+
+void network_disconnect(void)
+{
+ openthread_stop(openthread_get_default_context());
+}
diff --git a/demo/src/network/network_wifi.c b/demo/src/network/network_wifi.c
new file mode 100644
index 0000000..3126914
--- /dev/null
+++ b/demo/src/network/network_wifi.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020-2022 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 "network.h"
+#include "network_internal.h"
+
+#include "../config.h"
+#include "../utils.h"
+
+LOG_MODULE_REGISTER(network_wifi);
+
+int network_internal_platform_initialize(void)
+{
+ return 0;
+}
+
+int network_connect_async(void)
+{
+ struct wifi_connect_req_params wifi_params = config_get_wifi_params();
+
+ int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, net_if_get_default(), &wifi_params,
+ sizeof(struct wifi_connect_req_params));
+
+ if (ret > 0 || ret == -EALREADY || ret == -EINPROGRESS) {
+ ret = 0;
+ }
+ if (ret) {
+ LOG_ERR("Failed to configure Wi-Fi");
+ }
+ return ret;
+}
+
+void network_disconnect(void)
+{
+ net_mgmt(NET_REQUEST_WIFI_DISCONNECT, net_if_get_default(), NULL, 0);
+}
diff --git a/demo/src/nrf_lc_info.c b/demo/src/nrf_lc_info.c
index 86f7174..8c847b5 100644
--- a/demo/src/nrf_lc_info.c
+++ b/demo/src/nrf_lc_info.c
@@ -17,10 +17,11 @@
#include
#include
-#include
+#include
+#include
+
#include
#include
-#include
#include "common.h"
#include "nrf_lc_info.h"
@@ -98,7 +99,8 @@ static void lte_lc_evt_handler(const struct lte_lc_evt *const evt)
static void periodic_search_work_handler(struct k_work *work)
{
// TODO: consider using other search types, which work on nRF9160's with FW 1.3.1 and up
- int err = lte_lc_neighbor_cell_measurement(LTE_LC_NEIGHBOR_SEARCH_TYPE_DEFAULT);
+ int err = lte_lc_neighbor_cell_measurement(&(struct lte_lc_ncellmeas_params){
+ .search_type = LTE_LC_NEIGHBOR_SEARCH_TYPE_DEFAULT });
if (err) {
LOG_ERR("Can't search for neighbor cells, error: %d", err);
diff --git a/demo/src/nrf_lc_info.h b/demo/src/nrf_lc_info.h
index 627bb8e..92f2e75 100644
--- a/demo/src/nrf_lc_info.h
+++ b/demo/src/nrf_lc_info.h
@@ -19,7 +19,8 @@
#include
#include
-#include
+#include
+
#include
#include
diff --git a/demo/src/objects/basic_sensors.c b/demo/src/objects/basic_sensors.c
index 4c20d8e..b015ba7 100644
--- a/demo/src/objects/basic_sensors.c
+++ b/demo/src/objects/basic_sensors.c
@@ -17,9 +17,9 @@
#include
#include
-#include
-#include
-#include
+#include
+#include
+#include
#include "objects.h"
diff --git a/demo/src/objects/buzzer.c b/demo/src/objects/buzzer.c
index a823880..b211cb7 100644
--- a/demo/src/objects/buzzer.c
+++ b/demo/src/objects/buzzer.c
@@ -22,9 +22,9 @@
#include
#include
-#include
-#include
-#include
+#include
+#include
+#include
#include "../utils.h"
#include "objects.h"
diff --git a/demo/src/objects/conn_mon.c b/demo/src/objects/conn_mon.c
index 0bd834b..e13dfce 100644
--- a/demo/src/objects/conn_mon.c
+++ b/demo/src/objects/conn_mon.c
@@ -23,9 +23,9 @@
#include
#include
-#include
-#include
-#include
+#include
+#include
+#include
#include "objects.h"
#include "../nrf_lc_info.h"
diff --git a/demo/src/objects/device.c b/demo/src/objects/device.c
index b46d33f..f43f602 100644
--- a/demo/src/objects/device.c
+++ b/demo/src/objects/device.c
@@ -21,8 +21,8 @@
#include
#include
-#include
-#include
+#include
+#include
#include "../default_config.h"
#include "../utils.h"
diff --git a/demo/src/objects/ecid.c b/demo/src/objects/ecid.c
index ba0a312..be6718b 100644
--- a/demo/src/objects/ecid.c
+++ b/demo/src/objects/ecid.c
@@ -21,7 +21,8 @@
#include
#include
-#include
+#include
+
#include
#include "objects.h"
diff --git a/demo/src/objects/led_color_light.c b/demo/src/objects/led_color_light.c
index 0978244..4fcde8f 100644
--- a/demo/src/objects/led_color_light.c
+++ b/demo/src/objects/led_color_light.c
@@ -23,9 +23,9 @@
#include
#include
-#include
-#include
-#include
+#include
+#include
+#include
#include "objects.h"
diff --git a/demo/src/objects/loc_assist.c b/demo/src/objects/loc_assist.c
index 2eabc79..b4b589b 100644
--- a/demo/src/objects/loc_assist.c
+++ b/demo/src/objects/loc_assist.c
@@ -15,7 +15,8 @@
*/
#include
#include
-#include
+
+#include
#include
#include
diff --git a/demo/src/objects/objects.h b/demo/src/objects/objects.h
index 1b1f431..3e85f64 100644
--- a/demo/src/objects/objects.h
+++ b/demo/src/objects/objects.h
@@ -17,9 +17,10 @@
#pragma once
#include
-#include
-#include
-#include
+
+#include
+#include
+#include
#ifdef CONFIG_ANJAY_CLIENT_NRF_LC_INFO
#include "../nrf_lc_info.h"
@@ -58,7 +59,8 @@ void three_axis_sensors_update(anjay_t *anjay);
#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(0) || PUSH_BUTTON_AVAILABLE(1) || PUSH_BUTTON_AVAILABLE(2) || \
+ PUSH_BUTTON_AVAILABLE(3))
int push_button_object_install(anjay_t *anjay);
#define SWITCH_NODE(idx) DT_ALIAS(switch_##idx)
diff --git a/demo/src/objects/push_button.c b/demo/src/objects/push_button.c
index ff27494..20ff796 100644
--- a/demo/src/objects/push_button.c
+++ b/demo/src/objects/push_button.c
@@ -23,9 +23,10 @@
#include
#include
-#include
-#include
-#include
+#include
+#include
+#include
+#include
#include "objects.h"
@@ -57,6 +58,9 @@ static struct push_button_instance_glue button_glue[] = {
#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)
};
#define BUTTON_CHANGE_WORKS_NUM 256
@@ -97,8 +101,7 @@ static void button_state_changed(const struct device *dev, struct gpio_callback
work->reserved = true;
work->anjay = glue->anjay;
work->state = (bool)gpio_pin_get(dev, glue->gpio_pin);
- work->iid = (anjay_iid_t)(((size_t)(glue - button_glue)) /
- sizeof(struct push_button_instance_glue));
+ work->iid = (anjay_iid_t)((size_t)(glue - button_glue));
k_work_init(&work->work, button_change_state_handler);
diff --git a/demo/src/objects/switch.c b/demo/src/objects/switch.c
index 632a023..c3316c9 100644
--- a/demo/src/objects/switch.c
+++ b/demo/src/objects/switch.c
@@ -22,8 +22,8 @@
#include
#include
-#include
-#include
+#include
+#include
#include "objects.h"
diff --git a/demo/src/objects/three_axis_sensors.c b/demo/src/objects/three_axis_sensors.c
index 2e16404..49db2e1 100644
--- a/demo/src/objects/three_axis_sensors.c
+++ b/demo/src/objects/three_axis_sensors.c
@@ -17,9 +17,9 @@
#include
#include
-#include
-#include
-#include
+#include
+#include
+#include
#include "objects.h"
diff --git a/demo/src/persistence.c b/demo/src/persistence.c
index 31dbb50..e6c4c06 100644
--- a/demo/src/persistence.c
+++ b/demo/src/persistence.c
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#include
-#include
-#include
+#include
+#include
+#include
#include
#include
diff --git a/demo/src/status_led.c b/demo/src/status_led.c
index 43e39b8..13bc588 100644
--- a/demo/src/status_led.c
+++ b/demo/src/status_led.c
@@ -16,9 +16,9 @@
#include "status_led.h"
-#include
-#include
-#include
+#include
+#include
+#include
LOG_MODULE_REGISTER(status_led);
diff --git a/demo/src/status_led.h b/demo/src/status_led.h
index b73f2c8..07cf99c 100644
--- a/demo/src/status_led.h
+++ b/demo/src/status_led.h
@@ -16,7 +16,7 @@
#pragma once
-#include
+#include