Skip to content

Commit dd285f9

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 9cf7310 commit dd285f9

File tree

24 files changed

+1299
-424
lines changed

24 files changed

+1299
-424
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 in seconds 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+
synchronizes with cloud, performing 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: 102 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,131 @@
55
*/
66
#include <errno.h>
77
#include <string.h>
8+
#include <zcbor_encode.h>
89

910
#include "cbor_helper.h"
11+
#include "device_shadow_types.h"
1012
#include "device_shadow_decode.h"
13+
#include "device_shadow_encode.h"
1114

1215
#include <zephyr/logging/log.h>
1316

1417
LOG_MODULE_REGISTER(cbor_helper, CONFIG_APP_LOG_LEVEL);
1518

16-
int get_parameters_from_cbor_response(const uint8_t *cbor,
17-
size_t len,
18-
uint32_t *interval_sec,
19-
uint32_t *command_type)
19+
int decode_shadow_parameters_from_cbor(const uint8_t *cbor,
20+
size_t len,
21+
struct config_params *config,
22+
uint32_t *command_type,
23+
uint32_t *command_id)
2024
{
2125
int err;
22-
struct desired_object desired_object = { 0 };
26+
struct shadow_object shadow = { 0 };
2327
size_t decode_len = len;
2428

25-
if (!cbor || !interval_sec || !command_type || len == 0) {
29+
if (!cbor || !config || !command_type || len == 0) {
30+
LOG_ERR("Invalid input");
2631
return -EINVAL;
2732
}
2833

29-
err = cbor_decode_desired_object(cbor, decode_len, &desired_object, &decode_len);
34+
err = cbor_decode_shadow_object(cbor, decode_len, &shadow, &decode_len);
3035
if (err) {
31-
LOG_ERR("cbor_decode_desired_object, error: %d", err);
36+
LOG_ERR("cbor_decode_shadow_object, error: %d", err);
3237
LOG_HEXDUMP_ERR(cbor, len, "Unexpected CBOR data");
3338
return -EFAULT;
3439
}
3540

36-
if (desired_object.config_present && desired_object.config.update_interval_present) {
37-
*interval_sec = desired_object.config.update_interval.update_interval;
38-
} else {
39-
LOG_DBG("Update interval parameter not present");
41+
if (shadow.config_present) {
42+
if (shadow.config.update_interval_present) {
43+
config->update_interval = shadow.config.update_interval.update_interval;
44+
LOG_DBG("Configuration: Decoded update_interval = %d seconds",
45+
config->update_interval);
46+
}
47+
48+
if (shadow.config.sample_interval_present) {
49+
config->sample_interval = shadow.config.sample_interval.sample_interval;
50+
LOG_DBG("Configuration: Decoded sample_interval = %d seconds",
51+
config->sample_interval);
52+
}
53+
54+
if (shadow.config.buffer_mode_present) {
55+
config->buffer_mode = shadow.config.buffer_mode.buffer_mode;
56+
config->buffer_mode_valid = true;
57+
LOG_DBG("Configuration: Decoded buffer_mode = %s",
58+
config->buffer_mode ? "enabled" : "disabled");
59+
}
60+
}
61+
62+
if (shadow.command_present) {
63+
*command_type = shadow.command.type;
64+
*command_id = shadow.command.id;
65+
66+
LOG_DBG("Command parameter present: type=%u, id=%u",
67+
*command_type, *command_id);
68+
69+
}
70+
71+
return 0;
72+
}
73+
74+
int encode_shadow_parameters_to_cbor(const struct config_params *config,
75+
uint32_t command_type,
76+
uint32_t command_id,
77+
uint8_t *buffer,
78+
size_t buffer_size,
79+
size_t *encoded_len)
80+
{
81+
int err;
82+
struct shadow_object shadow = { 0 };
83+
size_t encode_len;
84+
85+
if (!config || !buffer || !encoded_len || buffer_size == 0) {
86+
return -EINVAL;
87+
}
88+
89+
/* Build shadow object with config section */
90+
if (config->update_interval > 0) {
91+
shadow.config_present = true;
92+
shadow.config.update_interval_present = true;
93+
shadow.config.update_interval.update_interval = config->update_interval;
4094
}
4195

42-
if (desired_object.command_present) {
43-
*command_type = desired_object.command.type;
44-
/* ID entry not used */
45-
} else {
46-
LOG_DBG("Command parameter not present");
96+
if (config->sample_interval > 0) {
97+
shadow.config_present = true;
98+
shadow.config.sample_interval_present = true;
99+
shadow.config.sample_interval.sample_interval = config->sample_interval;
47100
}
48101

102+
if (config->buffer_mode_valid) {
103+
shadow.config_present = true;
104+
shadow.config.buffer_mode_present = true;
105+
shadow.config.buffer_mode.buffer_mode = config->buffer_mode;
106+
}
107+
108+
/* Build shadow object with command section */
109+
if (command_type > 0) {
110+
shadow.command_present = true;
111+
shadow.command.type = command_type;
112+
shadow.command.id = command_id;
113+
}
114+
115+
/* Encode the shadow object to CBOR */
116+
err = cbor_encode_shadow_object(buffer, buffer_size, &shadow, &encode_len);
117+
if (err) {
118+
LOG_ERR("cbor_encode_shadow_object, error: %d", err);
119+
return (err == ZCBOR_ERR_NO_PAYLOAD) ? -ENOMEM : -EFAULT;
120+
}
121+
122+
*encoded_len = encode_len;
123+
124+
LOG_DBG("Encoded config: update_interval=%u, sample_interval=%u, "
125+
"buffer_mode=%s, len=%zu; Encoded command: type=%u, id=%u, len=%zu",
126+
config->update_interval,
127+
config->sample_interval,
128+
config->buffer_mode ? "enabled" : "disabled",
129+
encode_len,
130+
command_type,
131+
command_id,
132+
encode_len);
133+
49134
return 0;
50135
}

app/src/cbor/cbor_helper.h

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,59 @@
77
#include <zephyr/types.h>
88

99
#define CLOUD_COMMAND_TYPE_PROVISION 1
10-
#define CLOUD_COMMAND_TYPE_REBOOT 2
10+
11+
/** Device configuration parameters. */
12+
struct config_params {
13+
/** Update interval in seconds. */
14+
uint32_t update_interval;
15+
16+
/** Sample interval in seconds. */
17+
uint32_t sample_interval;
18+
19+
/** Buffer mode flag */
20+
bool buffer_mode;
21+
22+
/** Buffer mode validity flag */
23+
bool buffer_mode_valid;
24+
};
1125

1226
/**
13-
* @brief Get the device shadow parameters from a CBOR buffer.
27+
* @brief Decode shadow parameters from CBOR buffer.
28+
* If a parameter is not present, it is set to UINT32_MAX.
1429
*
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.
30+
* @param[in] cbor CBOR buffer.
31+
* @param[in] len CBOR buffer length.
32+
* @param[out] config Decoded configuration parameters.
33+
* @param[out] command_type Decoded command type.
34+
* @param[out] command_id Decoded command ID.
35+
*
36+
* @retval 0 Success.
37+
* @retval -EINVAL Invalid parameters.
38+
* @retval -EFAULT Invalid CBOR data.
39+
*/
40+
int decode_shadow_parameters_from_cbor(const uint8_t *cbor,
41+
size_t len,
42+
struct config_params *config,
43+
uint32_t *command_type,
44+
uint32_t *command_id);
45+
46+
/**
47+
* @brief Encode shadow parameters to CBOR buffer.
1948
*
20-
* @returns 0 If the operation was successful.
21-
* Otherwise, a (negative) error code is returned.
22-
* @retval -EFAULT if the CBOR buffer is invalid.
49+
* @param[in] config Configuration parameters.
50+
* @param[in] command_type Command type.
51+
* @param[in] command_id Command ID.
52+
* @param[out] buffer Output buffer for encoded CBOR.
53+
* @param[in] buffer_size Output buffer size.
54+
* @param[out] encoded_len Length of encoded data.
2355
*
56+
* @retval 0 Success.
57+
* @retval -EINVAL Invalid parameters.
58+
* @retval -ENOMEM Buffer too small.
2459
*/
25-
int get_parameters_from_cbor_response(const uint8_t *cbor,
26-
size_t len,
27-
uint32_t *interval_sec,
28-
uint32_t *command_type);
60+
int encode_shadow_parameters_to_cbor(const struct config_params *config,
61+
uint32_t command_type,
62+
uint32_t command_id,
63+
uint8_t *buffer,
64+
size_t buffer_size,
65+
size_t *encoded_len);

app/src/cbor/device_shadow.cddl

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,43 @@
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:
10+
; - In passthrough mode: How often the device samples sensors and sends data to cloud.
11+
; - In buffer mode: How often the device sends buffered data to cloud, polls shadow, and checks for FOTA updates.
12+
; Valid range: 1 to 4294967295. Values outside this range are decoded but rejected by application.
13+
; - "sample_interval": (optional) A 4-byte unsigned integer (in seconds) specifying how often the device
14+
; samples sensor data in buffer mode. This parameter is ignored in passthrough mode.
15+
; Valid range: 1 to 4294967295. Values outside this range are decoded but rejected by application.
16+
; - "buffer_mode": (optional) A boolean value that controls the storage mode.
17+
; true = Buffer mode: Device samples at "sample_interval" and sends data at "update_interval".
18+
; false = Passthrough mode: Device samples and sends data at "update_interval" (sample_interval is ignored).
19+
; When not present, the device uses the default passthrough mode.
920
; - Additional configuration parameters may be included as key-value pairs (tstr => any).
1021
;
1122
; 2. "command": (optional) An array specifying a command for the device to execute.
1223
; - Structure: [type, id]
1324
; - type: 4-byte unsigned integer indicating the command type.
25+
; Supported command types:
26+
; 1 = PROVISION: Triggers device provisioning sequence
27+
; Valid range: 1 to 1.
1428
; - id: 4-byte unsigned integer indicating the command ID.
29+
; Used to track command execution. Increment for successive commands.
30+
; Valid range: 1 to 4294967295 (excludes 0).
1531
;
1632
; Any additional fields may be included as key-value pairs (tstr => any).
1733

18-
desired-object = {
34+
shadow-object = {
1935
? "config": {
2036
? "update_interval": uint .size 4,
37+
? "sample_interval": uint .size 4,
38+
? "buffer_mode": bool,
2139
* tstr => any
2240
},
23-
? "command": [type: uint .size 4, id: uint .size 4],
41+
? "command": [type: uint .size 4 .ge 1 .le 1, id: uint .size 4 .ge 1 .le 4294967295],
2442
* tstr => any
2543
}

0 commit comments

Comments
 (0)