Skip to content

Commit 6fb4bee

Browse files
andreagilardonipennam
authored andcommitted
implementing CBOR encoder and decoder for Command protocol model
1 parent 17ba869 commit 6fb4bee

File tree

6 files changed

+683
-0
lines changed

6 files changed

+683
-0
lines changed

src/cbor/CBOR.cpp

Lines changed: 73 additions & 0 deletions
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+
}

src/cbor/CBOR.h

Lines changed: 49 additions & 0 deletions
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);

src/cbor/MessageDecoder.cpp

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
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+
return false;
74+
}
75+
76+
// NOTE: keep in mind that _cbor_value_copy_string tries to put a \0 at the end of the string
77+
if(_cbor_value_copy_string(param, dest, &dest_size, NULL) != CborNoError) {
78+
return false;
79+
}
80+
81+
return true;
82+
}
83+
84+
// FIXME dest_size should be also returned, the copied byte array can have a different size from the starting one
85+
// for the time being we need this on SHA256 only
86+
bool copyCBORByteToArray(CborValue * param, uint8_t * dest, size_t dest_size) {
87+
if (!cbor_value_is_byte_string(param)) {
88+
return false;
89+
}
90+
91+
// NOTE: keep in mind that _cbor_value_copy_string tries to put a \0 at the end of the string
92+
if(_cbor_value_copy_string(param, dest, &dest_size, NULL) != CborNoError) {
93+
94+
return false;
95+
}
96+
97+
return true;
98+
}
99+
100+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::handle_EnterArray(CborValue * main_iter, CborValue * array_iter) {
101+
ArrayParserState next_state = ArrayParserState::Error;
102+
if (cbor_value_get_type(main_iter) == CborArrayType) {
103+
if (cbor_value_enter_container(main_iter, array_iter) == CborNoError) {
104+
next_state = ArrayParserState::ParseParam;
105+
}
106+
}
107+
108+
return next_state;
109+
}
110+
111+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::handle_LeaveArray(CborValue * main_iter, CborValue * array_iter) {
112+
// Advance to the next parameter (the last one in the array)
113+
if (cbor_value_advance(array_iter) != CborNoError) {
114+
return ArrayParserState::Error;
115+
}
116+
// Leave the array
117+
if (cbor_value_leave_container(main_iter, array_iter) != CborNoError) {
118+
return ArrayParserState::Error;
119+
}
120+
return ArrayParserState::Complete;
121+
}
122+
123+
/******************************************************************************
124+
MESSAGE DECODE FUNCTIONS
125+
******************************************************************************/
126+
127+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeThingUpdateCmd(CborValue * param, Message * message) {
128+
ThingUpdateCmd * thingCommand = (ThingUpdateCmd *) message;
129+
130+
// Message is composed of a single parameter, a string (thing_id)
131+
if (!copyCBORStringToArray(param, thingCommand->params.thing_id, sizeof(thingCommand->params.thing_id))) {
132+
return ArrayParserState::Error;
133+
}
134+
135+
return ArrayParserState::LeaveArray;
136+
}
137+
138+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeTimezoneCommandDown(CborValue * param, Message * message) {
139+
TimezoneCommandDown * setTz = (TimezoneCommandDown *) message;
140+
141+
// Message is composed of 2 parameters, offset 32-bit signed integer and until 32-bit unsigned integer
142+
// Get offset
143+
if (cbor_value_is_integer(param)) {
144+
int64_t val = 0;
145+
if (cbor_value_get_int64(param, &val) == CborNoError) {
146+
setTz->params.offset = static_cast<int32_t>(val);
147+
}
148+
}
149+
150+
// Next
151+
if (cbor_value_advance(param) != CborNoError) {
152+
return ArrayParserState::Error;
153+
}
154+
155+
// Get until
156+
if (cbor_value_is_integer(param)) {
157+
uint64_t val = 0;
158+
if (cbor_value_get_uint64(param, &val) == CborNoError) {
159+
setTz->params.until = static_cast<uint32_t>(val);
160+
}
161+
}
162+
163+
return ArrayParserState::LeaveArray;
164+
}
165+
166+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeLastValuesUpdateCmd(CborValue * param, Message * message) {
167+
LastValuesUpdateCmd * setLv = (LastValuesUpdateCmd *) message;
168+
169+
// Message is composed by a single parameter, a variable length byte array.
170+
if (cbor_value_is_byte_string(param)) {
171+
// Cortex M0 is not able to assign a value to pointed memory that is not 32bit aligned
172+
// we use a support variable to cope with that
173+
size_t s;
174+
if (cbor_value_dup_byte_string(param, &setLv->params.last_values, &s, NULL) != CborNoError) {
175+
return ArrayParserState::Error;
176+
}
177+
178+
setLv->params.length = s;
179+
}
180+
181+
return ArrayParserState::LeaveArray;
182+
}
183+
184+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeOtaUpdateCmdDown(CborValue * param, Message * message) {
185+
OtaUpdateCmdDown * ota = (OtaUpdateCmdDown *) message;
186+
187+
// Message is composed 4 parameters: id, url, initialSha, finalSha
188+
if (!copyCBORByteToArray(param, ota->params.id, sizeof(ota->params.id))) {
189+
return ArrayParserState::Error;
190+
}
191+
192+
if (cbor_value_advance(param) != CborNoError) {
193+
return ArrayParserState::Error;
194+
}
195+
196+
if (!copyCBORStringToArray(param, ota->params.url, sizeof(ota->params.url))) {
197+
return ArrayParserState::Error;
198+
}
199+
200+
if (cbor_value_advance(param) != CborNoError) {
201+
return ArrayParserState::Error;
202+
}
203+
204+
if (!copyCBORByteToArray(param, ota->params.initialSha256, sizeof(ota->params.initialSha256))) {
205+
return ArrayParserState::Error;
206+
}
207+
208+
if (cbor_value_advance(param) != CborNoError) {
209+
return ArrayParserState::Error;
210+
}
211+
212+
if (!copyCBORByteToArray(param, ota->params.finalSha256, sizeof(ota->params.finalSha256))) {
213+
return ArrayParserState::Error;
214+
}
215+
216+
return ArrayParserState::LeaveArray;
217+
}
218+
219+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::handle_Param(CborValue * param, Message * message) {
220+
221+
switch (message->id)
222+
{
223+
case CommandId::ThingUpdateCmdId:
224+
return CBORMessageDecoder::decodeThingUpdateCmd(param, message);
225+
226+
case CommandId::TimezoneCommandDownId:
227+
return CBORMessageDecoder::decodeTimezoneCommandDown(param, message);
228+
229+
case CommandId::LastValuesUpdateCmdId:
230+
return CBORMessageDecoder::decodeLastValuesUpdateCmd(param, message);
231+
232+
case CommandId::OtaUpdateCmdDownId:
233+
return CBORMessageDecoder::decodeOtaUpdateCmdDown(param, message);
234+
235+
default:
236+
return ArrayParserState::MessageNotSupported;
237+
}
238+
239+
return ArrayParserState::LeaveArray;
240+
}

0 commit comments

Comments
 (0)