diff --git a/.codespellrc b/.codespellrc index 4cdd0de..025901d 100644 --- a/.codespellrc +++ b/.codespellrc @@ -3,7 +3,7 @@ [codespell] # In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: ignore-words-list = , -skip = ./.git,./.licenses,__pycache__,node_modules,./go.mod,./go.sum,./package-lock.json,./poetry.lock,./yarn.lock,./src/tinycbor +skip = ./.git,./.licenses,__pycache__,node_modules,./go.mod,./go.sum,./package-lock.json,./poetry.lock,./yarn.lock,./src/cbor/tinycbor builtin = clear,informal,en-GB_to_en-US check-filenames = check-hidden = diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 6e4d438..85bb2af 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -26,6 +26,9 @@ jobs: - examples/crc32 - examples/crc16 - examples/sha256 + - examples/customCborDecoder + - examples/customCborEncoder + - examples/timedBlink SKETCHES_REPORTS_PATH: sketches-reports strategy: diff --git a/README.md b/README.md index 80f3080..296ea8c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,14 @@ -# Arduino Cloud Utils +# 🛠️ Arduino Cloud Utils -This library contains a set of functionalities that may be shared among cloud related projects, in particular functionalities for ArduinoIoTCloud \ No newline at end of file +[![Arduino Lint](https://github.com/arduino-libraries/Arduino_CloudUtils/actions/workflows/check-arduino.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_CloudUtils/actions/workflows/check-arduino.yml) [![Compile Examples](https://github.com/arduino-libraries/Arduino_CloudUtils/actions/workflows/compile-examples.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_CloudUtils/actions/workflows/compile-examples.yml) [![Spell Check](https://github.com/arduino-libraries/Arduino_CloudUtils/actions/workflows/spell-check.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_CloudUtils/actions/workflows/spell-check.yml) [![Sync Labels](https://github.com/arduino-libraries/Arduino_CloudUtils/actions/workflows/sync-labels.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_CloudUtils/actions/workflows/sync-labels.yml) + +This library contains a set of functionalities that may be shared among cloud related projects, in particular functionalities for ArduinoIoTCloud + +# 🔧 Utilities +* ✉️ [CRC16](examples/crc16) +* 📧 [CRC32](examples/crc32) +* 🔒 [CBOR decoder](examples/customCborDecoder) +* 🔓 [CBOR encoder](examples/customCborEncoder) +* ⬆️ [LZSS decoder](examples/lzssDecoder) +* 💨 [SHA256](examples/sha256) +* ⏲️ [Timed action](examples/timedBlink) diff --git a/examples/customCborDecoder/customCborDecoder.ino b/examples/customCborDecoder/customCborDecoder.ino new file mode 100644 index 0000000..7d0931b --- /dev/null +++ b/examples/customCborDecoder/customCborDecoder.ino @@ -0,0 +1,79 @@ +/* + This file is part of the Arduino_CloudUtils library. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include + +enum : MessageId { + CBORTestMessageId = 0x0123, +}; + +enum : CBORTag { + CBORTestMessageTag = 0x0321, +}; + + +struct CBORTestMessage { + Message m; + char parameter[20]; +}; + +class CustomMessageDecoder: public CBORMessageDecoderInterface { +public: + CustomMessageDecoder() + : CBORMessageDecoderInterface(CBORTestMessageTag, CBORTestMessageId) {} + +protected: + MessageDecoder::Status decode(CborValue* iter, Message *msg) override { + CBORTestMessage* test = (CBORTestMessage*) msg; + size_t dest_size = 20; + + if(!cbor_value_is_text_string(iter)) { + return MessageDecoder::Status::Error; + } + + // NOTE: keep in mind that _cbor_value_copy_string tries to put a \0 at the end of the string + if(_cbor_value_copy_string(iter, test->parameter, &dest_size, NULL) != CborNoError) { + return MessageDecoder::Status::Error; + } + + return MessageDecoder::Status::Complete; + } +} customMessageDecoder; + +void setup() { + Serial.begin(9600); + while(!Serial); + + CBORMessageDecoder decoder; + + CBORTestMessage expected_result { + CBORTestMessageId, + "abcdef", + }; + + uint8_t buffer[] { + 0xD9, 0x03, 0x21, 0x81, 0x66, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + }; + size_t buffer_len = sizeof(buffer); + + CBORTestMessage cmd_res; + MessageDecoder::Status res = decoder.decode((Message*)&cmd_res, buffer, buffer_len); + + if(res == MessageDecoder::Status::Complete && + cmd_res.m.id == expected_result.m.id && + strcmp(cmd_res.parameter, expected_result.parameter) == 0) { + + Serial.println("Decode operation completed with success"); + } else { + Serial.println("Decode operation failed"); + } +} + +void loop() {} diff --git a/examples/customCborEncoder/customCborEncoder.ino b/examples/customCborEncoder/customCborEncoder.ino new file mode 100644 index 0000000..0ab9fc8 --- /dev/null +++ b/examples/customCborEncoder/customCborEncoder.ino @@ -0,0 +1,77 @@ +/* + This file is part of the Arduino_CloudUtils library. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include + +enum : MessageId { + CBORTestMessageId = 0x0123, +}; + +enum : CBORTag { + CBORTestMessageTag = 0x0321, +}; + + +struct CBORTestMessage { + Message m; + char parameter[20]; +}; + +class CustomMessageEncoder: public CBORMessageEncoderInterface { +public: + CustomMessageEncoder() + : CBORMessageEncoderInterface(CBORTestMessageTag, CBORTestMessageId) {} + +protected: + MessageEncoder::Status encode(CborEncoder* encoder, Message *msg) override { + CBORTestMessage * testMessage = (CBORTestMessage *) msg; + CborEncoder array_encoder; + + if(cbor_encoder_create_array(encoder, &array_encoder, 1) != CborNoError) { + return MessageEncoder::Status::Error; + } + + if(cbor_encode_text_stringz(&array_encoder, testMessage->parameter) != CborNoError) { + return MessageEncoder::Status::Error; + } + + if(cbor_encoder_close_container(encoder, &array_encoder) != CborNoError) { + return MessageEncoder::Status::Error; + } + return MessageEncoder::Status::Complete; + } +} customMessageEncoder; + +void setup() { + Serial.begin(9600); + while(!Serial); + + CBORMessageEncoder encoder; + uint8_t buffer[100]; // shared buffer for encoding + uint8_t expected[] = {0xD9, 0x03, 0x21, 0x81, 0x66, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; + const size_t buf_len = sizeof(buffer); + + CBORTestMessage cmd { + CBORTestMessageId, + "abcdef", + }; + size_t res_len=buf_len; + MessageEncoder::Status res = encoder.encode((Message*)&cmd, buffer, res_len); + + if(res == MessageEncoder::Status::Complete && + memcmp(buffer, expected, res_len) == 0) { + + Serial.println("Encode operation completed with success"); + } else { + Serial.println("Encode operation failed"); + } +} + +void loop() {} diff --git a/examples/timedBlink/timedBlink.ino b/examples/timedBlink/timedBlink.ino new file mode 100644 index 0000000..686e5ed --- /dev/null +++ b/examples/timedBlink/timedBlink.ino @@ -0,0 +1,29 @@ +/* + This file is part of the Arduino_CloudUtils library. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include + +#if !defined(LED_BUILTIN) && !defined(ARDUINO_NANO_ESP32) +static int const LED_BUILTIN = 2; +#endif + +TimedAttempt blink(500, 1000); + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); +} + +void loop() { + if(blink.isExpired()) { + blink.retry(); + digitalWrite(LED_BUILTIN, blink.getRetryCount() % 2); + } + +} diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index 2fb0dd0..b604c53 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -20,6 +20,7 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) include_directories(../../src) +include_directories(src/arduino) set(TEST_SRCS src/lzss/test_decoder.cpp @@ -27,6 +28,9 @@ set(TEST_SRCS src/crc16/test_crc16.cpp src/sha256/test_sha256.cpp src/hex/test_hex.cpp + src/cbor/test_cbor_encoder.cpp + src/cbor/test_cbor_decoder.cpp + src/time/test_TimedAttempt.cpp ) set(TEST_DUT_SRCS @@ -34,6 +38,24 @@ set(TEST_DUT_SRCS ../../src/crc/crc16.cpp ../../src/sha256/sha2.c ../../src/hex/chex.h + ../../src/cbor/MessageDecoder.cpp + ../../src/cbor/MessageEncoder.cpp + ../../src/cbor/tinycbor + ../../src/cbor/tinycbor/src/cborencoder.c + ../../src/cbor/tinycbor/src/cborencoder_close_container_checked.c + ../../src/cbor/tinycbor/src/cborerrorstrings.c + ../../src/cbor/tinycbor/src/cborparser.c + ../../src/cbor/tinycbor/src/cborparser_dup_string.c + ../../src/cbor/tinycbor/src/cborpretty.c + ../../src/cbor/tinycbor/src/cborpretty_stdio.c + ../../src/cbor/tinycbor/src/cbortojson.c + ../../src/cbor/tinycbor/src/cborvalidation.c + ../../src/cbor/tinycbor/src/open_memstream.c + ../../src/time/TimedAttempt.cpp +) + +set(TEST_STUB_SRCS + src/arduino/Arduino.cpp ) ########################################################################## @@ -45,7 +67,7 @@ add_compile_options(-Wno-cast-function-type) set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} "--coverage") set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "--coverage -Wno-deprecated-copy") -add_executable( ${TEST_TARGET} ${TEST_SRCS} ${TEST_DUT_SRCS} ) +add_executable( ${TEST_TARGET} ${TEST_SRCS} ${TEST_DUT_SRCS} ${TEST_STUB_SRCS}) target_compile_definitions( ${TEST_TARGET} PUBLIC SOURCE_DIR="${CMAKE_SOURCE_DIR}" ) target_link_libraries( ${TEST_TARGET} Catch2WithMain ) diff --git a/extras/test/src/arduino/Arduino.cpp b/extras/test/src/arduino/Arduino.cpp new file mode 100644 index 0000000..cf277de --- /dev/null +++ b/extras/test/src/arduino/Arduino.cpp @@ -0,0 +1,21 @@ +/* + This file is part of the Arduino_CloudUtils library. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include + +static unsigned long current_millis = 0; + +void set_millis(unsigned long const millis) { + current_millis = millis; +} + +unsigned long millis() { + return current_millis; +} diff --git a/extras/test/src/arduino/Arduino.h b/extras/test/src/arduino/Arduino.h new file mode 100644 index 0000000..91ef626 --- /dev/null +++ b/extras/test/src/arduino/Arduino.h @@ -0,0 +1,18 @@ +/* + This file is part of the Arduino_CloudUtils library. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#ifndef min + #define min(a,b) ((a)<(b)?(a):(b)) +#endif + +void set_millis(unsigned long const millis); +unsigned long millis(); diff --git a/extras/test/src/cbor/test_cbor_decoder.cpp b/extras/test/src/cbor/test_cbor_decoder.cpp new file mode 100644 index 0000000..09e24f4 --- /dev/null +++ b/extras/test/src/cbor/test_cbor_decoder.cpp @@ -0,0 +1,85 @@ +/* + This file is part of the Arduino_CloudUtils library. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ +#include +#include +#include +#include + +#include + +enum : MessageId { + CBORTestMessageId = 0x0123, +}; + +enum : CBORTag { + CBORTestMessageTag = 0x0321, +}; + +struct CBORTestMessage { + Message m; + char parameter[20]; +}; + +class CustomMessageDecoder: public CBORMessageDecoderInterface { +public: + CustomMessageDecoder() + : CBORMessageDecoderInterface(CBORTestMessageTag, CBORTestMessageId) {} + +protected: + MessageDecoder::Status decode(CborValue* iter, Message *msg) override { + CBORTestMessage* test = (CBORTestMessage*) msg; + size_t dest_size = 20; + + if(!cbor_value_is_text_string(iter)) { + return MessageDecoder::Status::Error; + } + + // NOTE: keep in mind that _cbor_value_copy_string tries to put a \0 at the end of the string + if(_cbor_value_copy_string(iter, test->parameter, &dest_size, NULL) != CborNoError) { + return MessageDecoder::Status::Error; + } + + return MessageDecoder::Status::Complete; + } +} customMessageDecoder; + +SCENARIO( "A custom decoder is defined", "[cbor][decode]" ) { + CBORMessageDecoder decoder; + GIVEN( "A buffer containing a cbor encoded message" ) { + CBORTestMessage expected_result { + CBORTestMessageId, + "abcdef", + }; + + uint8_t buffer[] { + 0xD9, 0x03, 0x21, 0x81, 0x66, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + }; + size_t buffer_len = sizeof(buffer); + + CBORTestMessage cmd_res; + + MessageDecoder::Status res = decoder.decode((Message*)&cmd_res, buffer, buffer_len); + + THEN( "Message decode result is Complete" ) { + REQUIRE(res == MessageDecoder::Status::Complete); + } + + THEN( "the decode result matches the expectations" ) { + REQUIRE(buffer_len == sizeof(buffer)); + + REQUIRE(expected_result.m.id == cmd_res.m.id); + + std::string parameter_expected(expected_result.parameter); + std::string parameter_result(cmd_res.parameter); + + REQUIRE_THAT(parameter_result, Catch::Matchers::Equals(parameter_expected)); + } + } +} diff --git a/extras/test/src/cbor/test_cbor_encoder.cpp b/extras/test/src/cbor/test_cbor_encoder.cpp new file mode 100644 index 0000000..8c2d899 --- /dev/null +++ b/extras/test/src/cbor/test_cbor_encoder.cpp @@ -0,0 +1,79 @@ +/* + This file is part of the Arduino_CloudUtils library. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ +#include +#include +#include +#include + +enum : MessageId { + CBORTestMessageId = 0x0123, +}; + +enum : CBORTag { + CBORTestMessageTag = 0x0321, +}; + + +struct CBORTestMessage { + Message m; + char parameter[20]; +}; + +class CustomMessageEncoder: public CBORMessageEncoderInterface { +public: + CustomMessageEncoder() + : CBORMessageEncoderInterface(CBORTestMessageTag, CBORTestMessageId) {} + +protected: + MessageEncoder::Status encode(CborEncoder* encoder, Message *msg) override { + CBORTestMessage * testMessage = (CBORTestMessage *) msg; + CborEncoder array_encoder; + + if(cbor_encoder_create_array(encoder, &array_encoder, 1) != CborNoError) { + return MessageEncoder::Status::Error; + } + + if(cbor_encode_text_stringz(&array_encoder, testMessage->parameter) != CborNoError) { + return MessageEncoder::Status::Error; + } + + if(cbor_encoder_close_container(encoder, &array_encoder) != CborNoError) { + return MessageEncoder::Status::Error; + } + return MessageEncoder::Status::Complete; + } +} customMessageEncoder; + +SCENARIO( "A custom encoder is defined", "[cbor][encode]" ) { + CBORMessageEncoder encoder; + uint8_t buffer[100]; // shared buffer for encoding + const size_t buf_len = sizeof(buffer); + + GIVEN( "A Message with an id that the global encoder is able to encode" ) { + CBORTestMessage cmd { + CBORTestMessageId, + "abcdef", + }; + size_t res_len=buf_len; + MessageEncoder::Status res = encoder.encode((Message*)&cmd, buffer, res_len); + + THEN( "Message encode result is Complete" ) { + REQUIRE(res == MessageEncoder::Status::Complete); + } + + THEN( "the encode result matches the expectations" ) { + std::vector res(buffer, buffer+res_len); + + REQUIRE_THAT(res, Catch::Matchers::Equals(std::vector{ + 0xD9, 0x03, 0x21, 0x81, 0x66, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + })); + } + } +} diff --git a/extras/test/src/sha256/test_sha256.cpp b/extras/test/src/sha256/test_sha256.cpp index 216d732..b80dec5 100644 --- a/extras/test/src/sha256/test_sha256.cpp +++ b/extras/test/src/sha256/test_sha256.cpp @@ -19,7 +19,7 @@ #ifdef TEST_VECTORS_LONG static void test_sha256_long_message(uint8_t *digest) { - sha256_ctx ctx; + acu_sha256_ctx ctx; uint8_t message[1000]; int i; @@ -36,7 +36,7 @@ static void test_sha256_long_message(uint8_t *digest) static void test_sha256_message4(uint8_t *digest) { /* Message of 929271 bytes */ - sha256_ctx ctx; + acu_sha256_ctx ctx; uint8_t message[1000]; int i; diff --git a/extras/test/src/time/test_TimedAttempt.cpp b/extras/test/src/time/test_TimedAttempt.cpp new file mode 100644 index 0000000..8009492 --- /dev/null +++ b/extras/test/src/time/test_TimedAttempt.cpp @@ -0,0 +1,173 @@ +/* + This file is part of the Arduino_CloudUtils library. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include + +#include