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..8cad13d1e 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,742 @@ 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 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; + 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 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; + 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 GSM buffer without enough space for login") + { + DeviceNetConfigCmdUp command; + command.c.id = CommandId::DeviceNetConfigCmdUpId; + + 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); + + 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) + // 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 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; + 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 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; + 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 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; + 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 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 IPv4 not enough space") + { + 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[20]; + 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 IPv4 not enough space for netmask") + { + 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[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 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; + 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); + } + } + + 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); + } + } } diff --git a/library.properties b/library.properties index 7ab62ce5c..b6ea055d6 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ArduinoIoTCloud -version=2.6.1 +version=2.7.0 author=Arduino maintainer=Arduino sentence=This library allows connecting to the Arduino IoT Cloud service. diff --git a/src/AIoTC_Config.h b/src/AIoTC_Config.h index a25b81080..b0fba96a3 100644 --- a/src/AIoTC_Config.h +++ b/src/AIoTC_Config.h @@ -142,6 +142,12 @@ #define BOARD_HAS_SECURE_ELEMENT #endif +#if (defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_MBED) ||\ + defined(ARDUINO_ARCH_RENESAS) || defined(ARDUINO_ARCH_ESP32)) &&\ + !defined(ARDUINO_ARCH_ZEPHYR) + #define BOARD_HAS_HW_RTC +#endif + #endif // HAS_NOTECARD #if defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_OPTA) || defined(ARDUINO_GIGA) @@ -185,6 +191,6 @@ #define AIOT_CONFIG_LASTVALUES_SYNC_MAX_RETRY_CNT (10UL) #endif -#define AIOT_CONFIG_LIB_VERSION "2.6.1" +#define AIOT_CONFIG_LIB_VERSION "2.7.0" #endif /* ARDUINO_AIOTC_CONFIG_H_ */ diff --git a/src/ArduinoIoTCloudDevice.cpp b/src/ArduinoIoTCloudDevice.cpp index 58f00d202..3c7e2872f 100644 --- a/src/ArduinoIoTCloudDevice.cpp +++ b/src/ArduinoIoTCloudDevice.cpp @@ -28,6 +28,7 @@ _state{State::Init}, _attachAttempt(0, 0), _propertyContainer(), _propertyContainerIndex(0), +_getNetConfigCallback(nullptr), _attached(false), _registered(false) { } @@ -109,8 +110,20 @@ ArduinoCloudDevice::State ArduinoCloudDevice::handleSendCapabilities() { DeviceBeginCmd deviceBegin = { DeviceBeginCmdId, AIOT_CONFIG_LIB_VERSION }; deliver(reinterpret_cast(&deviceBegin)); + /* Send Network Configuration */ + if(_getNetConfigCallback){ + DeviceNetConfigCmdUp deviceNetConfig = { DeviceNetConfigCmdUpId }; + _getNetConfigCallback(deviceNetConfig.params ); + deliver(reinterpret_cast(&deviceNetConfig)); + } + +#if defined(BOARD_HAS_WIFI) && not defined(BOARD_ESP) + String WiFiFWVersion = WiFi.firmwareVersion(); + VersionMessage versionMessage = { WiFiFWVersionMessageId, WiFiFWVersion.c_str() }; + deliver(reinterpret_cast(&versionMessage)); +#endif /* Subscribe to device topic to request */ - ThingBeginCmd thingBegin = { ThingBeginCmdId }; + ThingBeginCmd thingBegin = { ThingBeginCmdId, {} }; deliver(reinterpret_cast(&thingBegin)); /* No device configuration received. Wait: 4s -> 8s -> 16s -> 32s -> 32s ...*/ diff --git a/src/ArduinoIoTCloudDevice.h b/src/ArduinoIoTCloudDevice.h index 5bfe052fa..ae4386403 100644 --- a/src/ArduinoIoTCloudDevice.h +++ b/src/ArduinoIoTCloudDevice.h @@ -1,5 +1,5 @@ /* - This file is part of the Arduino_SecureElement library. + This file is part of the ArduinoIoTCloud library. Copyright (c) 2024 Arduino SA @@ -18,6 +18,8 @@ #include #include "interfaces/CloudProcess.h" #include "property/PropertyContainer.h" +#include +#include /****************************************************************************** * CLASS DECLARATION @@ -26,6 +28,8 @@ class ArduinoCloudDevice : public CloudProcess { public: + typedef std::function GetNetworkSettingCbk; + ArduinoCloudDevice(MessageStream* stream); virtual void update() override; virtual void handleMessage(Message* m) override; @@ -43,6 +47,10 @@ class ArduinoCloudDevice : public CloudProcess { return _attached; }; + void setGetNetworkSettingCbk(GetNetworkSettingCbk cbk) { + _getNetConfigCallback = cbk; + } + private: @@ -57,6 +65,7 @@ class ArduinoCloudDevice : public CloudProcess { CommandId _command; TimedAttempt _attachAttempt; PropertyContainer _propertyContainer; + GetNetworkSettingCbk _getNetConfigCallback; unsigned int _propertyContainerIndex; bool _attached; bool _registered; diff --git a/src/ArduinoIoTCloudNotecard.cpp b/src/ArduinoIoTCloudNotecard.cpp index fac028e61..90f00ceb5 100644 --- a/src/ArduinoIoTCloudNotecard.cpp +++ b/src/ArduinoIoTCloudNotecard.cpp @@ -105,6 +105,9 @@ int ArduinoIoTCloudNotecard::begin(ConnectionHandler &connection_, int interrupt // Configure the Device and Thing property containers _thing.begin(); _device.begin(); + _device.setGetNetworkSettingCbk([connection = this->_connection](models::NetworkSetting &setting) { + connection->getSetting(setting); + }); return 1; // (true -> success) } diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index 9de9d301a..9fa9de583 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -72,7 +72,8 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP() , _get_ota_confirmation{nullptr} #endif /* OTA_ENABLED */ { - + cbor::encoder::iotcloud::commandEncoders(); + cbor::decoder::iotcloud::commandDecoders(); } /****************************************************************************** @@ -167,6 +168,10 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress, _thing.begin(); _device.begin(); + _device.setGetNetworkSettingCbk([connection = this->_connection](models::NetworkSetting &setting) { + connection->getSetting(setting); + }); + #if OTA_ENABLED && !defined(OFFLOADED_DOWNLOAD) _ota.setClient(&_otaClient); #endif // OTA_ENABLED && !defined(OFFLOADED_DOWNLOAD) @@ -211,6 +216,7 @@ void ArduinoIoTCloudTCP::update() switch (_state) { case State::ConfigPhy: next_state = handle_ConfigPhy(); break; + case State::UpdatePhy: next_state = handle_UpdatePhy(); break; case State::Init: next_state = handle_Init(); break; case State::ConnectPhy: next_state = handle_ConnectPhy(); break; case State::SyncTime: next_state = handle_SyncTime(); break; @@ -235,7 +241,7 @@ void ArduinoIoTCloudTCP::update() */ #if NETWORK_CONFIGURATOR_ENABLED if(_configurator != nullptr && _state > State::Init && _configurator->update() == NetworkConfiguratorStates::UPDATING_CONFIG){ - _state = State::ConfigPhy; + _state = State::UpdatePhy; } #endif @@ -311,6 +317,19 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConfigPhy() #endif } +ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_UpdatePhy() +{ +#if NETWORK_CONFIGURATOR_ENABLED + if(_configurator->update() == NetworkConfiguratorStates::CONFIGURED) { + _configurator->disconnectAgent(); + return State::Disconnect; + } + return State::UpdatePhy; +#else + return State::Init; +#endif +} + ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Init() { /* Setup broker TLS client */ diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h index d59c6de97..fd2ffab7f 100644 --- a/src/ArduinoIoTCloudTCP.h +++ b/src/ArduinoIoTCloudTCP.h @@ -122,6 +122,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass enum class State { ConfigPhy, + UpdatePhy, Init, ConnectPhy, SyncTime, @@ -178,6 +179,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass inline String getTopic_datain () { return ( getThingId().length() == 0) ? String("") : String("/a/t/" + getThingId() + "/e/i"); } State handle_ConfigPhy(); + State handle_UpdatePhy(); State handle_Init(); State handle_ConnectPhy(); State handle_SyncTime(); diff --git a/src/ArduinoIoTCloudThing.cpp b/src/ArduinoIoTCloudThing.cpp index a6bb74190..20ddc382d 100644 --- a/src/ArduinoIoTCloudThing.cpp +++ b/src/ArduinoIoTCloudThing.cpp @@ -1,5 +1,5 @@ /* - This file is part of the Arduino_SecureElement library. + This file is part of the ArduinoIoTCloud library. Copyright (c) 2024 Arduino SA diff --git a/src/ArduinoIoTCloudThing.h b/src/ArduinoIoTCloudThing.h index d6a9b8dac..3e379be61 100644 --- a/src/ArduinoIoTCloudThing.h +++ b/src/ArduinoIoTCloudThing.h @@ -1,5 +1,5 @@ /* - This file is part of the Arduino_SecureElement library. + This file is part of the ArduinoIoTCloud library. Copyright (c) 2024 Arduino SA diff --git a/src/cbor/CBOR.h b/src/cbor/CBOR.h index a5e6bfdcc..5200f9d60 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, - CBORTimezoneCommandDown = 0x010900, + CBOROtaUpdateCmdDown = 0x010100, + CBORThingUpdateCmd = 0x010400, + CBORThingDetachCmd = 0x011000, + CBORLastValuesUpdate = 0x010600, + CBORTimezoneCommandDown = 0x010900, }; diff --git a/src/cbor/IoTCloudMessageDecoder.cpp b/src/cbor/IoTCloudMessageDecoder.cpp index 8a17ba1f3..8deb9e3e5 100644 --- a/src/cbor/IoTCloudMessageDecoder.cpp +++ b/src/cbor/IoTCloudMessageDecoder.cpp @@ -176,3 +176,13 @@ static ThingUpdateCommandDecoder thingUpdateCommandDecoder; static ThingDetachCommandDecoder thingDetachCommandDecoder; static LastValuesUpdateCommandDecoder lastValuesUpdateCommandDecoder; static TimezoneCommandDownDecoder timezoneCommandDownDecoder; + +namespace cbor { namespace decoder { namespace iotcloud { + void commandDecoders() { + (void) otaUpdateCommandDecoder; + (void) thingUpdateCommandDecoder; + (void) thingDetachCommandDecoder; + (void) lastValuesUpdateCommandDecoder; + (void) timezoneCommandDownDecoder; + } +}}} diff --git a/src/cbor/IoTCloudMessageDecoder.h b/src/cbor/IoTCloudMessageDecoder.h index 4b444f5c8..f072a2c3a 100644 --- a/src/cbor/IoTCloudMessageDecoder.h +++ b/src/cbor/IoTCloudMessageDecoder.h @@ -63,4 +63,12 @@ class TimezoneCommandDownDecoder: public CBORMessageDecoderInterface { MessageDecoder::Status decode(CborValue* iter, Message *msg) override; }; +namespace cbor { namespace decoder { namespace iotcloud { + /** + * Some link time optimization may exclude these classes to be instantiated + * thus it may be required to reference them from outside of this file + */ + void commandDecoders(); +}}} + #endif /* ARDUINO_CBOR_MESSAGE_DECODER_H_ */ diff --git a/src/cbor/IoTCloudMessageEncoder.cpp b/src/cbor/IoTCloudMessageEncoder.cpp index ed4c6d823..154053224 100644 --- a/src/cbor/IoTCloudMessageEncoder.cpp +++ b/src/cbor/IoTCloudMessageEncoder.cpp @@ -153,9 +153,209 @@ 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; + +namespace cbor { namespace encoder { namespace iotcloud { + void commandEncoders() { + (void) otaBeginCommandEncoder; + (void) thingBeginCommandEncoder; + (void) lastValuesBeginCommandEncoder; + (void) deviceBeginCommandEncoder; + (void) otaProgressCommandUpEncoder; + (void) timezoneCommandUpEncoder; + (void) deviceNetConfigCmdUpEncoder; + } +}}} + diff --git a/src/cbor/IoTCloudMessageEncoder.h b/src/cbor/IoTCloudMessageEncoder.h index 99922bc59..e71f4defc 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,44 @@ 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 + +}; + +namespace cbor { namespace encoder { namespace iotcloud { + /** + * Some link time optimization may exclude these classes to be instantiated + * thus it may be required to reference them from outside of this file + */ + void commandEncoders(); +}}} #endif /* ARDUINO_CBOR_MESSAGE_ENCODER_H_ */ diff --git a/src/message/Commands.h b/src/message/Commands.h index 7e044b7a7..60840231a 100644 --- a/src/message/Commands.h +++ b/src/message/Commands.h @@ -17,6 +17,8 @@ #include #include #include +#include +#include /****************************************************************************** * DEFINE @@ -42,6 +44,7 @@ enum CommandId: MessageId { DeviceRegisteredCmdId, DeviceAttachedCmdId, DeviceDetachedCmdId, + DeviceNetConfigCmdUpId, /* Thing commands */ LastValuesBeginCmdId, @@ -145,6 +148,11 @@ struct TimezoneCommandDown { } params; }; +struct DeviceNetConfigCmdUp { + Command c; + models::NetworkSetting params; +}; + union CommandDown { Command c; struct OtaUpdateCmdDown otaUpdateCmdDown; diff --git a/src/ota/implementation/OTAUnoR4.cpp b/src/ota/implementation/OTAUnoR4.cpp index 5ba12afa9..afe37b4a7 100644 --- a/src/ota/implementation/OTAUnoR4.cpp +++ b/src/ota/implementation/OTAUnoR4.cpp @@ -32,6 +32,7 @@ UNOR4OTACloudProcess::UNOR4OTACloudProcess(MessageStream *ms) } OTACloudProcessInterface::State UNOR4OTACloudProcess::resume(Message* msg) { + (void)msg; return OtaBegin; } @@ -57,22 +58,25 @@ OTACloudProcessInterface::State UNOR4OTACloudProcess::startOTA() { } OTACloudProcessInterface::State UNOR4OTACloudProcess::fetch() { - int ota_err = OTAUpdate::OTA_ERROR_NONE; - String fv = WiFi.firmwareVersion(); - if(fv >= "0.5.0") { + /* Firmware supports non blocking OTA */ + if (fv >= "0.5.0") { auto progress = ota.downloadProgress(); + if (progress < 0) { + return OtaDownloadFail; + } - if((millis() - context->lastReportTime) > 5000) { // Report the download progress each X millisecond + if ((millis() - context->lastReportTime) > 5000) { // Report the download progress each X millisecond DEBUG_VERBOSE("OTA Download Progress %d/%d", progress, context->downloadSize); reportStatus(progress); context->lastReportTime = millis(); } - if(progress < context->downloadSize) { + /* It is safe to cast progress here because we are sure that is positive */ + if ((size_t)progress < context->downloadSize) { return Fetch; - } else if(progress > context->downloadSize || progress < 0) { + } else if ((size_t)progress > context->downloadSize) { return OtaDownloadFail; } else { return FlashOTA; @@ -85,7 +89,6 @@ OTACloudProcessInterface::State UNOR4OTACloudProcess::fetch() { } DEBUG_VERBOSE("OTAUpdate::download() %d bytes downloaded", ota_download); - return FlashOTA; } } @@ -99,13 +102,18 @@ OTACloudProcessInterface::State UNOR4OTACloudProcess::flashOTA() { } /* Flash new firmware */ - if ((ota_err = ota.update(UPDATE_FILE_NAME)) != OTAUpdate::OTA_ERROR_NONE) { // This reboots the MCU + if ((ota_err = ota.update(UPDATE_FILE_NAME)) != OTAUpdate::OTA_ERROR_NONE) { DEBUG_VERBOSE("OTAUpdate::update() failed with %d", ota_err); return convertUnor4ErrorToState(ota_err); } + + /* This is never called because ota.uptade reboots the microcontroller */ + return Resume; } OTACloudProcessInterface::State UNOR4OTACloudProcess::reboot() { + /* This is never called; the microcontroller reboots in flashOTA state */ + return Resume; } void UNOR4OTACloudProcess::reset() { diff --git a/src/ota/interface/OTAInterface.cpp b/src/ota/interface/OTAInterface.cpp index e72d062d4..13644cabb 100644 --- a/src/ota/interface/OTAInterface.cpp +++ b/src/ota/interface/OTAInterface.cpp @@ -106,6 +106,7 @@ OTACloudProcessInterface::State OTACloudProcessInterface::otaBegin() { struct OtaBeginUp msg = { OtaBeginUpId, + {} }; SHA256 sha256_calc; @@ -199,6 +200,7 @@ void OTACloudProcessInterface::reportStatus(int32_t state_data) { struct OtaProgressCmdUp msg = { OtaProgressCmdUpId, + {} }; memcpy(msg.params.id, context->id, ID_SIZE); diff --git a/src/ota/interface/OTAInterfaceDefault.cpp b/src/ota/interface/OTAInterfaceDefault.cpp index fbea43e68..97389d430 100644 --- a/src/ota/interface/OTAInterfaceDefault.cpp +++ b/src/ota/interface/OTAInterfaceDefault.cpp @@ -14,8 +14,6 @@ #include "OTAInterfaceDefault.h" #include "../OTA.h" -static uint32_t crc_update(uint32_t crc, const void * data, size_t data_len); - OTADefaultCloudProcessInterface::OTADefaultCloudProcessInterface(MessageStream *ms, Client* client) : OTACloudProcessInterface(ms) , client(client) diff --git a/src/property/types/automation/CloudTelevision.h b/src/property/types/automation/CloudTelevision.h index 75967dd4f..f1d203d3b 100644 --- a/src/property/types/automation/CloudTelevision.h +++ b/src/property/types/automation/CloudTelevision.h @@ -226,8 +226,14 @@ class CloudTelevision : public Property { setAttribute(_cloud_value.swi, "swi"); setAttribute(_cloud_value.vol, "vol"); setAttribute(_cloud_value.mut, "mut"); +/* PlaybackCommands and InputValue are enum of type int so we can safely disable + * strict aliasing warnings here. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" setAttribute((int&)_cloud_value.pbc, "pbc"); setAttribute((int&)_cloud_value.inp, "inp"); +#pragma GCC diagnostic pop setAttribute(_cloud_value.cha, "cha"); } }; diff --git a/src/utility/time/RTCMillis.cpp b/src/utility/time/RTCMillis.cpp index 7ceb27436..0dc970e8d 100644 --- a/src/utility/time/RTCMillis.cpp +++ b/src/utility/time/RTCMillis.cpp @@ -21,7 +21,7 @@ #include "AIoTC_Config.h" -#if defined(HAS_NOTECARD) || defined(ARDUINO_ARCH_ESP8266) || defined (ARDUINO_RASPBERRY_PI_PICO_W) +#if !defined(BOARD_HAS_HW_RTC) #include #include "RTCMillis.h" @@ -61,4 +61,4 @@ unsigned long RTCMillis::get() return _last_rtc_update_value; } -#endif /* HAS_NOTECARD || ARDUINO_ARCH_ESP8266 || ARDUINO_RASPBERRY_PI_PICO_W */ +#endif /* BOARD_HAS_HW_RTC */ diff --git a/src/utility/time/RTCMillis.h b/src/utility/time/RTCMillis.h index 03d352941..543988071 100644 --- a/src/utility/time/RTCMillis.h +++ b/src/utility/time/RTCMillis.h @@ -18,7 +18,7 @@ #ifndef ARDUINO_IOT_CLOUD_RTC_MILLIS_H_ #define ARDUINO_IOT_CLOUD_RTC_MILLIS_H_ -#if defined(HAS_NOTECARD) || defined(ARDUINO_ARCH_ESP8266) || defined (ARDUINO_RASPBERRY_PI_PICO_W) +#if !defined(BOARD_HAS_HW_RTC) /************************************************************************************** * INCLUDE @@ -45,6 +45,6 @@ class RTCMillis }; -#endif /* HAS_NOTECARD || ARDUINO_ARCH_ESP8266 || ARDUINO_RASPBERRY_PI_PICO_W */ +#endif /* BOARD_HAS_HW_RTC */ #endif /* ARDUINO_IOT_CLOUD_RTC_MILLIS_H_ */ diff --git a/src/utility/time/TimeService.cpp b/src/utility/time/TimeService.cpp index fb7272fc4..9d88551df 100644 --- a/src/utility/time/TimeService.cpp +++ b/src/utility/time/TimeService.cpp @@ -1,24 +1,16 @@ /* - This file is part of ArduinoIoTCloud. + This file is part of the ArduinoIoTCloud library. - Copyright 2020 ARDUINO SA (http://www.arduino.cc/) + Copyright (c) 2020 Arduino SA - This software is released under the GNU General Public License version 3, - which covers the main part of arduino-cli. - The terms of this license can be found at: - https://www.gnu.org/licenses/gpl-3.0.en.html - - You can be released from the requirements of the above licenses by purchasing - a commercial license. Buying such a license is mandatory if you want to modify or - otherwise use the software for commercial activities involving the Arduino - software without disclosing the source code of your own applications. To purchase - a commercial license, send an email to license@arduino.cc. + 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 @@ -27,93 +19,121 @@ #include "NTPUtils.h" #include "TimeService.h" -#if defined(HAS_NOTECARD) || defined(ARDUINO_ARCH_ESP8266) || defined (ARDUINO_RASPBERRY_PI_PICO_W) +#if defined(BOARD_HAS_HW_RTC) + #if defined(ARDUINO_ARCH_SAMD) + #include + #endif + #if defined(ARDUINO_ARCH_MBED) + #include + #endif + #if defined(ARDUINO_ARCH_RENESAS) + #include + #endif +#else #include "RTCMillis.h" -#elif defined(ARDUINO_ARCH_SAMD) - #include -#elif defined(ARDUINO_ARCH_MBED) - #include -#elif defined(ARDUINO_ARCH_RENESAS) - #include "RTC.h" #endif -/************************************************************************************** +/****************************************************************************** * GLOBAL VARIABLES - **************************************************************************************/ + ******************************************************************************/ -#if defined(HAS_NOTECARD) || defined(ARDUINO_ARCH_ESP8266) || defined (ARDUINO_RASPBERRY_PI_PICO_W) +#if !defined(BOARD_HAS_HW_RTC) RTCMillis rtc; -#elif defined(ARDUINO_ARCH_SAMD) +#endif + +#if defined(BOARD_HAS_HW_RTC) && defined(ARDUINO_ARCH_SAMD) RTCZero rtc; #endif -/************************************************************************************** +/****************************************************************************** * INTERNAL FUNCTION DECLARATION - **************************************************************************************/ + ******************************************************************************/ time_t cvt_time(char const * time); -#if defined(HAS_NOTECARD) -void notecard_initRTC(); -void notecard_setRTC(unsigned long time); -unsigned long notecard_getRTC(); -#else - -#ifdef ARDUINO_ARCH_SAMD -void samd_initRTC(); -void samd_setRTC(unsigned long time); -unsigned long samd_getRTC(); -#endif - -#ifdef ARDUINO_ARCH_MBED -void mbed_initRTC(); -void mbed_setRTC(unsigned long time); -unsigned long mbed_getRTC(); -#endif - -#ifdef ARDUINO_ARCH_ESP32 -void esp32_initRTC(); -void esp32_setRTC(unsigned long time); -unsigned long esp32_getRTC(); -#endif - -#ifdef ARDUINO_ARCH_ESP8266 -void esp8266_initRTC(); -void esp8266_setRTC(unsigned long time); -unsigned long esp8266_getRTC(); -#endif - -#ifdef ARDUINO_ARCH_RENESAS -void renesas_initRTC(); -void renesas_setRTC(unsigned long time); -unsigned long renesas_getRTC(); -#endif +/****************************************************************************** + * RTC PRIVATE FUNCTION DEFINITION + ******************************************************************************/ -#ifdef ARDUINO_RASPBERRY_PI_PICO_W -void pico_w_initRTC(); -void pico_w_setRTC(unsigned long time); -unsigned long pico_w_getRTC(); +#if defined(BOARD_HAS_HW_RTC) + #if defined(ARDUINO_ARCH_SAMD) +static inline void _initRTC() { + rtc.begin(); +} +static inline void _setRTC(unsigned long time) { + rtc.setEpoch(time); +} +static inline unsigned long _getRTC() { + return rtc.getEpoch(); +} + #endif + #if defined(ARDUINO_ARCH_MBED) +static inline void _initRTC() { + /* Nothing to do */ +} +static inline void _setRTC(unsigned long time) { + set_time(time); +} +static inline unsigned long _getRTC() { + return time(NULL); +} + #endif + #if defined(ARDUINO_ARCH_RENESAS) +static inline void _initRTC() { + RTC.begin(); +} +static inline void _setRTC(unsigned long time) { + RTCTime t(time); + RTC.setTime(t); +} +static inline unsigned long _getRTC() { + RTCTime t; + RTC.getTime(t); + return t.getUnixTime(); +} + #endif + #if defined(ARDUINO_ARCH_ESP32) +static inline void _initRTC() { + //configTime(0, 0, "time.arduino.cc", "pool.ntp.org", "time.nist.gov"); +} +static inline void _setRTC(unsigned long time) { + const timeval epoch = {(time_t)time, 0}; + settimeofday(&epoch, 0); +} +static inline unsigned long _getRTC() { + return time(NULL); +} + #endif +#else /* !BOARD_HAS_HW_RTC */ + #pragma message "No hardware RTC implementation found, using soft RTC" +static inline void _initRTC() { + rtc.begin(); +} +static inline void _setRTC(unsigned long time) { + rtc.set(time); +} +static inline unsigned long _getRTC() { + return rtc.get(); +} #endif -#endif /* HAS_NOTECARD */ - -/************************************************************************************** +/****************************************************************************** * DEFINES - **************************************************************************************/ + ******************************************************************************/ #define EPOCH_AT_COMPILE_TIME cvt_time(__DATE__) -/************************************************************************************** +/****************************************************************************** * CONSTANTS - **************************************************************************************/ + ******************************************************************************/ /* Default NTP synch is scheduled each 24 hours from startup */ static time_t const TIMESERVICE_NTP_SYNC_TIMEOUT_ms = DAYS * 1000; static time_t const EPOCH = 0; -/************************************************************************************** +/****************************************************************************** * CTOR/DTOR - **************************************************************************************/ + ******************************************************************************/ TimeServiceClass::TimeServiceClass() : _con_hdl(nullptr) @@ -128,9 +148,9 @@ TimeServiceClass::TimeServiceClass() } -/************************************************************************************** +/****************************************************************************** * PUBLIC MEMBER FUNCTIONS - **************************************************************************************/ + ******************************************************************************/ void TimeServiceClass::begin(ConnectionHandler * con_hdl) { @@ -286,9 +306,9 @@ unsigned long TimeServiceClass::getTimeFromString(const String& input) return mktime(&t); } -/************************************************************************************** +/****************************************************************************** * PRIVATE MEMBER FUNCTIONS - **************************************************************************************/ + ******************************************************************************/ #if defined(HAS_NOTECARD) || defined(HAS_TCP) bool TimeServiceClass::connected() @@ -349,70 +369,22 @@ bool TimeServiceClass::isTimeZoneOffsetValid(long const offset) void TimeServiceClass::initRTC() { -#if defined (HAS_NOTECARD) - notecard_initRTC(); -#elif defined (ARDUINO_ARCH_SAMD) - samd_initRTC(); -#elif defined (ARDUINO_ARCH_MBED) - mbed_initRTC(); -#elif defined (ARDUINO_ARCH_ESP32) - esp32_initRTC(); -#elif defined (ARDUINO_ARCH_ESP8266) - esp8266_initRTC(); -#elif defined (ARDUINO_ARCH_RENESAS) - renesas_initRTC(); -#elif defined (ARDUINO_RASPBERRY_PI_PICO_W) - pico_w_initRTC(); -#else - #error "RTC not available for this architecture" -#endif + _initRTC(); } void TimeServiceClass::setRTC(unsigned long time) { -#if defined (HAS_NOTECARD) - notecard_setRTC(time); -#elif defined (ARDUINO_ARCH_SAMD) - samd_setRTC(time); -#elif defined (ARDUINO_ARCH_MBED) - mbed_setRTC(time); -#elif defined (ARDUINO_ARCH_ESP32) - esp32_setRTC(time); -#elif defined (ARDUINO_ARCH_ESP8266) - esp8266_setRTC(time); -#elif defined (ARDUINO_ARCH_RENESAS) - renesas_setRTC(time); -#elif defined (ARDUINO_RASPBERRY_PI_PICO_W) - pico_w_setRTC(time); -#else - #error "RTC not available for this architecture" -#endif + _setRTC(time); } unsigned long TimeServiceClass::getRTC() { -#if defined (HAS_NOTECARD) - return notecard_getRTC(); -#elif defined (ARDUINO_ARCH_SAMD) - return samd_getRTC(); -#elif defined (ARDUINO_ARCH_MBED) - return mbed_getRTC(); -#elif defined (ARDUINO_ARCH_ESP32) - return esp32_getRTC(); -#elif defined (ARDUINO_ARCH_ESP8266) - return esp8266_getRTC(); -#elif defined (ARDUINO_ARCH_RENESAS) - return renesas_getRTC(); -#elif defined (ARDUINO_RASPBERRY_PI_PICO_W) - return pico_w_getRTC(); -#else - #error "RTC not available for this architecture" -#endif + return _getRTC(); } -/************************************************************************************** +/****************************************************************************** * INTERNAL FUNCTION DEFINITION - **************************************************************************************/ + ******************************************************************************/ time_t cvt_time(char const * time) { @@ -450,131 +422,6 @@ time_t cvt_time(char const * time) return build_time; } -#ifdef HAS_NOTECARD -void notecard_initRTC() -{ - rtc.begin(); -} - -void notecard_setRTC(unsigned long time) -{ - rtc.set(time); -} - -unsigned long notecard_getRTC() -{ - return rtc.get(); -} -#else - -#ifdef ARDUINO_ARCH_SAMD -void samd_initRTC() -{ - rtc.begin(); -} - -void samd_setRTC(unsigned long time) -{ - rtc.setEpoch(time); -} - -unsigned long samd_getRTC() -{ - return rtc.getEpoch(); -} -#endif - -#ifdef ARDUINO_ARCH_MBED -void mbed_initRTC() -{ - /* Nothing to do */ -} - -void mbed_setRTC(unsigned long time) -{ - set_time(time); -} - -unsigned long mbed_getRTC() -{ - return time(NULL); -} -#endif - -#ifdef ARDUINO_ARCH_ESP32 -void esp32_initRTC() -{ - //configTime(0, 0, "time.arduino.cc", "pool.ntp.org", "time.nist.gov"); -} - -void esp32_setRTC(unsigned long time) -{ - const timeval epoch = {(time_t)time, 0}; - settimeofday(&epoch, 0); -} - -unsigned long esp32_getRTC() -{ - return time(NULL); -} -#endif - -#ifdef ARDUINO_ARCH_ESP8266 -void esp8266_initRTC() -{ - rtc.begin(); -} - -void esp8266_setRTC(unsigned long time) -{ - rtc.set(time); -} - -unsigned long esp8266_getRTC() -{ - return rtc.get(); -} -#endif - -#ifdef ARDUINO_ARCH_RENESAS -void renesas_initRTC() -{ - RTC.begin(); -} - -void renesas_setRTC(unsigned long time) -{ - RTCTime t(time); - RTC.setTime(t); -} - -unsigned long renesas_getRTC() -{ - RTCTime t; - RTC.getTime(t); - return t.getUnixTime(); -} -#endif - -#ifdef ARDUINO_RASPBERRY_PI_PICO_W -void pico_w_initRTC() -{ - rtc.begin(); -} - -void pico_w_setRTC(unsigned long time) -{ - rtc.set(time); -} - -unsigned long pico_w_getRTC() -{ - return rtc.get(); -} -#endif - -#endif /* HAS_NOTECARD */ - /****************************************************************************** * EXTERN DEFINITION ******************************************************************************/ diff --git a/src/utility/time/TimeService.h b/src/utility/time/TimeService.h index 71656d948..d4a455cef 100644 --- a/src/utility/time/TimeService.h +++ b/src/utility/time/TimeService.h @@ -1,26 +1,19 @@ /* - This file is part of ArduinoIoTCloud. + This file is part of the ArduinoIoTCloud library. - Copyright 2020 ARDUINO SA (http://www.arduino.cc/) + Copyright (c) 2020 Arduino SA - This software is released under the GNU General Public License version 3, - which covers the main part of arduino-cli. - The terms of this license can be found at: - https://www.gnu.org/licenses/gpl-3.0.en.html - - You can be released from the requirements of the above licenses by purchasing - a commercial license. Buying such a license is mandatory if you want to modify or - otherwise use the software for commercial activities involving the Arduino - software without disclosing the source code of your own applications. To purchase - a commercial license, send an email to license@arduino.cc. + 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/. */ #ifndef ARDUINO_IOT_CLOUD_TIME_SERVICE_H_ #define ARDUINO_IOT_CLOUD_TIME_SERVICE_H_ -/************************************************************************************** +/****************************************************************************** * INCLUDE - **************************************************************************************/ + ******************************************************************************/ #include #include @@ -31,9 +24,9 @@ typedef unsigned long(*syncTimeFunctionPtr)(void); -/************************************************************************************** +/****************************************************************************** * CLASS DECLARATION - **************************************************************************************/ + ******************************************************************************/ class TimeServiceClass {