Skip to content

Commit 99dbf84

Browse files
committed
config: Report config to shadow and unify CDDL schema
Unify desired/reported objects into single shadow-object in CDDL schema. Add automatic reporting of current config (update_interval, sample_interval) to cloud shadow upon connection. Simplify build system to generate both encode and decode functions from unified schema definition. Update documentation and tests accordingly. Signed-off-by: Simen S. Røstad <simen.rostad@nordicsemi.no>
1 parent a6d73a5 commit 99dbf84

File tree

14 files changed

+351
-102
lines changed

14 files changed

+351
-102
lines changed

app/src/Kconfig.main

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,27 @@
55
#
66
menu "App"
77

8-
config APP_SENSOR_SAMPLING_INTERVAL_SECONDS
9-
int "Sensor sampling interval"
10-
default 600
8+
config APP_BUFFER_MODE_SAMPLING_INTERVAL_SECONDS
9+
int "Sampling interval in buffer mode"
10+
default 150
1111
help
12-
Interval for triggering sensor data sampling and location search.
13-
In passthrough mode, this timer also triggers cloud shadow and FOTA status polling and data sending.
14-
This is the primary sampling interval for the application.
12+
Interval that determines how often sensor and location data is
13+
collected in buffer mode.
1514

1615
config APP_CLOUD_SYNC_INTERVAL_SECONDS
1716
int "Cloud synchronization interval"
18-
default 3600
17+
default 600
1918
help
20-
Interval for cloud synchronization activities including polling and data sending.
21-
This timer triggers cloud shadow and FOTA status polling, and data sending from storage.
22-
The data sending only applies if the storage module is in buffer mode.
23-
This interval is typically longer than the sensor sampling interval.
19+
Interval in seconds that controls how frequently the application
20+
performs cloud-related tasks such as:
21+
* Polling the device shadow
22+
* Checking for FOTA updates
23+
* Transmitting buffered data to the cloud
24+
In buffer mode, this option is typically set to a longer interval
25+
than CONFIG_APP_BUFFER_MODE_SAMPLING_INTERVAL_SECONDS to reduce
26+
power consumption while allowing data aggregation.
27+
In passthrough mode, this option controls both data sampling and
28+
cloud synchronization frequency.
2429

2530
config APP_REQUEST_NETWORK_QUALITY
2631
bool "Request network quality"

app/src/cbor/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/cbor_helper.c)
88
target_include_directories(app PRIVATE .)
99

