Skip to content

Commit b98e3d2

Browse files
andreagilardonipennam
authored andcommitted
CBOR encoder and decoder implementation for Command protocol model
1 parent 20d24e4 commit b98e3d2

File tree

6 files changed

+666
-0
lines changed

6 files changed

+666
-0
lines changed

Diff for: src/cbor/CBOR.cpp

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
This file is part of the ArduinoIoTCloud library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
11+
/******************************************************************************
12+
* INCLUDE
13+
******************************************************************************/
14+
15+
#include "CBOR.h"
16+
17+
/******************************************************************************
18+
* FUNCTION DEFINITION
19+
******************************************************************************/
20+
21+
CommandId toCommandId(CBORCommandTag tag) {
22+
switch(tag) {
23+
case CBORCommandTag::CBOROtaBeginUp:
24+
return CommandId::OtaBeginUpId;
25+
case CBORCommandTag::CBORThingBeginCmd:
26+
return CommandId::ThingBeginCmdId;
27+
case CBORCommandTag::CBORLastValuesBeginCmd:
28+
return CommandId::LastValuesBeginCmdId;
29+
case CBORCommandTag::CBORDeviceBeginCmd:
30+
return CommandId::DeviceBeginCmdId;
31+
case CBORCommandTag::CBOROtaProgressCmdUp:
32+
return CommandId::OtaProgressCmdUpId;
33+
case CBORCommandTag::CBORTimezoneCommandUp:
34+
return CommandId::TimezoneCommandUpId;
35+
case CBORCommandTag::CBOROtaUpdateCmdDown:
36+
return CommandId::OtaUpdateCmdDownId;
37+
case CBORCommandTag::CBORThingUpdateCmd:
38+
return CommandId::ThingUpdateCmdId;
39+
case CBORCommandTag::CBORLastValuesUpdate:
40+
return CommandId::LastValuesUpdateCmdId;
41+
case CBORCommandTag::CBORTimezoneCommandDown:
42+
return CommandId::TimezoneCommandDownId;
43+
default:
44+
return CommandId::UnknownCmdId;
45+
}
46+
}
47+
48+
CBORCommandTag toCBORCommandTag(CommandId id) {
49+
switch(id) {
50+
case CommandId::OtaBeginUpId:
51+
return CBORCommandTag::CBOROtaBeginUp;
52+
case CommandId::ThingBeginCmdId:
53+
return CBORCommandTag::CBORThingBeginCmd;
54+
case CommandId::LastValuesBeginCmdId:
55+
return CBORCommandTag::CBORLastValuesBeginCmd;
56+
case CommandId::DeviceBeginCmdId:
57+
return CBORCommandTag::CBORDeviceBeginCmd;
58+
case CommandId::OtaProgressCmdUpId:
59+
return CBORCommandTag::CBOROtaProgressCmdUp;
60+
case CommandId::TimezoneCommandUpId:
61+
return CBORCommandTag::CBORTimezoneCommandUp;
62+
case CommandId::OtaUpdateCmdDownId:
63+
return CBORCommandTag::CBOROtaUpdateCmdDown;
64+
case CommandId::ThingUpdateCmdId:
65+
return CBORCommandTag::CBORThingUpdateCmd;
66+
case CommandId::LastValuesUpdateCmdId:
67+
return CBORCommandTag::CBORLastValuesUpdate;
68+
case CommandId::TimezoneCommandDownId:
69+
return CBORCommandTag::CBORTimezoneCommandDown;
70+
default:
71+
return CBORCommandTag::CBORUnknownCmdTag;
72+
}
73+
}

Diff for: src/cbor/CBOR.h

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
This file is part of the ArduinoIoTCloud library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
11+
#pragma once
12+
13+
/******************************************************************************
14+
* INCLUDE
15+
******************************************************************************/
16+
#include <message/Commands.h>
17+
18+
/******************************************************************************
19+
TYPEDEF
20+
******************************************************************************/
21+
22+
enum CBORCommandTag: uint64_t {
23+
// Commands UP
24+
CBOROtaBeginUp = 0x010000,
25+
CBORThingBeginCmd = 0x010300,
26+
CBORLastValuesBeginCmd = 0x010500,
27+
CBORDeviceBeginCmd = 0x010700,
28+
CBOROtaProgressCmdUp = 0x010200,
29+
CBORTimezoneCommandUp = 0x010800,
30+
31+
// Commands DOWN
32+
CBOROtaUpdateCmdDown = 0x010100,
33+
CBORThingUpdateCmd = 0x010400,
34+
CBORLastValuesUpdate = 0x010600,
35+
CBORTimezoneCommandDown = 0x010900,
36+
37+
// Unknown Command Tag https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
38+
CBORUnknownCmdTag16b = 0xffff, // invalid tag
39+
CBORUnknownCmdTag32b = 0xffffffff, // invalid tag
40+
CBORUnknownCmdTag64b = 0xffffffffffffffff, // invalid tag
41+
CBORUnknownCmdTag = CBORUnknownCmdTag32b
42+
};
43+
44+
/******************************************************************************
45+
* FUNCTION DECLARATION
46+
******************************************************************************/
47+
48+
CommandId toCommandId(CBORCommandTag tag);
49+
CBORCommandTag toCBORCommandTag(CommandId id);

