From 0ce691b83f813716dcceef3a31950f907c33c285 Mon Sep 17 00:00:00 2001 From: fabik111 Date: Tue, 27 May 2025 18:12:42 +0200 Subject: [PATCH 1/5] add cbor encoding for sending network configuration in cloud --- extras/test/CMakeLists.txt | 24 +- extras/test/include/Arduino.h | 1 + extras/test/include/IPAddress.h | 13 + extras/test/src/test_command_encode.cpp | 331 ++++++++++++++++++++++++ src/cbor/CBOR.h | 21 +- src/cbor/IoTCloudMessageEncoder.cpp | 187 +++++++++++++ src/cbor/IoTCloudMessageEncoder.h | 32 +++ src/message/Commands.h | 7 + 8 files changed, 605 insertions(+), 11 deletions(-) create mode 100644 extras/test/include/IPAddress.h diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index dfe322324..f9c13c8c0 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -20,9 +20,17 @@ FetchContent_Declare( GIT_TAG main ) +FetchContent_Declare( + connectionhandler + GIT_REPOSITORY https://github.com/arduino-libraries/Arduino_ConnectionHandler.git + GIT_TAG master +) + FetchContent_MakeAvailable(Catch2) FetchContent_MakeAvailable(cloudutils) + +FetchContent_MakeAvailable(connectionhandler) ########################################################################## include_directories(include) @@ -55,6 +63,18 @@ target_include_directories( ${cloudutils_SOURCE_DIR}/src/interfaces ) +add_library(connectionhandler INTERFACE) + +target_include_directories( + connectionhandler INTERFACE + ${connectionhandler_SOURCE_DIR}/src/ +) + +target_include_directories( + connectionhandler INTERFACE + ${connectionhandler_SOURCE_DIR}/src/connectionHandlerModels +) + ########################################################################## set(CMAKE_CXX_STANDARD 11) @@ -126,12 +146,13 @@ set(TEST_TARGET_SRCS ########################################################################## +add_compile_definitions(BOARD_HAS_LORA BOARD_HAS_CATM1_NBIOT BOARD_HAS_WIFI BOARD_HAS_ETHERNET BOARD_HAS_CELLULAR BOARD_HAS_NB BOARD_HAS_GSM) add_compile_definitions(HOST HAS_TCP) add_compile_options(-Wall -Wextra -Wpedantic -Werror) 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") +set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "--coverage -Wno-deprecated-copy -Wno-missing-field-initializers") ########################################################################## @@ -140,6 +161,7 @@ add_executable( ${TEST_TARGET_SRCS} ) +target_link_libraries( ${TEST_TARGET} connectionhandler) target_link_libraries( ${TEST_TARGET} cloudutils) target_link_libraries( ${TEST_TARGET} Catch2WithMain ) diff --git a/extras/test/include/Arduino.h b/extras/test/include/Arduino.h index 9e743f96f..dbdcc7380 100644 --- a/extras/test/include/Arduino.h +++ b/extras/test/include/Arduino.h @@ -10,6 +10,7 @@ ******************************************************************************/ #include +#include /****************************************************************************** DEFINES diff --git a/extras/test/include/IPAddress.h b/extras/test/include/IPAddress.h new file mode 100644 index 000000000..241796caa --- /dev/null +++ b/extras/test/include/IPAddress.h @@ -0,0 +1,13 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +#ifndef TEST_IPADDRESS_H_ +#define TEST_IPADDRESS_H_ + +enum IPType { + IPv4, + IPv6 +}; + +#endif diff --git a/extras/test/src/test_command_encode.cpp b/extras/test/src/test_command_encode.cpp index 3b7ae4425..af9975532 100644 --- a/extras/test/src/test_command_encode.cpp +++ b/extras/test/src/test_command_encode.cpp @@ -14,6 +14,9 @@ #include #include #include +#include +//#include + /****************************************************************************** TEST CODE @@ -648,4 +651,332 @@ SCENARIO("Test the encoding of command messages") { REQUIRE(err == MessageEncoder::Status::Error); } } + + WHEN("Encode the DeviceNetConfigCmdUp message with WiFi") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::WIFI; + String ssid = "SSID"; + strcpy(command.params.wifi.ssid, ssid.c_str()); + uint8_t buffer[512]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + uint8_t expected_result[] = { + 0xda, 0x00, 0x01, 0x11, 0x00, 0x82, 0x01, 0x64, 0x53, 0x53, 0x49, 0x44 + }; + + // Test the encoding is + // DA 00011100 # tag(73728) + // 82 # array(2) + // 01 # unsigned(1) + // 64 # text(4) + // 53534944 # "SSID" + THEN("The encoding is successful") { + REQUIRE(err == MessageEncoder::Status::Complete); + REQUIRE(bytes_encoded == sizeof(expected_result)); + REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message with LoraWan") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::LORA; + + String app_eui = "APPEUI"; + strcpy(command.params.lora.appeui, app_eui.c_str()); + String app_key = "APPKEY"; + strcpy(command.params.lora.appkey, app_key.c_str()); + uint8_t buffer[512]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + uint8_t expected_result[] = { + 0xda, 0x00, 0x01, 0x11, 0x00, 0x82, + 0x02, 0x66, 0x41, 0x50, 0x50, 0x45, 0x55, 0x49 + }; + + // Test the encoding is + // DA 00011100 # tag(73728) + // 82 # array(2) + // 02 # unsigned(2) + // 66 # text(6) + // 415050455549 # "APPEUI" + THEN("The encoding is successful") { + REQUIRE(err == MessageEncoder::Status::Complete); + REQUIRE(bytes_encoded == sizeof(expected_result)); + REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message with GSM") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::GSM; + String apn = "apn.arduino.cc"; + strcpy(command.params.gsm.apn, apn.c_str()); + String user = "username"; + strcpy(command.params.gsm.login, user.c_str()); + String password = "PASSWORD"; + strcpy(command.params.gsm.pass, password.c_str()); + uint8_t buffer[512]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + uint8_t expected_result[] = { + 0xda, 0x00, 0x01, 0x11, 0x00, 0x83, + 0x03, 0x6e, 0x61, 0x70, 0x6e, 0x2e, 0x61, 0x72, + 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x63, + 0x68, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, + 0x65 + }; + + // Test the encoding is + // DA 00011100 # tag(73728) + // 83 # array(3) + // 03 # unsigned(3) + // 6E # text(14) + // 61706E2E61726475696E6F2E6363 # "apn.arduino.cc" + // 68 # text(8) + // 757365726E616D65 # "username" + + THEN("The encoding is successful") { + REQUIRE(err == MessageEncoder::Status::Complete); + REQUIRE(bytes_encoded == sizeof(expected_result)); + REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message with NB-IoT") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::NB; + String apn = "apn.arduino.cc"; + strcpy(command.params.nb.apn, apn.c_str()); + String user = "username"; + strcpy(command.params.nb.login, user.c_str()); + String password = "PASSWORD"; + strcpy(command.params.nb.pass, password.c_str()); + uint8_t buffer[512]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + uint8_t expected_result[] = { + 0xda, 0x00, 0x01, 0x11, 0x00, 0x83, + 0x04, 0x6e, 0x61, 0x70, 0x6e, 0x2e, 0x61, 0x72, + 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x63, + 0x68, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, + 0x65 + }; + + // Test the encoding is + // DA 00011100 # tag(73728) + // 83 # array(3) + // 04 # unsigned(4) + // 6E # text(14) + // 61706E2E61726475696E6F2E6363 # "apn.arduino.cc" + // 68 # text(8) + // 757365726E616D65 # "username" + + THEN("The encoding is successful") { + REQUIRE(err == MessageEncoder::Status::Complete); + REQUIRE(bytes_encoded == sizeof(expected_result)); + REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message with CAT-M1") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::CATM1; + String apn = "apn.arduino.cc"; + strcpy(command.params.catm1.apn, apn.c_str()); + String user = "username"; + strcpy(command.params.catm1.login, user.c_str()); + String password = "PASSWORD"; + strcpy(command.params.catm1.pass, password.c_str()); + uint8_t buffer[512]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + uint8_t expected_result[] = { + 0xda, 0x00, 0x01, 0x11, 0x00, 0x83, + 0x05, 0x6e, 0x61, 0x70, 0x6e, 0x2e, 0x61, 0x72, + 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x63, + 0x68, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, + 0x65 + }; + + // Test the encoding is + // DA 00011100 # tag(73728) + // 83 # array(3) + // 05 # unsigned(5) + // 6E # text(14) + // 61706E2E61726475696E6F2E6363 # "apn.arduino.cc" + // 68 # text(8) + // 757365726E616D65 # "username" + + THEN("The encoding is successful") { + REQUIRE(err == MessageEncoder::Status::Complete); + REQUIRE(bytes_encoded == sizeof(expected_result)); + REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message with Ethernet") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::ETHERNET; + uint8_t ip [4] = {192, 168, 0, 2}; + command.params.eth.ip.type = IPType::IPv4; + memcpy(command.params.eth.ip.bytes, ip, sizeof(ip)); + uint8_t dns[4] = {8, 8, 8, 8}; + command.params.eth.dns.type = IPType::IPv4; + memcpy(command.params.eth.dns.bytes, dns, sizeof(dns)); + uint8_t gateway [4] = {192, 168, 1, 1}; + command.params.eth.gateway.type = IPType::IPv4; + memcpy(command.params.eth.gateway.bytes, gateway, sizeof(gateway)); + uint8_t netmask [4] = {255, 255, 255, 0}; + command.params.eth.netmask.type = IPType::IPv4; + memcpy(command.params.eth.netmask.bytes, netmask, sizeof(netmask)); + uint8_t buffer[512]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + uint8_t expected_result[] = { + 0xda, 0x00, 0x01, 0x11, 0x00, 0x85, + 0x06, 0x44, 0xc0, 0xa8, 0x00, 0x02, + 0x44, 0x08, 0x08, 0x08, 0x08, + 0x44, 0xc0, 0xa8, 0x01, 0x01, + 0x44, 0xff, 0xff, 0xff, 0x00 + }; + + // Test the encoding is + // DA 00011100 # tag(73728) + // 85 # array(5) + // 06 # unsigned(6) + // 44 # bytes(4) + // C0A80002 # "\xC0\xA8\u0000\u0002" + // 44 # bytes(4) + // 08080808 # "\b\b\b\b" + // 44 # bytes(4) + // C0A80101 # "\xC0\xA8\u0001\u0001" + // 44 # bytes(4) + // FFFFFF00 # "\xFF\xFF\xFF\u0000" + + THEN("The encoding is successful") { + REQUIRE(err == MessageEncoder::Status::Complete); + REQUIRE(bytes_encoded == sizeof(expected_result)); + REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message with Ethernet DHCP") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::ETHERNET; + + memset(command.params.eth.ip.bytes, 0, sizeof(command.params.eth.ip.bytes)); + memset(command.params.eth.dns.bytes, 0, sizeof(command.params.eth.dns.bytes)); + memset(command.params.eth.gateway.bytes, 0, sizeof(command.params.eth.gateway.bytes)); + memset(command.params.eth.netmask.bytes, 0, sizeof(command.params.eth.netmask.bytes)); + uint8_t buffer[512]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + uint8_t expected_result[] = { + 0xda, 0x00, 0x01, 0x11, 0x00, 0x85, + 0x06, 0x40, 0x40, 0x40, 0x40, + }; + + // Test the encoding is + // DA 00011100 # tag(73737) + // 85 # array(5) + // 06 # unsigned(6) + // 40 # bytes(0) + // # "" + // 40 # bytes(0) + // # "" + // 40 # bytes(0) + // # "" + // 40 # bytes(0) + // # "" + + THEN("The encoding is successful") { + REQUIRE(err == MessageEncoder::Status::Complete); + REQUIRE(bytes_encoded == sizeof(expected_result)); + REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message with Cellular") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::CELL; + String apn = "apn.arduino.cc"; + strcpy(command.params.cell.apn, apn.c_str()); + String user = "username"; + strcpy(command.params.cell.login, user.c_str()); + String password = "PASSWORD"; + strcpy(command.params.cell.pass, password.c_str()); + uint8_t buffer[512]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + uint8_t expected_result[] = { + 0xda, 0x00, 0x01, 0x11, 0x00, 0x83, + 0x07, 0x6e, 0x61, 0x70, 0x6e, 0x2e, 0x61, 0x72, + 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x63, + 0x68, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, + 0x65 + }; + + // Test the encoding is + // DA 00011100 # tag(73728) + // 83 # array(3) + // 07 # unsigned(7) + // 6E # text(14) + // 61706E2E61726475696E6F2E6363 # "apn.arduino.cc" + // 68 # text(8) + // 757365726E616D65 # "username" + + THEN("The encoding is successful") { + REQUIRE(err == MessageEncoder::Status::Complete); + REQUIRE(bytes_encoded == sizeof(expected_result)); + REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0); + } + } } diff --git a/src/cbor/CBOR.h b/src/cbor/CBOR.h index a5e6bfdcc..70a6a1e49 100644 --- a/src/cbor/CBOR.h +++ b/src/cbor/CBOR.h @@ -22,17 +22,18 @@ enum CBORCommandTag: CBORTag { // Commands UP - CBOROtaBeginUp = 0x010000, - CBORThingBeginCmd = 0x010300, - CBORLastValuesBeginCmd = 0x010500, - CBORDeviceBeginCmd = 0x010700, - CBOROtaProgressCmdUp = 0x010200, - CBORTimezoneCommandUp = 0x010800, + CBOROtaBeginUp = 0x010000, + CBORThingBeginCmd = 0x010300, + CBORLastValuesBeginCmd = 0x010500, + CBORDeviceBeginCmd = 0x010700, + CBOROtaProgressCmdUp = 0x010200, + CBORTimezoneCommandUp = 0x010800, + CBORDeviceNetConfigCmdUp = 0x011100, // Commands DOWN - CBOROtaUpdateCmdDown = 0x010100, - CBORThingUpdateCmd = 0x010400, - CBORThingDetachCmd = 0x011000, - CBORLastValuesUpdate = 0x010600, + CBOROtaUpdateCmdDown = 0x010100, + CBORThingUpdateCmd = 0x010400, + CBORThingDetachCmd = 0x011000, + CBORLastValuesUpdate = 0x010600, CBORTimezoneCommandDown = 0x010900, }; diff --git a/src/cbor/IoTCloudMessageEncoder.cpp b/src/cbor/IoTCloudMessageEncoder.cpp index ed4c6d823..639524cbe 100644 --- a/src/cbor/IoTCloudMessageEncoder.cpp +++ b/src/cbor/IoTCloudMessageEncoder.cpp @@ -153,9 +153,196 @@ MessageEncoder::Status TimezoneCommandUpEncoder::encode(CborEncoder* encoder, Me return MessageEncoder::Status::Complete; } +MessageEncoder::Status DeviceNetConfigCmdUpEncoder::encode(CborEncoder* encoder, Message *msg) { + DeviceNetConfigCmdUp * netConfig = (DeviceNetConfigCmdUp*) msg; + CborEncoder array_encoder; + + uint8_t typeID, paramsNum; + getEncodingParams(netConfig->params.type, &typeID, ¶msNum); + + if(cbor_encoder_create_array(encoder, &array_encoder, 1 + paramsNum) != CborNoError) { + return MessageEncoder::Status::Error; + } + + if(cbor_encode_uint(&array_encoder, typeID) != CborNoError) { + return MessageEncoder::Status::Error; + } + + MessageEncoder::Status encodeStatus = MessageEncoder::Status::Complete; + + switch (netConfig->params.type) + { + #if defined(BOARD_HAS_WIFI) + case NetworkAdapter::WIFI: + encodeStatus = encodeWiFiNetwork(&array_encoder, &netConfig->params.wifi); + break; + #endif // defined(BOARD_HAS_WIFI) + + #if defined(BOARD_HAS_LORA) + case NetworkAdapter::LORA: + encodeStatus = encodeLoRaNetwork(&array_encoder, &netConfig->params.lora); + break; + #endif // defined(BOARD_HAS_LORA) + + #if defined(BOARD_HAS_GSM) + case NetworkAdapter::GSM: + encodeStatus = encodeCellularNetwork(&array_encoder, &netConfig->params.gsm); + break; + #endif // defined(BOARD_HAS_GSM) + + #if defined(BOARD_HAS_NB) + case NetworkAdapter::NB: + encodeStatus = encodeCellularNetwork(&array_encoder, &netConfig->params.nb); + break; + #endif // defined(BOARD_HAS_NB) + + #if defined(BOARD_HAS_CATM1_NBIOT) + case NetworkAdapter::CATM1: + encodeStatus = encodeCatM1Network(&array_encoder, &netConfig->params.catm1); + break; + #endif // defined(BOARD_HAS_CATM1_NBIOT) + + #if defined(BOARD_HAS_ETHERNET) + case NetworkAdapter::ETHERNET: + encodeStatus = encodeEthernetNetwork(&array_encoder, &netConfig->params.eth); + break; + #endif // defined(BOARD_HAS_ETHERNET) + + #if defined(BOARD_HAS_CELLULAR) + case NetworkAdapter::CELL: + encodeStatus = encodeCellularNetwork(&array_encoder, &netConfig->params.cell); + break; + #endif // defined(BOARD_HAS_CELLULAR) + + default: + // Nothing to encode + break; + } + + if(encodeStatus != MessageEncoder::Status::Complete) { + return MessageEncoder::Status::Error; + } + + if(cbor_encoder_close_container(encoder, &array_encoder) != CborNoError) { + return MessageEncoder::Status::Error; + } + + return MessageEncoder::Status::Complete; +} + +void DeviceNetConfigCmdUpEncoder::getEncodingParams(NetworkAdapter type, uint8_t *typeID, uint8_t *paramsNum) { + switch (type) + { + case NetworkAdapter::WIFI: *typeID = 1; *paramsNum = 1; break; + case NetworkAdapter::LORA: *typeID = 2; *paramsNum = 1; break; + case NetworkAdapter::GSM: *typeID = 3; *paramsNum = 2; break; + case NetworkAdapter::NB: *typeID = 4; *paramsNum = 2; break; + case NetworkAdapter::CATM1: *typeID = 5; *paramsNum = 2; break; + case NetworkAdapter::ETHERNET: *typeID = 6; *paramsNum = 4; break; + case NetworkAdapter::CELL: *typeID = 7; *paramsNum = 2; break; + case NetworkAdapter::NOTECARD: *typeID = 8; *paramsNum = 0; break; + default: *typeID = 0; *paramsNum = 0; break; + } +} + +#if defined(BOARD_HAS_WIFI) +MessageEncoder::Status DeviceNetConfigCmdUpEncoder::encodeWiFiNetwork(CborEncoder *encoder, models::WiFiSetting *config) { + + if(cbor_encode_text_stringz(encoder, config->ssid) != CborNoError) { + return MessageEncoder::Status::Error; + } + + return MessageEncoder::Status::Complete; +} +#endif // defined(BOARD_HAS_WIFI) + +#if defined(BOARD_HAS_CATM1_NBIOT) +MessageEncoder::Status DeviceNetConfigCmdUpEncoder::encodeCatM1Network(CborEncoder *encoder, models::CATM1Setting *config) +{ + if(cbor_encode_text_stringz(encoder, config->apn) != CborNoError) { + return MessageEncoder::Status::Error; + } + + if(cbor_encode_text_stringz(encoder, config->login) != CborNoError) { + return MessageEncoder::Status::Error; + } + + return MessageEncoder::Status::Complete; +} +#endif // defined(BOARD_HAS_CATM1_NBIOT) + +#if defined(BOARD_HAS_ETHERNET) +MessageEncoder::Status DeviceNetConfigCmdUpEncoder::encodeEthernetNetwork(CborEncoder *encoder, models::EthernetSetting *config) { + + if(encodeIP(encoder, &config->ip) != MessageEncoder::Status::Complete) { + return MessageEncoder::Status::Error; + } + + if(encodeIP(encoder, &config->dns) != MessageEncoder::Status::Complete) { + return MessageEncoder::Status::Error; + } + + if(encodeIP(encoder, &config->gateway) != MessageEncoder::Status::Complete) { + return MessageEncoder::Status::Error; + } + + if(encodeIP(encoder, &config->netmask) != MessageEncoder::Status::Complete) { + return MessageEncoder::Status::Error; + } + + return MessageEncoder::Status::Complete; +} + +MessageEncoder::Status DeviceNetConfigCmdUpEncoder::encodeIP(CborEncoder *encoder, const models::ip_addr *ip) +{ + uint8_t ip_len = 0; + uint8_t emptyIP[16]; + memset(emptyIP, 0, sizeof(emptyIP)); + // Check if the IP is empty, DHCP case + if(memcmp(ip->bytes, emptyIP, sizeof(emptyIP)) == 0) { + ip_len = 0; + } else if(ip->type == IPType::IPv4) { + ip_len = 4; + } else if(ip->type == IPType::IPv6) { + ip_len = 16; + } + + if(cbor_encode_byte_string(encoder, ip->bytes, ip_len) != CborNoError) { + return MessageEncoder::Status::Error; + } + + return MessageEncoder::Status::Complete; +} +#endif // defined(BOARD_HAS_ETHERNET) + +#if defined(BOARD_HAS_NB) || defined(BOARD_HAS_GSM) || defined(BOARD_HAS_CELLULAR) +MessageEncoder::Status DeviceNetConfigCmdUpEncoder::encodeCellularNetwork(CborEncoder *encoder, models::CellularSetting *config) { + if(cbor_encode_text_stringz(encoder, config->apn) != CborNoError) { + return MessageEncoder::Status::Error; + } + + if(cbor_encode_text_stringz(encoder, config->login) != CborNoError) { + return MessageEncoder::Status::Error; + } + + return MessageEncoder::Status::Complete; +} +#endif // defined(BOARD_HAS_NB) || defined(BOARD_HAS_GSM) ||defined(BOARD_HAS_CELLULAR) + +#if defined(BOARD_HAS_LORA) +MessageEncoder::Status DeviceNetConfigCmdUpEncoder::encodeLoRaNetwork(CborEncoder *encoder, models::LoraSetting *config) { + if(cbor_encode_text_stringz(encoder, config->appeui) != CborNoError) { + return MessageEncoder::Status::Error; + } + + return MessageEncoder::Status::Complete; +} +#endif // defined(BOARD_HAS_LORA) + static OtaBeginCommandEncoder otaBeginCommandEncoder; static ThingBeginCommandEncoder thingBeginCommandEncoder; static LastValuesBeginCommandEncoder lastValuesBeginCommandEncoder; static DeviceBeginCommandEncoder deviceBeginCommandEncoder; static OtaProgressCommandUpEncoder otaProgressCommandUpEncoder; static TimezoneCommandUpEncoder timezoneCommandUpEncoder; +static DeviceNetConfigCmdUpEncoder deviceNetConfigCmdUpEncoder; diff --git a/src/cbor/IoTCloudMessageEncoder.h b/src/cbor/IoTCloudMessageEncoder.h index 99922bc59..57eec1e4a 100644 --- a/src/cbor/IoTCloudMessageEncoder.h +++ b/src/cbor/IoTCloudMessageEncoder.h @@ -15,6 +15,7 @@ * INCLUDE ******************************************************************************/ +#include #include "./CBOR.h" #include #include "message/Commands.h" @@ -71,5 +72,36 @@ class TimezoneCommandUpEncoder: public CBORMessageEncoderInterface { MessageEncoder::Status encode(CborEncoder* encoder, Message *msg) override; }; +class DeviceNetConfigCmdUpEncoder: public CBORMessageEncoderInterface { +public: + DeviceNetConfigCmdUpEncoder() + : CBORMessageEncoderInterface(CBORDeviceNetConfigCmdUp, DeviceNetConfigCmdUpId) {} +protected: + MessageEncoder::Status encode(CborEncoder* encoder, Message *msg) override; +private: + void getEncodingParams(NetworkAdapter type, uint8_t *typeID, uint8_t *paramsNum); + +#if defined(BOARD_HAS_WIFI) + MessageEncoder::Status encodeWiFiNetwork(CborEncoder* encoder, models::WiFiSetting *config); +#endif + +#if defined(BOARD_HAS_CATM1_NBIOT) + MessageEncoder::Status encodeCatM1Network(CborEncoder* encoder, models::CATM1Setting *config); +#endif + +#if defined(BOARD_HAS_ETHERNET) + MessageEncoder::Status encodeEthernetNetwork(CborEncoder* encoder, models::EthernetSetting *config); + MessageEncoder::Status encodeIP(CborEncoder* encoder, const models::ip_addr *ip); +#endif + +#if defined(BOARD_HAS_NB) || defined(BOARD_HAS_GSM) || defined(BOARD_HAS_CELLULAR) + MessageEncoder::Status encodeCellularNetwork(CborEncoder* encoder, models::CellularSetting *config); +#endif + +#if defined(BOARD_HAS_LORA) + MessageEncoder::Status encodeLoRaNetwork(CborEncoder* encoder, models::LoraSetting *config); +#endif + +}; #endif /* ARDUINO_CBOR_MESSAGE_ENCODER_H_ */ diff --git a/src/message/Commands.h b/src/message/Commands.h index 7e044b7a7..2195e4395 100644 --- a/src/message/Commands.h +++ b/src/message/Commands.h @@ -17,6 +17,7 @@ #include #include #include +#include /****************************************************************************** * DEFINE @@ -42,6 +43,7 @@ enum CommandId: MessageId { DeviceRegisteredCmdId, DeviceAttachedCmdId, DeviceDetachedCmdId, + DeviceNetConfigCmdUpId, /* Thing commands */ LastValuesBeginCmdId, @@ -145,6 +147,11 @@ struct TimezoneCommandDown { } params; }; +struct DeviceNetConfigCmdUp { + Command c; + models::NetworkSetting params; +}; + union CommandDown { Command c; struct OtaUpdateCmdDown otaUpdateCmdDown; From 45d819ca80ea9272d5e2a900fc0531b2a4435014 Mon Sep 17 00:00:00 2001 From: fabik111 Date: Fri, 30 May 2025 17:45:32 +0200 Subject: [PATCH 2/5] send network data to cloud --- src/ArduinoIoTCloudDevice.cpp | 11 ++++++++++- src/ArduinoIoTCloudDevice.h | 4 +++- src/ArduinoIoTCloudNotecard.cpp | 2 +- src/ArduinoIoTCloudTCP.cpp | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ArduinoIoTCloudDevice.cpp b/src/ArduinoIoTCloudDevice.cpp index 58f00d202..9430dd294 100644 --- a/src/ArduinoIoTCloudDevice.cpp +++ b/src/ArduinoIoTCloudDevice.cpp @@ -28,13 +28,15 @@ _state{State::Init}, _attachAttempt(0, 0), _propertyContainer(), _propertyContainerIndex(0), +_connectionHandler(nullptr), _attached(false), _registered(false) { } -void ArduinoCloudDevice::begin() { +void ArduinoCloudDevice::begin(ConnectionHandler *connectionHandler) { _attachAttempt.begin(AIOT_CONFIG_THING_ID_REQUEST_RETRY_DELAY_ms, AIOT_CONFIG_MAX_THING_ID_REQUEST_RETRY_DELAY_ms); + _connectionHandler = connectionHandler; } void ArduinoCloudDevice::update() { @@ -109,6 +111,13 @@ ArduinoCloudDevice::State ArduinoCloudDevice::handleSendCapabilities() { DeviceBeginCmd deviceBegin = { DeviceBeginCmdId, AIOT_CONFIG_LIB_VERSION }; deliver(reinterpret_cast(&deviceBegin)); + /* Send Network Configuration */ + if(_connectionHandler != nullptr){ + DeviceNetConfigCmdUp deviceNetConfig = { DeviceNetConfigCmdUpId }; + _connectionHandler->getSetting(deviceNetConfig.params ); + deliver(reinterpret_cast(&deviceNetConfig)); + } + /* Subscribe to device topic to request */ ThingBeginCmd thingBegin = { ThingBeginCmdId }; deliver(reinterpret_cast(&thingBegin)); diff --git a/src/ArduinoIoTCloudDevice.h b/src/ArduinoIoTCloudDevice.h index 5bfe052fa..fdea5fac6 100644 --- a/src/ArduinoIoTCloudDevice.h +++ b/src/ArduinoIoTCloudDevice.h @@ -18,6 +18,7 @@ #include #include "interfaces/CloudProcess.h" #include "property/PropertyContainer.h" +#include /****************************************************************************** * CLASS DECLARATION @@ -30,7 +31,7 @@ class ArduinoCloudDevice : public CloudProcess { virtual void update() override; virtual void handleMessage(Message* m) override; - virtual void begin(); + virtual void begin(ConnectionHandler *connectionHandler); virtual int connected(); inline PropertyContainer &getPropertyContainer() { @@ -57,6 +58,7 @@ class ArduinoCloudDevice : public CloudProcess { CommandId _command; TimedAttempt _attachAttempt; PropertyContainer _propertyContainer; + ConnectionHandler *_connectionHandler; unsigned int _propertyContainerIndex; bool _attached; bool _registered; diff --git a/src/ArduinoIoTCloudNotecard.cpp b/src/ArduinoIoTCloudNotecard.cpp index fac028e61..5bb53f96d 100644 --- a/src/ArduinoIoTCloudNotecard.cpp +++ b/src/ArduinoIoTCloudNotecard.cpp @@ -104,7 +104,7 @@ int ArduinoIoTCloudNotecard::begin(ConnectionHandler &connection_, int interrupt // Configure the Device and Thing property containers _thing.begin(); - _device.begin(); + _device.begin(_connection); return 1; // (true -> success) } diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index 9de9d301a..ec24159f8 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -165,7 +165,7 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress, _messageTopicIn = getTopic_messagein(); _thing.begin(); - _device.begin(); + _device.begin(_connection); #if OTA_ENABLED && !defined(OFFLOADED_DOWNLOAD) _ota.setClient(&_otaClient); From 16e2ad38ad648780a202fc5d04f4f1cad0d3b2d9 Mon Sep 17 00:00:00 2001 From: fabik111 Date: Tue, 3 Jun 2025 11:57:39 +0200 Subject: [PATCH 3/5] add test cases --- extras/test/src/test_command_encode.cpp | 358 +++++++++++++++++++++++- 1 file changed, 355 insertions(+), 3 deletions(-) diff --git a/extras/test/src/test_command_encode.cpp b/extras/test/src/test_command_encode.cpp index af9975532..2afc0dddb 100644 --- a/extras/test/src/test_command_encode.cpp +++ b/extras/test/src/test_command_encode.cpp @@ -683,6 +683,27 @@ SCENARIO("Test the encoding of command messages") { } } + + WHEN("Encode the DeviceNetConfigCmdUp message with Wifi and buffer without enough space") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::WIFI; + String longSsid = "longSSID"; + strcpy(command.params.wifi.ssid, longSsid.c_str()); + + uint8_t buffer[7]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + THEN("The encoding fails") { + REQUIRE(err == MessageEncoder::Status::Error); + } + } + WHEN("Encode the DeviceNetConfigCmdUp message with LoraWan") { DeviceNetConfigCmdUp command; @@ -718,6 +739,28 @@ SCENARIO("Test the encoding of command messages") { } } + WHEN("Encode the DeviceNetConfigCmdUp message with LoRa buffer without enough space") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::LORA; + + String app_eui = "APPEUI"; + strcpy(command.params.lora.appeui, app_eui.c_str()); + String app_key = "APPKEY"; + strcpy(command.params.lora.appkey, app_key.c_str()); + uint8_t buffer[7]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + THEN("The encoding fails") { + REQUIRE(err == MessageEncoder::Status::Error); + } + } + WHEN("Encode the DeviceNetConfigCmdUp message with GSM") { DeviceNetConfigCmdUp command; @@ -760,18 +803,41 @@ SCENARIO("Test the encoding of command messages") { } } - WHEN("Encode the DeviceNetConfigCmdUp message with NB-IoT") + WHEN("Encode the DeviceNetConfigCmdUp message with GSM buffer without enough space for login") { DeviceNetConfigCmdUp command; command.c.id = CommandId::DeviceNetConfigCmdUpId; - command.params.type = NetworkAdapter::NB; + command.params.type = NetworkAdapter::GSM; String apn = "apn.arduino.cc"; strcpy(command.params.nb.apn, apn.c_str()); String user = "username"; strcpy(command.params.nb.login, user.c_str()); String password = "PASSWORD"; strcpy(command.params.nb.pass, password.c_str()); + uint8_t buffer[25]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + THEN("The encoding fails") { + REQUIRE(err == MessageEncoder::Status::Error); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message with NB-IoT") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::NB; + String apn = "apn.arduino.cc"; + strcpy(command.params.gsm.apn, apn.c_str()); + String user = "username"; + strcpy(command.params.gsm.login, user.c_str()); + String password = "PASSWORD"; + strcpy(command.params.gsm.pass, password.c_str()); uint8_t buffer[512]; size_t bytes_encoded = sizeof(buffer); @@ -789,7 +855,7 @@ SCENARIO("Test the encoding of command messages") { // Test the encoding is // DA 00011100 # tag(73728) // 83 # array(3) - // 04 # unsigned(4) + // 03 # unsigned(3) // 6E # text(14) // 61706E2E61726475696E6F2E6363 # "apn.arduino.cc" // 68 # text(8) @@ -802,6 +868,29 @@ SCENARIO("Test the encoding of command messages") { } } + WHEN("Encode the DeviceNetConfigCmdUp message with NB-IoT buffer without enough space") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::NB; + String apn = "apn.arduino.cc"; + strcpy(command.params.nb.apn, apn.c_str()); + String user = "username"; + strcpy(command.params.nb.login, user.c_str()); + String password = "PASSWORD"; + strcpy(command.params.nb.pass, password.c_str()); + uint8_t buffer[12]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + THEN("The encoding fails") { + REQUIRE(err == MessageEncoder::Status::Error); + } + } + WHEN("Encode the DeviceNetConfigCmdUp message with CAT-M1") { DeviceNetConfigCmdUp command; @@ -844,6 +933,52 @@ SCENARIO("Test the encoding of command messages") { } } + WHEN("Encode the DeviceNetConfigCmdUp message with CAT-M1 buffer without enough space") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::CATM1; + String apn = "apn.arduino.cc"; + strcpy(command.params.nb.apn, apn.c_str()); + String user = "username"; + strcpy(command.params.nb.login, user.c_str()); + String password = "PASSWORD"; + strcpy(command.params.nb.pass, password.c_str()); + uint8_t buffer[12]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + THEN("The encoding fails") { + REQUIRE(err == MessageEncoder::Status::Error); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message with CAT-M1 buffer without enough space for login") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::CATM1; + String apn = "apn.arduino.cc"; + strcpy(command.params.nb.apn, apn.c_str()); + String user = "username"; + strcpy(command.params.nb.login, user.c_str()); + String password = "PASSWORD"; + strcpy(command.params.nb.pass, password.c_str()); + uint8_t buffer[25]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + THEN("The encoding fails") { + REQUIRE(err == MessageEncoder::Status::Error); + } + } + WHEN("Encode the DeviceNetConfigCmdUp message with Ethernet") { DeviceNetConfigCmdUp command; @@ -896,6 +1031,65 @@ SCENARIO("Test the encoding of command messages") { } } + WHEN("Encode the DeviceNetConfigCmdUp message with Ethernet IPv6") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::ETHERNET; + uint8_t ip[] = {0x1a, 0x4f, 0xa7, 0xa9, 0x92, 0x8f, 0x7b, 0x1c, 0xec, 0x3b, 0x1e, 0xcd, 0x88, 0x58, 0x0d, 0x1e}; + command.params.eth.ip.type = IPType::IPv6; + memcpy(command.params.eth.ip.bytes, ip, sizeof(ip)); + uint8_t dns[] = {0x21, 0xf6, 0x3b, 0x22, 0x99, 0x6f, 0x5b, 0x72, 0x25, 0xd9, 0xe0, 0x24, 0xf0, 0x36, 0xb5, 0xd2}; + command.params.eth.dns.type = IPType::IPv6; + memcpy(command.params.eth.dns.bytes, dns, sizeof(dns)); + uint8_t gateway[] = {0x2e, 0xc2, 0x27, 0xf1, 0xf1, 0x9a, 0x0c, 0x11, 0x47, 0x1b, 0x84, 0xaf, 0x96, 0x10, 0xb0, 0x17}; + command.params.eth.gateway.type = IPType::IPv6; + memcpy(command.params.eth.gateway.bytes, gateway, sizeof(gateway)); + uint8_t netmask[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + command.params.eth.netmask.type = IPType::IPv6; + memcpy(command.params.eth.netmask.bytes, netmask, sizeof(netmask)); + + + uint8_t buffer[512]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + uint8_t expected_result[] = { + 0xda, 0x00, 0x01, 0x11, 0x00, 0x85, + 0x06,0x50, 0x1A, 0x4F, 0xA7, 0xA9, 0x92, 0x8F, 0x7B, 0x1C, + 0xEC, 0x3B, 0x1E, 0xCD, 0x88, 0x58, 0x0D, 0x1E, + 0x50, 0x21, 0xF6, 0x3B, 0x22, 0x99, 0x6F, + 0x5B, 0x72, 0x25, 0xD9, 0xE0, 0x24, 0xF0, 0x36, + 0xB5, 0xD2, 0x50, 0x2E, 0xC2, 0x27, 0xF1, + 0xF1, 0x9A, 0x0C, 0x11, 0x47, 0x1B, 0x84, 0xAF, + 0x96, 0x10, 0xB0, 0x17, 0x50, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + // Test the encoding is + // DA 00011100 # tag(73728) + // 85 # array(5) + // 06 # unsigned(6) + // 50 # bytes(16) + // 1A4FA7A9928F7B1CEC3B1ECD88580D1E # "\u001AO\xA7\xA9\x92\x8F{\u001C\xEC;\u001E͈X\r\u001E" + // 50 # bytes(16) + // 21F63B22996F5B7225D9E024F036B5D2 # "!\xF6;\"\x99o[r%\xD9\xE0$\xF06\xB5\xD2" + // 50 # bytes(16) + // 2EC227F1F19A0C11471B84AF9610B017 # ".\xC2'\xF1\xF1\x9A\f\u0011G\e\x84\xAF\x96\u0010\xB0\u0017" + // 50 # bytes(16) + // FFFFFFFFFFFFFFFF0000000000000000 # "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + + THEN("The encoding is successful") { + REQUIRE(err == MessageEncoder::Status::Complete); + REQUIRE(bytes_encoded == sizeof(expected_result)); + REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0); + } + } + WHEN("Encode the DeviceNetConfigCmdUp message with Ethernet DHCP") { DeviceNetConfigCmdUp command; @@ -938,6 +1132,68 @@ SCENARIO("Test the encoding of command messages") { } } + WHEN("Encode the DeviceNetConfigCmdUp message with Ethernet IPv6 not enough space") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::ETHERNET; + uint8_t ip[] = {0x1a, 0x4f, 0xa7, 0xa9, 0x92, 0x8f, 0x7b, 0x1c, 0xec, 0x3b, 0x1e, 0xcd, 0x88, 0x58, 0x0d, 0x1e}; + command.params.eth.ip.type = IPType::IPv6; + memcpy(command.params.eth.ip.bytes, ip, sizeof(ip)); + uint8_t dns[] = {0x21, 0xf6, 0x3b, 0x22, 0x99, 0x6f, 0x5b, 0x72, 0x25, 0xd9, 0xe0, 0x24, 0xf0, 0x36, 0xb5, 0xd2}; + command.params.eth.dns.type = IPType::IPv6; + memcpy(command.params.eth.dns.bytes, dns, sizeof(dns)); + uint8_t gateway[] = {0x2e, 0xc2, 0x27, 0xf1, 0xf1, 0x9a, 0x0c, 0x11, 0x47, 0x1b, 0x84, 0xaf, 0x96, 0x10, 0xb0, 0x17}; + command.params.eth.gateway.type = IPType::IPv6; + memcpy(command.params.eth.gateway.bytes, gateway, sizeof(gateway)); + uint8_t netmask[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + command.params.eth.netmask.type = IPType::IPv6; + memcpy(command.params.eth.netmask.bytes, netmask, sizeof(netmask)); + + + uint8_t buffer[35]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + THEN("The encoding fails") { + REQUIRE(err == MessageEncoder::Status::Error); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message with Ethernet IPv6 not enough space for any") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::ETHERNET; + uint8_t ip[] = {0x1a, 0x4f, 0xa7, 0xa9, 0x92, 0x8f, 0x7b, 0x1c, 0xec, 0x3b, 0x1e, 0xcd, 0x88, 0x58, 0x0d, 0x1e}; + command.params.eth.ip.type = IPType::IPv6; + memcpy(command.params.eth.ip.bytes, ip, sizeof(ip)); + uint8_t dns[] = {0x21, 0xf6, 0x3b, 0x22, 0x99, 0x6f, 0x5b, 0x72, 0x25, 0xd9, 0xe0, 0x24, 0xf0, 0x36, 0xb5, 0xd2}; + command.params.eth.dns.type = IPType::IPv6; + memcpy(command.params.eth.dns.bytes, dns, sizeof(dns)); + uint8_t gateway[] = {0x2e, 0xc2, 0x27, 0xf1, 0xf1, 0x9a, 0x0c, 0x11, 0x47, 0x1b, 0x84, 0xaf, 0x96, 0x10, 0xb0, 0x17}; + command.params.eth.gateway.type = IPType::IPv6; + memcpy(command.params.eth.gateway.bytes, gateway, sizeof(gateway)); + uint8_t netmask[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + command.params.eth.netmask.type = IPType::IPv6; + memcpy(command.params.eth.netmask.bytes, netmask, sizeof(netmask)); + + + uint8_t buffer[12]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + THEN("The encoding fails") { + REQUIRE(err == MessageEncoder::Status::Error); + } + } + WHEN("Encode the DeviceNetConfigCmdUp message with Cellular") { DeviceNetConfigCmdUp command; @@ -979,4 +1235,100 @@ SCENARIO("Test the encoding of command messages") { REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0); } } + + WHEN("Encode the DeviceNetConfigCmdUp message with Notecard") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::NOTECARD; + + uint8_t buffer[512]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + uint8_t expected_result[] = { + 0xda, 0x00, 0x01, 0x11, 0x00, 0x81, + 0x08 + }; + + // Test the encoding is + // DA 00011100 # tag(73728) + // 81 # array(1) + // 08 # unsigned(8) + + THEN("The encoding is successful") { + REQUIRE(err == MessageEncoder::Status::Complete); + REQUIRE(bytes_encoded == sizeof(expected_result)); + REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message with None") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::NONE; + + uint8_t buffer[512]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + uint8_t expected_result[] = { + 0xda, 0x00, 0x01, 0x11, 0x00, 0x81, 0x00 + }; + + // Test the encoding is + // DA 00011100 # tag(73728) + // 80 # array(1) + + THEN("The encoding is successful") { + REQUIRE(err == MessageEncoder::Status::Complete); + REQUIRE(bytes_encoded == sizeof(expected_result)); + REQUIRE(memcmp(buffer, expected_result, sizeof(expected_result)) == 0); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message buffer without enough space") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::ETHERNET; + + + uint8_t buffer[6]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + THEN("The encoding fails") { + REQUIRE(err == MessageEncoder::Status::Error); + } + } + + WHEN("Encode the DeviceNetConfigCmdUp message buffer without enough space for array") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + command.params.type = NetworkAdapter::ETHERNET; + + + uint8_t buffer[5]; + size_t bytes_encoded = sizeof(buffer); + + CBORMessageEncoder encoder; + MessageEncoder::Status err = encoder.encode((Message*)&command, buffer, bytes_encoded); + + THEN("The encoding fails") { + REQUIRE(err == MessageEncoder::Status::Error); + } + } } From 2430efaea6f07d65eac2bd6d3b226c7fd2322f0c Mon Sep 17 00:00:00 2001 From: fabik111 Date: Tue, 3 Jun 2025 14:52:52 +0200 Subject: [PATCH 4/5] fix CI --- .github/workflows/compile-examples.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index d30277a07..bf67445cf 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -21,7 +21,8 @@ jobs: UNIVERSAL_LIBRARIES: | # Install the ArduinoIoTCloud library from the repository - source-path: ./ - - name: Arduino_ConnectionHandler + - source-url: https://github.com/fabik111/Arduino_ConnectionHandler.git + version: add-get-setting - name: ArduinoHttpClient - name: Arduino_DebugUtils - name: ArduinoMqttClient From e4943b005b2a0a1172dc56bd122538e394f8e885 Mon Sep 17 00:00:00 2001 From: fabik111 Date: Tue, 3 Jun 2025 18:16:05 +0200 Subject: [PATCH 5/5] send wifi fw version to cloud --- .github/workflows/compile-examples.yml | 3 ++- src/ArduinoIoTCloudDevice.cpp | 5 +++++ src/message/Commands.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index bf67445cf..961db3204 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -27,7 +27,8 @@ jobs: - name: Arduino_DebugUtils - name: ArduinoMqttClient - name: Arduino_SecureElement - - name: Arduino_CloudUtils + - source-url: https://github.com/fabik111/Arduino_CloudUtils.git + version: add-standard-encoders # sketch paths to compile (recursive) for all boards UNIVERSAL_SKETCH_PATHS: | - examples/ArduinoIoTCloud-Advanced diff --git a/src/ArduinoIoTCloudDevice.cpp b/src/ArduinoIoTCloudDevice.cpp index 9430dd294..4af685513 100644 --- a/src/ArduinoIoTCloudDevice.cpp +++ b/src/ArduinoIoTCloudDevice.cpp @@ -118,6 +118,11 @@ ArduinoCloudDevice::State ArduinoCloudDevice::handleSendCapabilities() { deliver(reinterpret_cast(&deviceNetConfig)); } +#if defined(BOARD_HAS_WIFI) + String WiFiFWVersion = WiFi.firmwareVersion(); + WiFiFWVersionMessage wifiFWVersionMessage = { WiFiFWVersionMessageId, WiFiFWVersion.c_str() }; + deliver(reinterpret_cast(&wifiFWVersionMessage)); +#endif /* Subscribe to device topic to request */ ThingBeginCmd thingBegin = { ThingBeginCmdId }; deliver(reinterpret_cast(&thingBegin)); diff --git a/src/message/Commands.h b/src/message/Commands.h index 2195e4395..60840231a 100644 --- a/src/message/Commands.h +++ b/src/message/Commands.h @@ -17,6 +17,7 @@ #include #include #include +#include #include /******************************************************************************