10-
# generate encoder code using zcbor
10+
# generate encoder and decoder code using zcbor
1111
set(zcbor_command
1212
zcbor code # Invoke code generation
1313
--cddl ${CMAKE_CURRENT_SOURCE_DIR}/device_shadow.cddl
1414
--decode # Generate decoding functions
15+
--encode # Generate encoding functions
1516
--short-names # Attempt to make generated symbol names shorter (at the risk of collision)
16-
-t desired-object # Create a public API for decoding the "desired-object" type from the cddl file
17+
-t shadow-object # Create a public API for encoding and decoding the "shadow-object" type from the cddl file
1718
--output-cmake device_shadow.cmake # The generated cmake file will be placed here
1819
)
1920
execute_process(COMMAND ${zcbor_command}

app/src/cbor/cbor_helper.c

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,96 @@
55
*/
66
#include <errno.h>
77
#include <string.h>
8+
#include <zcbor_encode.h>
89

910
#include "cbor_helper.h"
10-
#include "device_shadow_decode.h"
11+
#include "device_shadow_types.h"
1112

1213
#include <zephyr/logging/log.h>
1314

1415
LOG_MODULE_REGISTER(cbor_helper, CONFIG_APP_LOG_LEVEL);
1516

1617
int get_parameters_from_cbor_response(const uint8_t *cbor,
1718
size_t len,
18-
uint32_t *interval_sec,
19+
uint32_t *update_interval,
20+
uint32_t *sample_interval,
1921
uint32_t *command_type)
2022
{
2123
int err;
22-
struct desired_object desired_object = { 0 };
24+
struct shadow_object shadow = { 0 };
2325
size_t decode_len = len;
2426

25-
if (!cbor || !interval_sec || !command_type || len == 0) {
27+
if (!cbor || !update_interval || !sample_interval || !command_type || len == 0) {
2628
return -EINVAL;
2729
}
2830

29-
err = cbor_decode_desired_object(cbor, decode_len, &desired_object, &decode_len);
31+
err = cbor_decode_shadow_object(cbor, decode_len, &shadow, &decode_len);
3032
if (err) {
31-
LOG_ERR("cbor_decode_desired_object, error: %d", err);
33+
LOG_ERR("cbor_decode_shadow_object, error: %d", err);
3234
LOG_HEXDUMP_ERR(cbor, len, "Unexpected CBOR data");
3335
return -EFAULT;
3436
}
3537

36-
if (desired_object.config_present && desired_object.config.update_interval_present) {
37-
*interval_sec = desired_object.config.update_interval.update_interval;
38+
if (shadow.config_present && shadow.config.update_interval_present) {
39+
*update_interval = shadow.config.update_interval.update_interval;
3840
} else {
3941
LOG_DBG("Update interval parameter not present");
4042
}
4143

42-
if (desired_object.command_present) {
43-
*command_type = desired_object.command.type;
44+
if (shadow.config_present && shadow.config.sample_interval_present) {
45+
*sample_interval = shadow.config.sample_interval.sample_interval;
46+
} else {
47+
LOG_DBG("Sample interval parameter not present");
48+
}
49+
50+
if (shadow.command_present) {
51+
*command_type = shadow.command.type;
4452
/* ID entry not used */
4553
} else {
4654
LOG_DBG("Command parameter not present");
4755
}
4856

4957
return 0;
5058
}
59+
60+
int encode_config_reported_to_cbor(uint32_t update_interval,
61+
uint32_t sample_interval,
62+
uint8_t *buffer,
63+
size_t buffer_size,
64+
size_t *encoded_len)
65+
{
66+
int err;
67+
struct shadow_object shadow = { 0 };
68+
size_t encode_len;
69+
70+
if (!buffer || !encoded_len || buffer_size == 0) {
71+
return -EINVAL;
72+
}
73+
74+
/* Build shadow object with config section */
75+
shadow.config_present = true;
76+
77+
if (update_interval != UINT32_MAX) {
78+
shadow.config.update_interval_present = true;
79+
shadow.config.update_interval.update_interval = update_interval;
80+
}
81+
82+
if (sample_interval != UINT32_MAX) {
83+
shadow.config.sample_interval_present = true;
84+
shadow.config.sample_interval.sample_interval = sample_interval;
85+
}
86+
87+
/* Encode the shadow object to CBOR */
88+
err = cbor_encode_shadow_object(buffer, buffer_size, &shadow, &encode_len);
89+
if (err) {
90+
LOG_ERR("cbor_encode_shadow_object, error: %d", err);
91+
return (err == ZCBOR_ERR_NO_PAYLOAD) ? -ENOMEM : -EFAULT;
92+
}
93+
94+
*encoded_len = encode_len;
95+
96+
LOG_DBG("Encoded config: update_interval=%u, sample_interval=%u, len=%zu",
97+
update_interval, sample_interval, encode_len);
98+
99+
return 0;
100+
}

app/src/cbor/cbor_helper.h

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
/**
1313
* @brief Get the device shadow parameters from a CBOR buffer.
1414
*
15-
* @param[in] cbor The CBOR buffer.
16-
* @param[in] len The length of the CBOR buffer.
17-
* @param[out] interval_sec Update interval in seconds.
18-
* @param[out] command_type Cloud command type.
15+
* @param[in] cbor The CBOR buffer.
16+
* @param[in] len The length of the CBOR buffer.
17+
* @param[out] update_interval Update interval in seconds.
18+
* @param[out] sample_interval Sample interval in seconds.
19+
* @param[out] command_type Cloud command type.
1920
*
2021
* @returns 0 If the operation was successful.
2122
* Otherwise, a (negative) error code is returned.
@@ -24,5 +25,27 @@
2425
*/
2526
int get_parameters_from_cbor_response(const uint8_t *cbor,
2627
size_t len,
27-
uint32_t *interval_sec,
28+
uint32_t *update_interval,
29+
uint32_t *sample_interval,
2830
uint32_t *command_type);
31+
32+
/**
33+
* @brief Encode device configuration parameters to CBOR for shadow reporting.
34+
*
35+
* @param[in] update_interval Update interval in seconds.
36+
* @param[in] sample_interval Sample interval in seconds.
37+
* @param[out] buffer Buffer to store encoded CBOR data.
38+
* @param[in] buffer_size Size of the output buffer.
39+
* @param[out] encoded_len Length of encoded data.
40+
*
41+
* @returns 0 If the operation was successful.
42+
* Otherwise, a (negative) error code is returned.
43+
* @retval -EINVAL if parameters are invalid.
44+
* @retval -ENOMEM if buffer is too small.
45+
*
46+
*/
47+
int encode_config_reported_to_cbor(uint32_t update_interval,
48+
uint32_t sample_interval,
49+
uint8_t *buffer,
50+
size_t buffer_size,
51+
size_t *encoded_len);

app/src/cbor/device_shadow.cddl

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,44 @@
1-
; Device Shadow Desired Object Definition
2-
; ---------------------------------------
3-
; This section defines the structure of the "desired" object in the device shadow.
4-
; All sections are optional, but if present, they must appear in the specified order when decoding the CBOR buffer.
1+
; Device Shadow Object Definition
2+
; --------------------------------
3+
; This unified structure defines both "desired" (cloud-to-device) and "reported" (device-to-cloud)
4+
; objects in the device shadow. All sections are optional, but if present, they must appear in
5+
; the specified order when decoding the CBOR buffer.
56
;
67
; Sections:
7-
; 1. "config": Contains device configuration parameters.
8-
; - "update_interval": (optional) A 4-byte unsigned integer specifying the update interval.
8+
; 1. "config": Device configuration parameters.
9+
; - "update_interval": (optional) A 4-byte unsigned integer (in seconds) specifying how often the device
10+
; synchronizes with the cloud (polls shadow, checks for FOTA updates, sends buffered data).
11+
; Valid range: 1 to 4294967294 (excludes 0 and UINT32_MAX).
12+
; - "sample_interval": (optional) A 4-byte unsigned integer (in seconds) specifying how often the device
13+
; samples sensor data.
14+
; Valid range: 1 to 4294967294 (excludes 0 and UINT32_MAX).
15+
;
16+
; Storage mode behavior:
17+
; - Buffer mode: Activated when both "update_interval" and "sample_interval" are present.
18+
; Device samples at "sample_interval" and sends data at "update_interval".
19+
; - Passthrough mode: Activated when only "update_interval" is present ("sample_interval" absent).
20+
; Device samples and sends data at "update_interval".
921
; - Additional configuration parameters may be included as key-value pairs (tstr => any).
1022
;
1123
; 2. "command": (optional) An array specifying a command for the device to execute.
1224
; - Structure: [type, id]
1325
; - type: 4-byte unsigned integer indicating the command type.
26+
; Supported command types:
27+
; 1 = PROVISION: Triggers device provisioning sequence
28+
; 2 = REBOOT: Triggers device reboot
29+
; Valid range: 1 to 2.
1430
; - id: 4-byte unsigned integer indicating the command ID.
31+
; Used to track command execution. Increment for successive commands.
32+
; Valid range: 1 to 4294967294 (excludes 0 and UINT32_MAX).
1533
;
1634
; Any additional fields may be included as key-value pairs (tstr => any).
1735

18-
desired-object = {
36+
shadow-object = {
1937
? "config": {
20-
? "update_interval": uint .size 4,
38+
? "update_interval": uint .size 4 .ge 1 .le 4294967294,
39+
? "sample_interval": uint .size 4 .ge 1 .le 4294967294,
2140
* tstr => any
2241
},
23-
? "command": [type: uint .size 4, id: uint .size 4],
42+
? "command": [type: uint .size 4 .ge 1 .le 2, id: uint .size 4 .ge 1 .le 4294967294],
2443
* tstr => any
2544
}

0 commit comments

Comments
 (0)