Diff for: src/cbor/MessageDecoder.cpp

+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
This file is part of the ArduinoIoTCloud library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
11+
/******************************************************************************
12+
INCLUDE
13+
******************************************************************************/
14+
15+
#include <Arduino.h>
16+
17+
#undef max
18+
#undef min
19+
#include <algorithm>
20+
21+
#include "MessageDecoder.h"
22+
#include <AIoTC_Config.h>
23+
24+
/******************************************************************************
25+
PUBLIC MEMBER FUNCTIONS
26+
******************************************************************************/
27+
28+
Decoder::Status CBORMessageDecoder::decode(Message * message, uint8_t const * const payload, size_t& length)
29+
{
30+
CborValue main_iter, array_iter;
31+
CborTag tag;
32+
CborParser parser;
33+
34+
if (cbor_parser_init(payload, length, 0, &parser, &main_iter) != CborNoError)
35+
return Decoder::Status::Error;
36+
37+
if (main_iter.type != CborTagType)
38+
return Decoder::Status::Error;
39+
40+
if (cbor_value_get_tag(&main_iter, &tag) == CborNoError) {
41+
message->id = toCommandId(CBORCommandTag(tag));
42+
}
43+
44+
if (cbor_value_advance(&main_iter) != CborNoError) {
45+
return Decoder::Status::Error;
46+
}
47+
48+
ArrayParserState current_state = ArrayParserState::EnterArray,
49+
next_state = ArrayParserState::Error;
50+
51+
while (current_state != ArrayParserState::Complete) {
52+
switch (current_state) {
53+
case ArrayParserState::EnterArray : next_state = handle_EnterArray(&main_iter, &array_iter); break;
54+
case ArrayParserState::ParseParam : next_state = handle_Param(&array_iter, message); break;
55+
case ArrayParserState::LeaveArray : next_state = handle_LeaveArray(&main_iter, &array_iter); break;
56+
case ArrayParserState::Complete : return Decoder::Status::Complete;
57+
case ArrayParserState::MessageNotSupported : return Decoder::Status::Error;
58+
case ArrayParserState::Error : return Decoder::Status::Error;
59+
}
60+
61+
current_state = next_state;
62+
}
63+
64+
return Decoder::Status::Complete;
65+
}
66+
67+
/******************************************************************************
68+
PRIVATE MEMBER FUNCTIONS
69+
******************************************************************************/
70+
71+
bool copyCBORStringToArray(CborValue * param, char * dest, size_t dest_size) {
72+
if (cbor_value_is_text_string(param)) {
73+
// NOTE: keep in mind that _cbor_value_copy_string tries to put a \0 at the end of the string
74+
if(_cbor_value_copy_string(param, dest, &dest_size, NULL) == CborNoError) {
75+
return true;
76+
}
77+
}
78+
79+
return false;
80+
}
81+
82+
// FIXME dest_size should be also returned, the copied byte array can have a different size from the starting one
83+
// for the time being we need this on SHA256 only
84+
bool copyCBORByteToArray(CborValue * param, uint8_t * dest, size_t dest_size) {
85+
if (cbor_value_is_byte_string(param)) {
86+
// NOTE: keep in mind that _cbor_value_copy_string tries to put a \0 at the end of the string
87+
if(_cbor_value_copy_string(param, dest, &dest_size, NULL) == CborNoError) {
88+
return true;
89+
}
90+
}
91+
92+
return false;
93+
}
94+
95+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::handle_EnterArray(CborValue * main_iter, CborValue * array_iter) {
96+
ArrayParserState next_state = ArrayParserState::Error;
97+
if (cbor_value_get_type(main_iter) == CborArrayType) {
98+
if (cbor_value_enter_container(main_iter, array_iter) == CborNoError) {
99+
next_state = ArrayParserState::ParseParam;
100+
}
101+
}
102+
103+
return next_state;
104+
}
105+
106+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::handle_LeaveArray(CborValue * main_iter, CborValue * array_iter) {
107+
// Advance to the next parameter (the last one in the array)
108+
if (cbor_value_advance(array_iter) == CborNoError) {
109+
// Leave the array
110+
if (cbor_value_leave_container(main_iter, array_iter) == CborNoError) {
111+
return ArrayParserState::Complete;
112+
}
113+
}
114+
115+
return ArrayParserState::Error;
116+
}
117+
118+
/******************************************************************************
119+
MESSAGE DECODE FUNCTIONS
120+
******************************************************************************/
121+
122+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeThingUpdateCmd(CborValue * param, Message * message) {
123+
ThingUpdateCmd * thingCommand = (ThingUpdateCmd *) message;
124+
125+
// Message is composed of a single parameter, a string (thing_id)
126+
if (!copyCBORStringToArray(param, thingCommand->params.thing_id, sizeof(thingCommand->params.thing_id))) {
127+
return ArrayParserState::Error;
128+
}
129+
130+
return ArrayParserState::LeaveArray;
131+
}
132+
133+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeTimezoneCommandDown(CborValue * param, Message * message) {
134+
TimezoneCommandDown * setTz = (TimezoneCommandDown *) message;
135+
136+
// Message is composed of 2 parameters, offset 32-bit signed integer and until 32-bit unsigned integer
137+
// Get offset
138+
if (cbor_value_is_integer(param)) {
139+
int64_t val = 0;
140+
if (cbor_value_get_int64(param, &val) == CborNoError) {
141+
setTz->params.offset = static_cast<int32_t>(val);
142+
}
143+
}
144+
145+
// Next
146+
if (cbor_value_advance(param) != CborNoError) {
147+
return ArrayParserState::Error;
148+
}
149+
150+
// Get until
151+
if (cbor_value_is_integer(param)) {
152+
uint64_t val = 0;
153+
if (cbor_value_get_uint64(param, &val) == CborNoError) {
154+
setTz->params.until = static_cast<uint32_t>(val);
155+
}
156+
}
157+
158+
return ArrayParserState::LeaveArray;
159+
}
160+
161+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeLastValuesUpdateCmd(CborValue * param, Message * message) {
162+
LastValuesUpdateCmd * setLv = (LastValuesUpdateCmd *) message;
163+
164+
// Message is composed by a single parameter, a variable length byte array.
165+
if (cbor_value_is_byte_string(param)) {
166+
// Cortex M0 is not able to assign a value to pointed memory that is not 32bit aligned
167+
// we use a support variable to cope with that
168+
size_t s;
169+
if (cbor_value_dup_byte_string(param, &setLv->params.last_values, &s, NULL) != CborNoError) {
170+
return ArrayParserState::Error;
171+
}
172+
173+
setLv->params.length = s;
174+
}
175+
176+
return ArrayParserState::LeaveArray;
177+
}
178+
179+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeOtaUpdateCmdDown(CborValue * param, Message * message) {
180+
CborError error = CborNoError;
181+
OtaUpdateCmdDown * ota = (OtaUpdateCmdDown *) message;
182+
183+
// Message is composed 4 parameters: id, url, initialSha, finalSha
184+
if (!copyCBORByteToArray(param, ota->params.id, sizeof(ota->params.id))) {
185+
return ArrayParserState::Error;
186+
}
187+
188+
error = cbor_value_advance(param);
189+
190+
if ((error != CborNoError) || !copyCBORStringToArray(param, ota->params.url, sizeof(ota->params.url))) {
191+
return ArrayParserState::Error;
192+
}
193+
194+
error = cbor_value_advance(param);
195+
196+
if ((error != CborNoError) || !copyCBORByteToArray(param, ota->params.initialSha256, sizeof(ota->params.initialSha256))) {
197+
return ArrayParserState::Error;
198+
}
199+
200+
error = cbor_value_advance(param);
201+
202+
if ((error != CborNoError) || !copyCBORByteToArray(param, ota->params.finalSha256, sizeof(ota->params.finalSha256))) {
203+
return ArrayParserState::Error;
204+
}
205+
206+
return ArrayParserState::LeaveArray;
207+
}
208+
209+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::handle_Param(CborValue * param, Message * message) {
210+
211+
switch (message->id)
212+
{
213+
case CommandId::ThingUpdateCmdId:
214+
return CBORMessageDecoder::decodeThingUpdateCmd(param, message);
215+
216+
case CommandId::TimezoneCommandDownId:
217+
return CBORMessageDecoder::decodeTimezoneCommandDown(param, message);
218+
219+
case CommandId::LastValuesUpdateCmdId:
220+
return CBORMessageDecoder::decodeLastValuesUpdateCmd(param, message);
221+
222+
case CommandId::OtaUpdateCmdDownId:
223+
return CBORMessageDecoder::decodeOtaUpdateCmdDown(param, message);
224+
225+
default:
226+
return ArrayParserState::MessageNotSupported;
227+
}
228+
229+
return ArrayParserState::LeaveArray;
230+
}

0 commit comments

Comments
 (0)