Skip to content

Commit 5970405

Browse files
committed
modules: main: Add CDDL CBOR encoding of device shadow
Add missing CDDL CBOR decoding of incoming data. Used to decode update interval set in cloud. Signed-off-by: Simen S. Røstad <simen.rostad@nordicsemi.no>
1 parent 6b9e39c commit 5970405

File tree

7 files changed

+102
-71
lines changed

7 files changed

+102
-71
lines changed

app/src/cbor/CMakeLists.txt

+28
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,31 @@
66

77
target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/cbor_helper.c)
88
target_include_directories(app PRIVATE .)
9+
10+
# generate encoder code using zcbor
11+
set(zcbor_command
12+
zcbor code # Invoke code generation
13+
--cddl ${CMAKE_CURRENT_SOURCE_DIR}/device_shadow.cddl
14+
--decode # Generate decoding functions
15+
--short-names # Attempt to make generated symbol names shorter (at the risk of collision)
16+
-t config-object # Create a public API for decoding the "config-object" type from the cddl file
17+
--output-cmake device_shadow.cmake # The generated cmake file will be placed here
18+
)
19+
execute_process(COMMAND ${zcbor_command}
20+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
21+
COMMAND_ERROR_IS_FATAL ANY)
22+
23+
# Include the cmake file generated by zcbor. It adds the
24+
# generated code and the necessary zcbor C code files.
25+
include(${CMAKE_CURRENT_BINARY_DIR}/device_shadow.cmake)
26+
27+
# Ensure that the cmake reconfiguration is triggerred everytime the cddl file changes.
28+
# This ensures that the codec generation is triggered.
29+
set_property(
30+
DIRECTORY
31+
PROPERTY
32+
CMAKE_CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/device_shadow.cddl
33+
)
34+
35+
zephyr_link_libraries(device_shadow)
36+
target_link_libraries(device_shadow PRIVATE zephyr_interface)

app/src/cbor/cbor_helper.c

+13-5
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,23 @@
33
*
44
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
55
*/
6-
7-
#include <zephyr/logging/log.h>
8-
9-
LOG_MODULE_DECLARE(main, CONFIG_APP_LOG_LEVEL);
6+
#include <errno.h>
7+
#include "device_shadow_decode.h"
108

119
int get_update_interval_from_cbor_response(const uint8_t *cbor,
1210
size_t len,
1311
uint64_t *interval_sec)
1412
{
15-
*interval_sec = 1800;
13+
struct config_object object = { 0 };
14+
size_t not_used;
15+
16+
int err = cbor_decode_config_object(cbor, len, &object, &not_used);
17+
18+
if (err) {
19+
return -EFAULT;
20+
}
21+
22+
*interval_sec = object.update_interval;
23+
1624
return 0;
1725
}

app/src/cbor/cbor_helper.h

+12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@
66

77
#include <zephyr/types.h>
88

9+
/**
10+
* @brief Get the update interval from a CBOR buffer.
11+
*
12+
* @param[in] cbor The CBOR buffer.
13+
* @param[in] len The length of the CBOR buffer.
14+
* @param[out] interval_sec The update interval in seconds.
15+
*
16+
* @returns 0 If the operation was successful.
17+
* Otherwise, a (negative) error code is returned.
18+
* @retval -EFAULT if the CBOR buffer is invalid.
19+
*
20+
*/
921
int get_update_interval_from_cbor_response(const uint8_t *cbor,
1022
size_t len,
1123
uint64_t *interval_sec);

app/src/cbor/device_shadow.cddl

+4-52
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,5 @@
1-
; Define the basic structure of the JSON object
2-
3-
; The 'any' type is used in this schema to allow forward compatibility.
4-
; They are inserted into maps that may contain additional fields in the future.
5-
; This prevents cbor decoding from failing in the field when the cloud side has new fields.
6-
app-object = {
7-
? "nrfcloud_mqtt_topic_prefix": tstr,
8-
? "pairing": pairing-type,
9-
? "lwm2m": lwm2m-map,
10-
* tstr => any
11-
}
12-
13-
; Define the pairing object with nested topics
14-
pairing-type = {
15-
"state": tstr,
16-
"topics": {
17-
"d2c": tstr,
18-
"c2d": tstr,
19-
* tstr => any
20-
},
21-
* any => any
22-
}
23-
24-
lwm2m-map = {
25-
? "14240:1.0": led,
26-
? "14301:1.0": config,
27-
* tstr => any
28-
}
29-
30-
led = {
31-
"0": led_inner_object,
32-
* tstr => any
33-
}
34-
35-
led_inner_object = {
36-
? "0": int .size 4,
37-
? "1": int .size 4,
38-
? "2": int .size 4,
39-
"99": int .size 8,
40-
* tstr => any
41-
}
42-
43-
config = {
44-
"0": config_inner_object,
45-
* tstr => any
46-
}
47-
48-
config_inner_object = {
49-
? "0": int .size 8,
50-
? "1": bool,
51-
"99": int .size 8,
52-
* tstr => any
1+
config-object = {
2+
"config": {
3+
"update_interval": uint .size 8
4+
}
535
}

app/src/main.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ static void triggering_run(void *o)
460460
msg.response.buffer_data_len,
461461
&state_object->interval_sec);
462462
if (err) {
463-
LOG_ERR("json_parse, error: %d", err);
463+
LOG_ERR("get_update_interval_from_cbor_response, error: %d", err);
464464
SEND_FATAL_ERROR();
465465
return;
466466
}

tests/module/main/CMakeLists.txt

+31-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ project(app_module_test)
1111

1212
test_runner_generate(src/main.c)
1313

14-
set(ASSET_TRACKER_TEMPLATE_DIR ../../..)
14+
set(ASSET_TRACKER_TEMPLATE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
1515

1616
target_sources(app
1717
PRIVATE
@@ -67,3 +67,33 @@ target_compile_definitions(app PRIVATE
6767
-DZEPHYR_INCLUDE_SYS_REBOOT_H_
6868
-DCONFIG_APP_REQUEST_NETWORK_QUALITY=1
6969
)
70+
71+
# CONSIDER: REUSING THE CODEC GENERATION LOGIC FROM THE MAIN APP
72+
73+
# generate encoder code using zcbor
74+
set(zcbor_command
75+
zcbor code # Invoke code generation
76+
--cddl ${ASSET_TRACKER_TEMPLATE_DIR}/app/src/cbor/device_shadow.cddl
77+
--decode # Generate decoding functions
78+
--short-names # Attempt to make generated symbol names shorter (at the risk of collision)
79+
-t config-object # Create a public API for decoding the "config-object" type from the cddl file
80+
--output-cmake device_shadow.cmake # The generated cmake file will be placed here
81+
)
82+
execute_process(COMMAND ${zcbor_command}
83+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
84+
COMMAND_ERROR_IS_FATAL ANY)
85+
86+
# Include the cmake file generated by zcbor. It adds the
87+
# generated code and the necessary zcbor C code files.
88+
include(${CMAKE_CURRENT_BINARY_DIR}/device_shadow.cmake)
89+
90+
# Ensure that the cmake reconfiguration is triggerred everytime the cddl file changes.
91+
# This ensures that the codec generation is triggered.
92+
set_property(
93+
DIRECTORY
94+
PROPERTY
95+
CMAKE_CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/device_shadow.cddl
96+
)
97+
98+
zephyr_link_libraries(device_shadow)
99+
target_link_libraries(device_shadow PRIVATE zephyr_interface)

tests/module/main/src/main.c

+13-12
Original file line numberDiff line numberDiff line change
@@ -141,20 +141,25 @@ static void send_location_search_done(void)
141141
TEST_ASSERT_EQUAL(0, err);
142142
}
143143

144-
/*
145-
static void send_config(uint64_t interval)
144+
static void twelve_hour_interval_set(void)
146145
{
146+
// MISSING: set interval for 12 hours, or even different intervals.
147+
147148
const struct cloud_msg msg = {
148149
.type = CLOUD_SHADOW_RESPONSE,
149-
.response.buffer = "{\"update_interval\": 60}",
150-
.response.buffer_data_len = strlen("{\"update_interval\": 60}"),
150+
/* JSON equivalent string: "{"config":{"update_interval": 60 }}" */
151+
.response.buffer = {
152+
0xA1, 0x66, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0xA1,
153+
0x6F, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5F, 0x69,
154+
0x6E, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6C, 0x18, 0x3C
155+
},
156+
.response.buffer_data_len = 27,
151157
};
152158

153-
int err = zbus_chan_pub(&CLOUD_CHAN, &shadow_response, K_SECONDS(1));
159+
int err = zbus_chan_pub(&CLOUD_CHAN, &msg, K_SECONDS(1));
154160

155161
TEST_ASSERT_EQUAL(0, err);
156162
}
157-
*/
158163

159164
static void send_cloud_disconnected(void)
160165
{
@@ -215,11 +220,10 @@ void test_button_press_on_disconnected(void)
215220
check_no_events(7200);
216221
}
217222

218-
/*
219223
void test_trigger_interval_change_in_connected(void)
220224
{
221225
send_cloud_connected_ready_to_send();
222-
send_config(HOUR_IN_SECONDS * 12);
226+
twelve_hour_interval_set();
223227

224228
for (int i = 0; i < 10; i++) {
225229
send_location_search_done();
@@ -231,13 +235,11 @@ void test_trigger_interval_change_in_connected(void)
231235
send_cloud_disconnected();
232236
check_no_events(WEEK_IN_SECONDS);
233237
}
234-
*/
235238

236-
/*
237239
void test_trigger_disconnect_and_connect_when_triggering(void)
238240
{
239241
send_cloud_connected_ready_to_send();
240-
send_config(HOUR_IN_SECONDS * 12);
242+
twelve_hour_interval_set();
241243

242244
for (int i = 0; i < 10; i++) {
243245

@@ -256,7 +258,6 @@ void test_trigger_disconnect_and_connect_when_triggering(void)
256258
send_cloud_disconnected();
257259
check_no_events(WEEK_IN_SECONDS);
258260
}
259-
*/
260261

261262
/* This is required to be added to each test. That is because unity's
262263
* main may return nonzero, while zephyr's main currently must

0 commit comments

Comments
 (0)