diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 4e286b9f..c914adc1 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -103,10 +103,12 @@ jobs: - name: arduino:samd libraries: | - name: ArduinoECCX08 + - name: Blues Wireless Notecard - name: RTCZero - name: WiFi101 - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git sketch-paths: | + - examples/ArduinoIoTCloud-Notecard - examples/utility/Provisioning # MKR WiFi 1010, Nano 33 IoT, Nano RP2040 Connect - board: @@ -117,12 +119,14 @@ jobs: - name: arduino:mbed_nano libraries: | - name: ArduinoECCX08 + - name: Blues Wireless Notecard - name: RTCZero - name: WiFiNINA - name: Arduino_JSON - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git sketch-paths: | - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard - examples/utility/Provisioning - examples/utility/SelfProvisioning - board: @@ -132,10 +136,12 @@ jobs: - name: arduino:samd libraries: | - name: ArduinoECCX08 + - name: Blues Wireless Notecard - name: RTCZero - name: MKRWAN - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git - sketch-paths: + sketch-paths: | + - examples/ArduinoIoTCloud-Notecard # GSM boards - board: type: gsm @@ -144,10 +150,12 @@ jobs: - name: arduino:samd libraries: | - name: ArduinoECCX08 + - name: Blues Wireless Notecard - name: RTCZero - name: MKRGSM - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git sketch-paths: | + - examples/ArduinoIoTCloud-Notecard - examples/utility/Provisioning # NB boards - board: @@ -157,10 +165,12 @@ jobs: - name: arduino:samd libraries: | - name: ArduinoECCX08 + - name: Blues Wireless Notecard - name: RTCZero - name: MKRNB - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git sketch-paths: | + - examples/ArduinoIoTCloud-Notecard - examples/utility/Provisioning # Portenta - board: @@ -171,8 +181,10 @@ jobs: libraries: | - name: ArduinoECCX08 - name: Arduino_Cellular + - name: Blues Wireless Notecard sketch-paths: | - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard - examples/utility/Provisioning # Nicla Vision - board: @@ -181,8 +193,10 @@ jobs: # Install mbed_nicla platform via Boards Manager - name: arduino:mbed_nicla libraries: | + - name: Blues Wireless Notecard sketch-paths: | - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard - examples/utility/Provisioning # Opta - board: @@ -192,8 +206,10 @@ jobs: - name: arduino:mbed_opta libraries: | - name: ArduinoECCX08 + - name: Blues Wireless Notecard sketch-paths: | - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard - examples/utility/Provisioning # GIGA - board: @@ -203,8 +219,10 @@ jobs: - name: arduino:mbed_giga libraries: | - name: ArduinoECCX08 + - name: Blues Wireless Notecard sketch-paths: | - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard - examples/utility/Provisioning # Portenta C33 - board: @@ -214,7 +232,9 @@ jobs: - name: arduino:renesas_portenta libraries: | - name: Arduino_Cellular + - name: Blues Wireless Notecard sketch-paths: | + - examples/ArduinoIoTCloud-Notecard - examples/utility/Provisioning # UNO R4 WiFi - board: @@ -222,20 +242,31 @@ jobs: platforms: | # Install renesas_uno platform via Boards Manager - name: arduino:renesas_uno + libraries: | + - name: Blues Wireless Notecard + sketch-paths: | + - examples/ArduinoIoTCloud-Notecard # Nano ESP32 - board: type: arduino_esp32 platforms: | # Install arduino_esp32 platform via Boards Manager - name: arduino:esp32 + libraries: | + - name: Blues Wireless Notecard sketch-paths: | - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard # Edge Control - board: type: mbed_edge platforms: | # Install mbed_edge platform via Boards Manager - name: arduino:mbed_edge + libraries: | + - name: Blues Wireless Notecard + sketch-paths: | + - examples/ArduinoIoTCloud-Notecard # ESP8266 boards - board: type: esp8266 @@ -254,8 +285,11 @@ jobs: # Install ESP32 platform via Boards Manager - name: esp32:esp32 source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + libraries: | + - name: Blues Wireless Notecard sketch-paths: | - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard steps: - name: Checkout diff --git a/README.md b/README.md index 83a7e70c..4b248e78 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ The `ArduinoIoTCloud` library is the central element of the firmware enabling ce * **5G**: [`MKR NB 1500`](https://store.arduino.cc/arduino-mkr-nb-1500-1413) * **LoRa**: [`MKR WAN 1300/1310`](https://store.arduino.cc/mkr-wan-1310) * **Ethernet**: [`Portenta H7`](https://store.arduino.cc/products/portenta-h7) + [`Vision Shield Ethernet`](https://store.arduino.cc/products/arduino-portenta-vision-shield-ethernet), [`Max Carrier`](https://store.arduino.cc/products/portenta-max-carrier), [`Breakout`](https://store.arduino.cc/products/arduino-portenta-breakout), [`Portenta Machine Control`](https://store.arduino.cc/products/arduino-portenta-machine-control), [`OPTA WiFi`](https://store.arduino.cc/products/opta-wifi), [`OPTA RS485`](https://store.arduino.cc/products/opta-rs485), [`OPTA Lite`](https://store.arduino.cc/products/opta-lite), [`Portenta C33`](https://store.arduino.cc/products/portenta-c33) + [`Vision Shield Ethernet`](https://store.arduino.cc/products/arduino-portenta-vision-shield-ethernet) +* **Notecard**: [Provides Cellular/LoRa/Satellite/Wi-Fi to any modern board/architecture](examples/ArduinoIoTCloud-Notecard/README.md) ### How? 1) Register your Arduino IoT Cloud capable board via [Arduino IoT Cloud](https://create.arduino.cc/iot) (Devices Section). @@ -70,7 +71,7 @@ void onLedChange() { ### FAQ #### Watchdog -The [1.0.0](https://github.com/arduino-libraries/ArduinoIoTCloud/releases/tag/1.0.0) release of this library adds watchdog functionality to all ATSAMD21G18 based cloud connected boards. A watchdog is simply an electronic timer counting down from a preset start value which, upon reaching zero, triggers a reset of the microcontroller. It can be used to automatically recover from temporary hardware faults or unrecoverable software errors. In order to avoid the watchdog from reaching zero the countdown timer needs to be regularly re-set to its start value. This is happening within `ArduinoCloud.update()` which is periodically called at the start of the `loop()` function. Although the watchdog is automatically enabled it can be disabled by setting the second parameter of `ArduinoCloud.begin(...)` to `false`: +The [1.0.0](https://github.com/arduino-libraries/ArduinoIoTCloud/releases/tag/1.0.0) release of this library adds watchdog functionality to all ATSAMD21G18 based cloud connected boards. A watchdog is simply an electronic timer counting down from a preset start value which, upon reaching zero, triggers a reset of the microcontroller. It can be used to automatically recover from temporary hardware faults or unrecoverable software errors. In order to avoid the watchdog from reaching zero the countdown timer needs to be regularly re-set to its start value. This is happening within `ArduinoCloud.update()` which is periodically called at the start of the `loop()` function. Although the watchdog is automatically enabled it can be disabled by setting the second parameter of `ArduinoCloud.begin(...)` to `false`: ```C++ ArduinoCloud.begin(ArduinoIoTPreferredConnection, false). ``` @@ -95,5 +96,3 @@ Boards can authenticate to the ArduinoIoTCloud servers using 3 methods: * `DEVICE_CERTIFICATE` and `PRIVATE_KEY`. This values are stored inside the board secure element during the device provisioning phase. Boards that are using this method are: [`MKR 1000`](https://store.arduino.cc/arduino-mkr1000-wifi), [`MKR WiFi 1010`](https://store.arduino.cc/arduino-mkr-wifi-1010), [`MKR GSM 1400`](https://store.arduino.cc/arduino-mkr-gsm-1400-1415), [`MKR NB 1500`](https://store.arduino.cc/arduino-mkr-nb-1500-1413), [`Nano 33 IoT`](https://store.arduino.cc/arduino-nano-33-iot), [`Portenta H7`](https://store.arduino.cc/portenta-h7), [`Nano RP2040 Connect`](https://store.arduino.cc/products/arduino-nano-rp2040-connect), [`Nicla Vision`](https://store.arduino.cc/products/nicla-vision), [`OPTA WiFi`](https://store.arduino.cc/products/opta-wifi), [`OPTA RS485`](https://store.arduino.cc/products/opta-rs485), [`OPTA Lite`](https://store.arduino.cc/products/opta-lite), [`GIGA R1 WiFi`](https://store.arduino.cc/products/giga-r1-wifi), [`Portenta C33`](https://store.arduino.cc/products/portenta-c33) * `APP_EUI` and `APP_KEY`. This values are defined in the `thingProperties.h` file and included in the Sketch. Boards that are using this method are: [`MKR WAN 1300/1310`](https://store.arduino.cc/mkr-wan-1310) - - \ No newline at end of file diff --git a/examples/ArduinoIoTCloud-Notecard/ArduinoIoTCloud-Notecard.ino b/examples/ArduinoIoTCloud-Notecard/ArduinoIoTCloud-Notecard.ino new file mode 100644 index 00000000..a31c404b --- /dev/null +++ b/examples/ArduinoIoTCloud-Notecard/ArduinoIoTCloud-Notecard.ino @@ -0,0 +1,73 @@ +/* + This sketch demonstrates how to exchange data between your board and the + Arduino IoT Cloud, while using the Notecard for wireless communication. + + * Connect a potentiometer (or other analog sensor) to A0. + * When the potentiometer (or sensor) value changes the data is sent to the Cloud. + * When you flip the switch in the Cloud dashboard the onboard LED lights gets turned ON or OFF. + + IMPORTANT: + This sketch works with any Wi-Fi, Cellular, LoRa or Satellite enabled Notecard. + + The full list of compatible boards can be found here: + - https://github.com/arduino-libraries/ArduinoIoTCloud#what +*/ + +#include +#include "thingProperties.h" + +#if !defined(LED_BUILTIN) && !defined(ARDUINO_NANO_ESP32) +static int const LED_BUILTIN = 2; +#endif + +/* + * Choose an interrupt capable pin to reduce polling and improve + * the overall responsiveness of the ArduinoIoTCloud library + */ +// #define ATTN_PIN 9 + +void setup() { + /* Initialize serial and wait up to 5 seconds for port to open */ + Serial.begin(9600); + for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime <= 5000); ) { } + + /* Set the debug message level: + * - DBG_ERROR: Only show error messages + * - DBG_WARNING: Show warning and error messages + * - DBG_INFO: Show info, warning, and error messages + * - DBG_DEBUG: Show debug, info, warning, and error messages + * - DBG_VERBOSE: Show all messages + */ + setDebugMessageLevel(DBG_INFO); + + /* Configure LED pin as an output */ + pinMode(LED_BUILTIN, OUTPUT); + + /* This function takes care of connecting your sketch variables to the ArduinoIoTCloud object */ + initProperties(); + + /* Initialize Arduino IoT Cloud library */ +#ifndef ATTN_PIN + ArduinoCloud.begin(ArduinoIoTPreferredConnection); + ArduinoCloud.setNotecardPollingInterval(3000); // default: 1000ms, min: 250ms +#else + ArduinoCloud.begin(ArduinoIoTPreferredConnection, ATTN_PIN); +#endif + + ArduinoCloud.printDebugInfo(); +} + +void loop() { + ArduinoCloud.update(); + potentiometer = analogRead(A0); + seconds = millis() / 1000; +} + +/* + * 'onLedChange' is called when the "led" property of your Thing changes + */ +void onLedChange() { + Serial.print("LED set to "); + Serial.println(led); + digitalWrite(LED_BUILTIN, led); +} diff --git a/examples/ArduinoIoTCloud-Notecard/README.md b/examples/ArduinoIoTCloud-Notecard/README.md new file mode 100644 index 00000000..a17d954c --- /dev/null +++ b/examples/ArduinoIoTCloud-Notecard/README.md @@ -0,0 +1,78 @@ +Notecard Connectivity +===================== + +The Notecard is a wireless, secure abstraction for device connectivity, that can +be used to enable _ANY*_ device with I2C, or UART, to connect to the Arduino IoT +Cloud via cellular, LoRa, satellite or Wi-Fi! + +As a result, your existing device architecture can now have first class support +in the Arduino IoT Cloud, by using a Notecard as a secure communication channel. + +> \*_While any device with I2C/UART may use the Notecard, the Arduino IoT Cloud +> library is not supported by the AVR toolchain. Therefore, devices based on the +> AVR architecture cannot access the Arduino IoT Cloud via the Notecard._ +> +> _However, any device (including AVR), may use the Notecard library to send data +> to Notehub, then that data may be routed to any endpoint of your choosing. See the +> [Notecard Routing Guide](https://dev.blues.io/guides-and-tutorials/routing-data-to-cloud) +> for more information..._ + +Wireless Connectivity Options +----------------------------- + +- [Cellular](https://shop.blues.com/collections/notecard/products/notecard-cellular) +- [Cellular + Wi-Fi](https://shop.blues.com/collections/notecard/products/notecard-cell-wifi) +- [Wi-Fi](https://shop.blues.com/collections/notecard/products/wifi-notecard) +- [LoRa](https://shop.blues.com/collections/notecard/products/notecard-lora) +- [Satellite](https://shop.blues.com/products/starnote) + +How it Works +------------ + +**Architecture Diagram:** + +``` +-------- ------------ ----------- ----------- +| | | | | | | | +| Host | | | Secure | | | Arduino | +| MCU |------| Notecard | ( ( Wireless ) ) | Notehub |------| IoT | +| | | | Protocol | | | Cloud | +|______| |__________| |_________| |_________| +``` + +Getting Started +--------------- + +### Setup a Notehub Account + +Using the Notecard only requires a couple of easy steps: + +1. [Purchase a Notecard](https://shop.blues.com/collections/notecard) (and +[Notecarrier](https://shop.blues.com/collections/notecarrier)) that fits the +needs of your device. + > _**NOTE:** We recommend starting with our [Dev Kit](https://shop.blues.com/products/blues-global-starter-kit) + > if you are unsure._ +1. [Setup a Notehub account](https://dev.blues.io/quickstart/notecard-quickstart/notecard-and-notecarrier-f/#set-up-notehub). + > _**NOTE:** Notehub accounts are free (no credit card required)._ +1. [Create a project on your Notehub account](https://dev.blues.io/quickstart/notecard-quickstart/notecard-and-notecarrier-f/#create-a-notehub-project). +1. In `thingProperties.h`, replace "com.domain.you:product" (from +`NOTECARD_PRODUCT_UID`) with the ProductUID of your new Notehub project. + +### Power-up the Device + +1. [Connect the Notecard to your Host MCU](https://dev.blues.io/quickstart/notecard-quickstart/notecard-and-notecarrier-f/#connect-your-notecard-and-notecarrier) +1. Flash the `ArduinoIoTCloud-Notecard` example sketch to your device. You +should see the device reporting itself as online in your [Notehub Project](https://notehub.io). + +### Associate Notecard to Arduino IoT Cloud + +1. Create a "MANUAL Device" in the Arduino IoT Cloud, then [add environment +variables for the "Device ID" and "Secret Key" to your Notecard in Notehub](https://dev.blues.io/guides-and-tutorials/notecard-guides/understanding-environment-variables/#setting-a-notehub-device-variable). + - `_sn`: \ + - `_secret_key`: \ + +### More Information + +For more information about the Notecard and Notehub in general, please see our +[Quickstart Guide](https://dev.blues.io/quickstart/) for a general overview of +how the Notecard and Notehub are designed to work. diff --git a/examples/ArduinoIoTCloud-Notecard/arduino_secrets.h b/examples/ArduinoIoTCloud-Notecard/arduino_secrets.h new file mode 100644 index 00000000..43e3b76e --- /dev/null +++ b/examples/ArduinoIoTCloud-Notecard/arduino_secrets.h @@ -0,0 +1,7 @@ +#include + +/* If provided, the Wi-Fi Credentials will be passed along to the Notecard. If + * the Notecard supports Wi-Fi, it will attempt to connect to the network using + * these credentials, if not, the Notecard will safely ignore these values. */ +#define SECRET_WIFI_SSID "YOUR_WIFI_NETWORK_NAME" +#define SECRET_WIFI_PASS "YOUR_WIFI_PASSWORD" diff --git a/examples/ArduinoIoTCloud-Notecard/thingProperties.h b/examples/ArduinoIoTCloud-Notecard/thingProperties.h new file mode 100644 index 00000000..cbcaf4da --- /dev/null +++ b/examples/ArduinoIoTCloud-Notecard/thingProperties.h @@ -0,0 +1,36 @@ +#include + +#include +#include +#include "arduino_secrets.h" + +/* The Notecard can provide connectivity to almost any board via ESLOV (I2C) + * or UART. An empty string (or the default value provided below) will not + * override the Notecard's existing configuration. + * Learn more at: https://dev.blues.io */ +#define NOTECARD_PRODUCT_UID "com.domain.you:product" + +/* Uncomment the following line to use the Notecard over UART */ +// #define UART_INTERFACE Serial1 + +void onLedChange(); + +bool led; +int potentiometer; +int seconds; + +#ifndef UART_INTERFACE +NotecardConnectionHandler ArduinoIoTPreferredConnection(NOTECARD_PRODUCT_UID); +#else +NotecardConnectionHandler ArduinoIoTPreferredConnection(NOTECARD_PRODUCT_UID, UART_INTERFACE); +#endif + +void initProperties() { + ArduinoCloud.addProperty(led, Permission::ReadWrite).onUpdate(onLedChange); + ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10); + ArduinoCloud.addProperty(seconds, Permission::Read).publishEvery(5 * MINUTES); + + if (::strncmp(SECRET_WIFI_SSID, "YOUR_WIFI_NETWORK_NAME", sizeof(SECRET_WIFI_SSID))) { + ArduinoIoTPreferredConnection.setWiFiCredentials(SECRET_WIFI_SSID, SECRET_WIFI_PASS); + } +} diff --git a/keywords.txt b/keywords.txt index a2d04e19..ffea51e1 100644 --- a/keywords.txt +++ b/keywords.txt @@ -25,6 +25,8 @@ getThingId KEYWORD2 setDeviceId KEYWORD2 getDeviceId KEYWORD2 getConnection KEYWORD2 +getInternalTime KEYWORD2 +getLocalTime KEYWORD2 addCallback KEYWORD2 addProperty KEYWORD2 @@ -46,6 +48,14 @@ getBrokerPort KEYWORD2 setOTAStorage KEYWORD2 reconnect KEYWORD2 +# ArduinoIoTCloudNotecard.h +begin KEYWORD2 +setNotecardPollingInterval KEYWORD2 + +# OTA +onOTARequestCallbackFunc KEYWORD2 +onOTARequestCb KEYWORD2 + ####################################### # Constants (LITERAL1) ####################################### diff --git a/library.properties b/library.properties index 267c5b40..ac24f20f 100644 --- a/library.properties +++ b/library.properties @@ -6,6 +6,6 @@ sentence=This library allows connecting to the Arduino IoT Cloud service. paragraph=It provides a ConnectionManager to handle connection/disconnection, property-change updates and events callbacks. The supported boards are MKR GSM, MKR1000 and WiFi101. category=Communication url=https://github.com/arduino-libraries/ArduinoIoTCloud -architectures=mbed,samd,esp8266,mbed_nano,mbed_portenta,mbed_nicla,esp32,mbed_opta,mbed_giga,renesas_portenta,renesas_uno,mbed_edge +architectures=mbed,samd,esp8266,mbed_nano,mbed_portenta,mbed_nicla,esp32,mbed_opta,mbed_giga,renesas_portenta,renesas_uno,mbed_edge,stm32 includes=ArduinoIoTCloud.h depends=Arduino_ConnectionHandler,Arduino_DebugUtils,Arduino_SecureElement,ArduinoMqttClient,ArduinoECCX08,RTCZero,Adafruit SleepyDog Library,ArduinoHttpClient diff --git a/src/AIoTC_Config.h b/src/AIoTC_Config.h index 2efb7021..2412be10 100644 --- a/src/AIoTC_Config.h +++ b/src/AIoTC_Config.h @@ -18,6 +18,12 @@ #ifndef ARDUINO_AIOTC_CONFIG_H_ #define ARDUINO_AIOTC_CONFIG_H_ +#if defined __has_include + #if __has_include () + #define HAS_NOTECARD + #endif +#endif + #include /****************************************************************************** @@ -52,6 +58,8 @@ * AUTOMATICALLY CONFIGURED DEFINES ******************************************************************************/ +#if !defined(HAS_NOTECARD) + #if defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT) #define OTA_STORAGE_SNU (1) #else @@ -114,11 +122,6 @@ #define HAS_TCP #endif -#if defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_OPTA) || defined(ARDUINO_GIGA) - #define BEAR_SSL_CLIENT_IBUF_SIZE (16384 + 325) // Allows download from storage API - #define BOARD_STM32H7 -#endif - #if defined(ARDUINO_NANO_RP2040_CONNECT) #define BEAR_SSL_CLIENT_IBUF_SIZE (16384 + 325) // Allows download from storage API #endif @@ -138,6 +141,13 @@ #define BOARD_HAS_SECURE_ELEMENT #endif +#endif // HAS_NOTECARD + +#if defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_OPTA) || defined(ARDUINO_GIGA) + #define BEAR_SSL_CLIENT_IBUF_SIZE (16384 + 325) // Allows download from storage API + #define BOARD_STM32H7 +#endif + /****************************************************************************** * CONSTANTS ******************************************************************************/ @@ -146,12 +156,19 @@ #define AIOT_CONFIG_LPWAN_UPDATE_RETRY_DELAY_ms (10000UL) #endif -#if defined(HAS_TCP) +#if defined(HAS_NOTECARD) || defined(HAS_TCP) #define AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms (1000UL) #define AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms (32000UL) - #define AIOT_CONFIG_THING_ID_REQUEST_RETRY_DELAY_ms (2000UL) - #define AIOT_CONFIG_MAX_THING_ID_REQUEST_RETRY_DELAY_ms (32000UL) + #if defined(HAS_NOTECARD) + // 10x the standard delays for Notecard + #define AIOT_CONFIG_THING_ID_REQUEST_RETRY_DELAY_ms (20000UL) + #define AIOT_CONFIG_MAX_THING_ID_REQUEST_RETRY_DELAY_ms (320000UL) + #else + #define AIOT_CONFIG_THING_ID_REQUEST_RETRY_DELAY_ms (2000UL) + #define AIOT_CONFIG_MAX_THING_ID_REQUEST_RETRY_DELAY_ms (32000UL) + #endif + #define AIOT_CONFIG_THING_ID_REQUEST_MAX_RETRY_CNT (10UL) #define AIOT_CONFIG_DEVICE_REGISTERED_RETRY_DELAY_k (10UL) diff --git a/src/ArduinoIoTCloud.h b/src/ArduinoIoTCloud.h index 7c0bcc5f..7cea9382 100644 --- a/src/ArduinoIoTCloud.h +++ b/src/ArduinoIoTCloud.h @@ -161,7 +161,9 @@ class ArduinoIoTCloudClass OnCloudEventCallback _cloud_event_callback[3]; }; -#ifdef HAS_TCP +#if defined(HAS_NOTECARD) + #include "ArduinoIoTCloudNotecard.h" +#elif defined(HAS_TCP) #include "ArduinoIoTCloudTCP.h" #elif defined(HAS_LORA) #include "ArduinoIoTCloudLPWAN.h" diff --git a/src/ArduinoIoTCloudDevice.cpp b/src/ArduinoIoTCloudDevice.cpp index cfef518e..574ad3a7 100644 --- a/src/ArduinoIoTCloudDevice.cpp +++ b/src/ArduinoIoTCloudDevice.cpp @@ -14,7 +14,7 @@ #include -#ifdef HAS_TCP +#if defined(HAS_NOTECARD) || defined(HAS_TCP) #include "ArduinoIoTCloudDevice.h" #include "interfaces/CloudProcess.h" @@ -146,4 +146,4 @@ ArduinoCloudDevice::State ArduinoCloudDevice::handleDisconnected() { return State::Disconnected; } -#endif /* HAS_TCP */ +#endif /* HAS_NOTECARD || HAS_TCP */ diff --git a/src/ArduinoIoTCloudNotecard.cpp b/src/ArduinoIoTCloudNotecard.cpp new file mode 100644 index 00000000..aeab0b76 --- /dev/null +++ b/src/ArduinoIoTCloudNotecard.cpp @@ -0,0 +1,481 @@ +/* + This file is part of the ArduinoIoTCloud library. + + Copyright 2024 Blues (http://www.blues.com/) + + 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 "AIoTC_Config.h" + +#if defined(HAS_NOTECARD) + +#include "ArduinoIoTCloudNotecard.h" + +#include +#include + +#include +#include + +#include "cbor/CBOREncoder.h" +#include "cbor/MessageDecoder.h" +#include "cbor/MessageEncoder.h" + +/****************************************************************************** + * CONSTANTS + ******************************************************************************/ + +static size_t const CBOR_MSG_BUFFER_SIZE = 256; +static size_t const CBOR_LORA_PAYLOAD_MAX_SIZE = 236; +static size_t const DEFAULT_READ_INTERVAL_MS = 1000; // 1 second +static size_t const FAILSAFE_READ_INTERVAL_MS = 15000; // 15 seconds + +/****************************************************************************** + * LOCAL MODULE FUNCTIONS + ******************************************************************************/ + +unsigned long getTime() +{ + return ArduinoCloud.getInternalTime(); +} + +void ISR_dataAvailable(void) +{ + ArduinoCloud._data_available = true; +} + +/****************************************************************************** + * CTOR/DTOR + ******************************************************************************/ + +ArduinoIoTCloudNotecard::ArduinoIoTCloudNotecard() +: + _state{State::ConnectPhy} + ,_connection_attempt(0,0) + ,_message_stream(std::bind(&ArduinoIoTCloudNotecard::sendMessage, this, std::placeholders::_1)) + ,_thing(&_message_stream) + ,_device(&_message_stream) + ,_notecard_last_poll_ms{static_cast(-DEFAULT_READ_INTERVAL_MS)} + ,_notecard_polling_interval_ms{DEFAULT_READ_INTERVAL_MS} + ,_interrupt_pin{-1} + ,_data_available{false} +{ + +} + +/****************************************************************************** + * PUBLIC MEMBER FUNCTIONS + ******************************************************************************/ + +int ArduinoIoTCloudNotecard::begin(ConnectionHandler &connection_, int interrupt_pin_) +{ + _connection = &connection_; + NotecardConnectionHandler *notecard_connection = reinterpret_cast(_connection); + + // Configure the interrupt pin + if (interrupt_pin_ >= 0) { + _interrupt_pin = interrupt_pin_; + ::pinMode(_interrupt_pin, INPUT); + ::attachInterrupt(digitalPinToInterrupt(_interrupt_pin), ISR_dataAvailable, RISING); + notecard_connection->enableHardwareInterrupts(); + } + + // Initialize the connection to the Notecard + if (NetworkConnectionState::ERROR == _connection->check()) { + DEBUG_ERROR("ArduinoIoTCloudNotecard::%s encountered fatal connection error!", __FUNCTION__); + return 0; // (false -> failure) + } + + // Pull the Arduino IoT Cloud Device ID from the Notecard + setDeviceId(notecard_connection->getDeviceId()); + + // Begin the Notecard time service + _time_service.begin(&connection_); + + // Setup retry timers + _connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms); + + // Configure the Device and Thing property containers + _thing.begin(); + _device.begin(); + + return 1; // (true -> success) +} + +int ArduinoIoTCloudNotecard::connected() +{ + return (_connection->check() == NetworkConnectionState::CONNECTED); +} + +void ArduinoIoTCloudNotecard::printDebugInfo() +{ + // Print the debug information + NetworkConnectionState conn_state = _connection->check(); + DEBUG_INFO("***** Arduino IoT Cloud Notecard - configuration info *****"); + DEBUG_INFO("Notecard UID: %s", reinterpret_cast(_connection)->getNotecardUid().c_str()); + DEBUG_INFO("Arduino Device ID: %s", getDeviceId().c_str()); + if (NetworkConnectionState::CONNECTED == conn_state) + { + DEBUG_INFO("Arduino Thing ID: %s", getThingId().c_str()); + } + else + { + DEBUG_INFO("Arduino Thing ID: awaiting connection..."); + } +} + +void ArduinoIoTCloudNotecard::update() +{ + // Run through the state machine + State next_state = _state; + switch (_state) + { + case State::ConnectPhy: next_state = handle_ConnectPhy(); break; + case State::SyncTime: next_state = handle_SyncTime(); break; + case State::Connected: next_state = handle_Connected(); break; + case State::Disconnect: next_state = handle_Disconnect(); break; + } + _state = next_state; +} + +/****************************************************************************** + * PRIVATE STATE MACHINE FUNCTIONS + ******************************************************************************/ + +ArduinoIoTCloudNotecard::State ArduinoIoTCloudNotecard::handle_ConnectPhy() +{ + if (_connection->check() == NetworkConnectionState::CONNECTED + && (!_connection_attempt.isRetry() || (_connection_attempt.isRetry() && _connection_attempt.isExpired()))) + return State::SyncTime; + else + return State::ConnectPhy; +} + +ArduinoIoTCloudNotecard::State ArduinoIoTCloudNotecard::handle_SyncTime() +{ + const uint32_t current_time = ArduinoCloud.getInternalTime(); + if (TimeServiceClass::isTimeValid(current_time)) + { + DEBUG_VERBOSE("ArduinoIoTCloudNotecard::%s [%d] internal clock configured to posix timestamp %d", __FUNCTION__, millis(), current_time); + return State::Connected; + } + + DEBUG_ERROR("ArduinoIoTCloudNotecard::%s could not get valid time. Retrying now.", __FUNCTION__); + return State::ConnectPhy; +} + +ArduinoIoTCloudNotecard::State ArduinoIoTCloudNotecard::handle_Connected() +{ + if (!connected() || !_thing.connected() || !_device.connected()) + { + return State::Disconnect; + } + + // Poll Notecard for new messages + pollNotecard(); + + // Call CloudDevice process to get configuration + _device.update(); + +#if OTA_ENABLED + if(_get_ota_confirmation != nullptr && + _ota.getState() == OTACloudProcessInterface::State::OtaAvailable && + _get_ota_confirmation()) { + _ota.approveOta(); + } + + _ota.update(); +#endif // OTA_ENABLED + + + if (_device.isAttached()) { + // Call CloudThing process to synchronize properties + _thing.update(); + } + + return State::Connected; +} + +ArduinoIoTCloudNotecard::State ArduinoIoTCloudNotecard::handle_Disconnect() +{ + if (!connected()) { + DEBUG_ERROR("ArduinoIoTCloudNotecard::%s connection to Notehub lost", __FUNCTION__); + } + + // Reset the Thing and Device property containers + Message message = { ResetCmdId }; + _thing.handleMessage(&message); + _device.handleMessage(&message); + + DEBUG_INFO("Disconnected from Arduino IoT Cloud"); + + // Execute the user-defined disconnect callback + execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT); + + // Setup timer for broker connection and restart + _connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms); + return State::ConnectPhy; +} + +/****************************************************************************** + * PRIVATE SUPPORT FUNCTIONS + ******************************************************************************/ + +void ArduinoIoTCloudNotecard::attachThing(String thingId) +{ + _thing_id = thingId; + + Message message; + message = { DeviceAttachedCmdId }; + _device.handleMessage(&message); + + DEBUG_INFO("Connected to Arduino IoT Cloud"); + DEBUG_INFO("Thing ID: %s", getThingId().c_str()); + execCloudEventCallback(ArduinoIoTCloudEvent::CONNECT); +} + +bool ArduinoIoTCloudNotecard::available(void) +{ + bool result; + + const bool interrupts_enabled = (_interrupt_pin >= 0); + const uint32_t now_ms = ::millis(); + + bool check_data = ((now_ms - _notecard_last_poll_ms) > _notecard_polling_interval_ms); + if (interrupts_enabled) { + check_data = (_data_available || ((now_ms - _notecard_last_poll_ms) > FAILSAFE_READ_INTERVAL_MS)); + } + + if (check_data) { + result = _connection->available(); + _data_available = (interrupts_enabled && ::digitalRead(_interrupt_pin)); + _notecard_last_poll_ms = now_ms; + } else { + result = false; + } + + return result; +} + +void ArduinoIoTCloudNotecard::detachThing() +{ + Message message; + message = { DeviceDetachedCmdId }; + _device.handleMessage(&message); + + _thing_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; + DEBUG_INFO("Disconnected from Arduino IoT Cloud"); + execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT); +} + +void ArduinoIoTCloudNotecard::fetchIncomingBytes(uint8_t *buf, size_t &len) +{ + size_t bytes_received = 0; + for (; + _connection->available() && (bytes_received < len); + ++bytes_received) + { + buf[bytes_received] = _connection->read(); + } + if (bytes_received == len && _connection->available()) { + DEBUG_ERROR("ArduinoIoTCloudNotecard::%s buffer overflow on inbound message", __FUNCTION__); + } else { + DEBUG_DEBUG("ArduinoIoTCloudNotecard::%s received %d bytes from cloud", __FUNCTION__, bytes_received); + len = bytes_received; + } +} + +void ArduinoIoTCloudNotecard::pollNotecard(void) +{ + /* Decode available data. */ + if (available()) { + size_t note_len = CBOR_MSG_BUFFER_SIZE; + uint8_t note_buf[CBOR_MSG_BUFFER_SIZE]; + fetchIncomingBytes(note_buf, note_len); + processMessage(note_buf, note_len); + } +} + +void ArduinoIoTCloudNotecard::processCommand(const uint8_t *buf, size_t len) +{ + CommandDown command; + DEBUG_VERBOSE("ArduinoIoTCloudNotecard::%s [%d] received %d bytes", __FUNCTION__, millis(), len); + CBORMessageDecoder decoder; + + if (decoder.decode((Message*)&command, buf, len) != Decoder::Status::Error) { + DEBUG_VERBOSE("ArduinoIoTCloudNotecard::%s [%d] received command id %d", __FUNCTION__, millis(), command.c.id); + switch (command.c.id) + { + case CommandId::ThingUpdateCmdId: + { + DEBUG_VERBOSE("ArduinoIoTCloudNotecard::%s [%d] device configuration received", __FUNCTION__, millis()); + String new_thing_id = String(command.thingUpdateCmd.params.thing_id); + + if (!new_thing_id.length()) { + DEBUG_DEBUG("ArduinoIoTCloudNotecard::%s received null Thing ID.", __FUNCTION__); + _thing_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; + + // Send message to device state machine to inform we have received a null thing-id + Message message; + message = { DeviceRegisteredCmdId }; + _device.handleMessage(&message); + } else { + if (_device.isAttached() && _thing_id != new_thing_id) { + DEBUG_DEBUG("ArduinoIoTCloudNotecard::%s detaching Thing ID: %s", __FUNCTION__, _thing_id.c_str()); + detachThing(); + } + if (!_device.isAttached()) { + DEBUG_DEBUG("ArduinoIoTCloudNotecard::%s attaching Thing ID: %s", __FUNCTION__, new_thing_id.c_str()); + attachThing(new_thing_id); + } + } + } + break; + + case CommandId::ThingDetachCmdId: + { + if (!_device.isAttached() || _thing_id != String(command.thingDetachCmd.params.thing_id)) { + DEBUG_VERBOSE("ArduinoIoTCloudNotecard::%s [%d] thing detach rejected", __FUNCTION__, millis()); + } + + DEBUG_VERBOSE("ArduinoIoTCloudNotecard::%s [%d] thing detach received", __FUNCTION__, millis()); + detachThing(); + } + break; + + case CommandId::TimezoneCommandDownId: + { + DEBUG_VERBOSE("ArduinoIoTCloudNotecard::%s [%d] timezone update received", __FUNCTION__, millis()); + _thing.handleMessage((Message*)&command); + } + break; + + case CommandId::LastValuesUpdateCmdId: + { + DEBUG_VERBOSE("ArduinoIoTCloudNotecard::%s [%d] last values received", __FUNCTION__, millis()); + CBORDecoder::decode(_thing.getPropertyContainer(), + (uint8_t*)command.lastValuesUpdateCmd.params.last_values, + command.lastValuesUpdateCmd.params.length, true); + _thing.handleMessage((Message*)&command); + execCloudEventCallback(ArduinoIoTCloudEvent::SYNC); + + /* + * NOTE: In this current version properties are not properly integrated + * with the new paradigm of modeling the messages with C structs. The + * current CBOR library allocates an array in the heap thus we need to + * delete it after decoding it with the old CBORDecoder + */ + free(command.lastValuesUpdateCmd.params.last_values); + } + break; + +#if OTA_ENABLED + case CommandId::OtaUpdateCmdDownId: + { + DEBUG_VERBOSE("ArduinoIoTCloudNotecard::%s [%d] ota update received", __FUNCTION__, millis()); + _ota.handleMessage((Message*)&command); + } +#endif + + default: + break; + } + } +} + +void ArduinoIoTCloudNotecard::processMessage(const uint8_t *buf, size_t len) +{ + DEBUG_VERBOSE("ArduinoIoTCloudNotecard::%s [%d] decoding %d bytes from cloud...", __FUNCTION__, millis(), len); + NotecardConnectionHandler *notecard_connection = reinterpret_cast(_connection); + switch (notecard_connection->getTopicType()) { + // Commands + case NotecardConnectionHandler::TopicType::Command: + processCommand(buf, len); + break; + // Telemetry + case NotecardConnectionHandler::TopicType::Thing: + CBORDecoder::decode(_thing.getPropertyContainer(), buf, len); + break; + default: + DEBUG_WARNING("Unable to decode unknown topic type: 0x%2X", notecard_connection->getTopicType()); + break; + } +} + +void ArduinoIoTCloudNotecard::sendMessage(Message * msg) +{ + switch (msg->id) { + case PropertiesUpdateCmdId: + sendThingPropertyContainerToCloud(); + break; + + default: + sendCommandMsgToCloud(msg); + break; + } +} + +void ArduinoIoTCloudNotecard::sendCommandMsgToCloud(Message * msg_) +{ + size_t bytes_encoded = CBOR_MSG_BUFFER_SIZE; + uint8_t data[CBOR_MSG_BUFFER_SIZE]; + CBORMessageEncoder encoder; + NotecardConnectionHandler *notecard_connection = reinterpret_cast(_connection); + + if (encoder.encode(msg_, data, bytes_encoded) == Encoder::Status::Complete) { + if (CBOR_LORA_PAYLOAD_MAX_SIZE < bytes_encoded) { + DEBUG_WARNING("Encoded %d bytes for Command Message. Exceeds maximum payload size of %d bytes, and cannot be sent to cloud.", bytes_encoded, CBOR_LORA_PAYLOAD_MAX_SIZE); + } else if (bytes_encoded > 0) { + DEBUG_DEBUG("ArduinoIoTCloudNotecard::%s encoded %d bytes for Command Message", __FUNCTION__, bytes_encoded); + notecard_connection->setTopicType(NotecardConnectionHandler::TopicType::Command); + if (notecard_connection->write(data, bytes_encoded)) { + DEBUG_ERROR("Failed to send Command Message to cloud"); + } else { + notecard_connection->initiateNotehubSync(NotecardConnectionHandler::SyncType::Inbound); + } + } else { + DEBUG_DEBUG("ArduinoIoTCloudNotecard::%s encoded zero (0) bytes for Command Message", __FUNCTION__); + } + } else { + DEBUG_ERROR("Failed to encode Command Message"); + } +} + +void ArduinoIoTCloudNotecard::sendThingPropertyContainerToCloud() +{ + int bytes_encoded = 0; + uint8_t data[CBOR_MSG_BUFFER_SIZE]; + NotecardConnectionHandler *notecard_connection = reinterpret_cast(_connection); + + // Check if any property needs encoding and send them to the cloud + if (CBOREncoder::encode(_thing.getPropertyContainer(), data, sizeof(data), bytes_encoded, _thing.getPropertyContainerIndex(), USE_LIGHT_PAYLOADS) == CborNoError) { + if (static_cast(CBOR_LORA_PAYLOAD_MAX_SIZE) < bytes_encoded) { + DEBUG_ERROR("Encoded %d bytes for Thing properties. Exceeds maximum encoded payload size of %d bytes, and cannot sync with cloud.", bytes_encoded, CBOR_LORA_PAYLOAD_MAX_SIZE); + } else if (bytes_encoded < 0) { + DEBUG_ERROR("Encoding Thing properties resulted in error: %d.", bytes_encoded); + } else if (bytes_encoded > 0) { + DEBUG_DEBUG("ArduinoIoTCloudNotecard::%s encoded %d bytes of Thing properties", __FUNCTION__, bytes_encoded); + notecard_connection->setTopicType(NotecardConnectionHandler::TopicType::Thing); + if (notecard_connection->write(data, bytes_encoded)) { + DEBUG_ERROR("Failed to sync Thing properties with cloud"); + } + } + } else { + DEBUG_ERROR("Failed to encode Thing properties"); + } +} + +/****************************************************************************** + * EXTERN DEFINITION + ******************************************************************************/ + +ArduinoIoTCloudNotecard ArduinoCloud; + +#endif // HAS_NOTECARD diff --git a/src/ArduinoIoTCloudNotecard.h b/src/ArduinoIoTCloudNotecard.h new file mode 100644 index 00000000..d43147c2 --- /dev/null +++ b/src/ArduinoIoTCloudNotecard.h @@ -0,0 +1,134 @@ +/* + This file is part of the ArduinoIoTCloud library. + + Copyright 2024 Blues (http://www.blues.com/) + + 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_NOTECARD_H +#define ARDUINO_IOT_CLOUD_NOTECARD_H + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include "ArduinoIoTCloud.h" +#include "ArduinoIoTCloudThing.h" +#include "ArduinoIoTCloudDevice.h" + +/****************************************************************************** + * DEFINES + ******************************************************************************/ + +#define USE_LIGHT_PAYLOADS (false) + +/****************************************************************************** + * CONSTANTS + ******************************************************************************/ + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +/** + * @brief The ArduinoIoTCloudNotecard class + * + * This class is used to connect to the Arduino IoT Cloud using a Notecard. + */ +class ArduinoIoTCloudNotecard : public ArduinoIoTCloudClass +{ + public: + ArduinoIoTCloudNotecard(); + virtual ~ArduinoIoTCloudNotecard() { } + + virtual void update () override; + virtual int connected () override; + virtual void printDebugInfo() override; + + /** + * @brief Begin the connection to the Arduino IoT Cloud. + * + * @param connection The connection handler to use. + * @param interrupt_pin The interrupt pin to use for the Notecard. + * + * @note The interrupt pin is optional and only required if you want to + * eliminate the need to poll the Notecard for new data. The pin only needs + * to be specified, and will otherwise be configured by the library. + * + * @return 1 on success, 0 on failure. + */ + int begin(ConnectionHandler &connection, int interrupt_pin = -1); + + /** + * @brief Set the Notecard polling interval. + * + * The interval at which the Notecard is polled (via I2C/UART) for new data. + * This is not a network transaction, but a local polling of the Notecard. + * + * @param interval_ms The interval in milliseconds. + * @par + * - Minimum: 250ms + * - Default: 1000ms + * + * @note The Notecard poll interval is ignored if an interrupt pin is + * provided to the `begin()` function. + */ + inline void setNotecardPollingInterval(uint32_t interval_ms) { _notecard_polling_interval_ms = ((interval_ms < 250) ? 250 : interval_ms); } + + private: + + enum class State + { + ConnectPhy, + SyncTime, + Connected, + Disconnect, + }; + + State _state; + TimedAttempt _connection_attempt; + MessageStream _message_stream; + ArduinoCloudThing _thing; + ArduinoCloudDevice _device; + + // Notecard member variables + uint32_t _notecard_last_poll_ms; + uint32_t _notecard_polling_interval_ms; + int _interrupt_pin; + volatile bool _data_available; + + inline virtual PropertyContainer &getThingPropertyContainer() override { return _thing.getPropertyContainer(); } + + State handle_ConnectPhy(); + State handle_SyncTime(); + State handle_Connected(); + State handle_Disconnect(); + + void attachThing(String thingId); + bool available (void); + void detachThing(); + void fetchIncomingBytes(uint8_t *buf, size_t &len); + void pollNotecard(void); + void processCommand(const uint8_t *buf, size_t len); + void processMessage(const uint8_t *buf, size_t len); + void sendMessage(Message * msg); + void sendCommandMsgToCloud(Message * msg_); + void sendThingPropertyContainerToCloud(void); + + friend void ISR_dataAvailable (void); +}; + +/****************************************************************************** + * EXTERN DECLARATION + ******************************************************************************/ + +extern ArduinoIoTCloudNotecard ArduinoCloud; + +#endif // ARDUINO_IOT_CLOUD_NOTECARD_H diff --git a/src/ArduinoIoTCloudThing.cpp b/src/ArduinoIoTCloudThing.cpp index 98ebb0fb..5b07d0a5 100644 --- a/src/ArduinoIoTCloudThing.cpp +++ b/src/ArduinoIoTCloudThing.cpp @@ -15,7 +15,7 @@ #include -#ifdef HAS_TCP +#if defined(HAS_NOTECARD) || defined(HAS_TCP) #include "ArduinoIoTCloudThing.h" #include "interfaces/CloudProcess.h" @@ -180,4 +180,4 @@ ArduinoCloudThing::State ArduinoCloudThing::handleDisconnect() { return State::Disconnect; } -#endif /* HAS_TCP */ +#endif /* HAS_NOTECARD || HAS_TCP */ diff --git a/src/utility/time/RTCMillis.cpp b/src/utility/time/RTCMillis.cpp index 419236d1..d7debd26 100644 --- a/src/utility/time/RTCMillis.cpp +++ b/src/utility/time/RTCMillis.cpp @@ -15,12 +15,14 @@ a commercial license, send an email to license@arduino.cc. */ -#ifdef ARDUINO_ARCH_ESP8266 - /************************************************************************************** * INCLUDE **************************************************************************************/ +#include "AIoTC_Config.h" + +#if defined(HAS_NOTECARD) || defined(ARDUINO_ARCH_ESP8266) + #include #include "RTCMillis.h" @@ -59,4 +61,4 @@ unsigned long RTCMillis::get() return _last_rtc_update_value; } -#endif /* ARDUINO_ARCH_ESP8266 */ +#endif /* HAS_NOTECARD || ARDUINO_ARCH_ESP8266 */ diff --git a/src/utility/time/RTCMillis.h b/src/utility/time/RTCMillis.h index 1fa7e0f4..ab35b953 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_ -#ifdef ARDUINO_ARCH_ESP8266 +#if defined(HAS_NOTECARD) || defined(ARDUINO_ARCH_ESP8266) /************************************************************************************** * INCLUDE @@ -45,6 +45,6 @@ class RTCMillis }; -#endif /* ARDUINO_ARCH_ESP8266 */ +#endif /* HAS_NOTECARD || ARDUINO_ARCH_ESP8266 */ #endif /* ARDUINO_IOT_CLOUD_RTC_MILLIS_H_ */ diff --git a/src/utility/time/TimeService.cpp b/src/utility/time/TimeService.cpp index 5bf081de..6cae34b2 100644 --- a/src/utility/time/TimeService.cpp +++ b/src/utility/time/TimeService.cpp @@ -19,26 +19,21 @@ * INCLUDE **************************************************************************************/ -#include #include -#include "TimeService.h" -#include "NTPUtils.h" + +#include "AIoTC_Config.h" #include "AIoTC_Const.h" +#include "NTPUtils.h" +#include "TimeService.h" -#ifdef ARDUINO_ARCH_SAMD +#if defined(HAS_NOTECARD) || defined(ARDUINO_ARCH_ESP8266) + #include "RTCMillis.h" +#elif defined(ARDUINO_ARCH_SAMD) #include -#endif - -#ifdef ARDUINO_ARCH_MBED +#elif defined(ARDUINO_ARCH_MBED) #include -#endif - -#ifdef ARDUINO_ARCH_ESP8266 - #include "RTCMillis.h" -#endif - -#ifdef ARDUINO_ARCH_RENESAS +#elif defined(ARDUINO_ARCH_RENESAS) #include "RTC.h" #endif @@ -46,12 +41,10 @@ * GLOBAL VARIABLES **************************************************************************************/ -#ifdef ARDUINO_ARCH_SAMD -RTCZero rtc; -#endif - -#ifdef ARDUINO_ARCH_ESP8266 +#if defined(HAS_NOTECARD) || defined(ARDUINO_ARCH_ESP8266) RTCMillis rtc; +#elif defined(ARDUINO_ARCH_SAMD) +RTCZero rtc; #endif /************************************************************************************** @@ -60,6 +53,12 @@ RTCMillis rtc; 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); @@ -90,6 +89,8 @@ void renesas_setRTC(unsigned long time); unsigned long renesas_getRTC(); #endif +#endif /* HAS_NOTECARD */ + /************************************************************************************** * DEFINES **************************************************************************************/ @@ -161,10 +162,9 @@ bool TimeServiceClass::sync() if(_sync_func) { utc = _sync_func(); } else { -#ifdef HAS_TCP +#if defined(HAS_NOTECARD) || defined(HAS_TCP) utc = getRemoteTime(); -#endif -#ifdef HAS_LORA +#elif defined(HAS_LORA) /* Just keep incrementing stored RTC value*/ utc = getRTC(); #endif @@ -275,7 +275,7 @@ unsigned long TimeServiceClass::getTimeFromString(const String& input) * PRIVATE MEMBER FUNCTIONS **************************************************************************************/ -#ifdef HAS_TCP +#if defined(HAS_NOTECARD) || defined(HAS_TCP) bool TimeServiceClass::connected() { if(_con_hdl == nullptr) { @@ -288,6 +288,7 @@ bool TimeServiceClass::connected() unsigned long TimeServiceClass::getRemoteTime() { if(connected()) { +#ifdef HAS_TCP /* At first try to obtain a valid time via NTP. * This is the most reliable time source and it will * ensure a correct behaviour of the library. @@ -298,6 +299,7 @@ unsigned long TimeServiceClass::getRemoteTime() return ntp_time; } } +#endif /* HAS_TCP */ /* As fallback if NTP request fails try to obtain the * network time using the connection handler. @@ -316,7 +318,7 @@ unsigned long TimeServiceClass::getRemoteTime() return EPOCH_AT_COMPILE_TIME; } -#endif /* HAS_TCP */ +#endif /* HAS_NOTECARD || HAS_TCP */ bool TimeServiceClass::isTimeValid(unsigned long const time) { @@ -331,7 +333,9 @@ bool TimeServiceClass::isTimeZoneOffsetValid(long const offset) void TimeServiceClass::initRTC() { -#if defined (ARDUINO_ARCH_SAMD) +#if defined (HAS_NOTECARD) + notecard_initRTC(); +#elif defined (ARDUINO_ARCH_SAMD) samd_initRTC(); #elif defined (ARDUINO_ARCH_MBED) mbed_initRTC(); @@ -348,7 +352,9 @@ void TimeServiceClass::initRTC() void TimeServiceClass::setRTC(unsigned long time) { -#if defined (ARDUINO_ARCH_SAMD) +#if defined (HAS_NOTECARD) + notecard_setRTC(time); +#elif defined (ARDUINO_ARCH_SAMD) samd_setRTC(time); #elif defined (ARDUINO_ARCH_MBED) mbed_setRTC(time); @@ -365,7 +371,9 @@ void TimeServiceClass::setRTC(unsigned long time) unsigned long TimeServiceClass::getRTC() { -#if defined (ARDUINO_ARCH_SAMD) +#if defined (HAS_NOTECARD) + return notecard_getRTC(); +#elif defined (ARDUINO_ARCH_SAMD) return samd_getRTC(); #elif defined (ARDUINO_ARCH_MBED) return mbed_getRTC(); @@ -420,6 +428,23 @@ 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() { @@ -509,6 +534,8 @@ unsigned long renesas_getRTC() } #endif +#endif /* HAS_NOTECARD */ + /****************************************************************************** * EXTERN DEFINITION ******************************************************************************/ diff --git a/src/utility/time/TimeService.h b/src/utility/time/TimeService.h index 794ec344..71656d94 100644 --- a/src/utility/time/TimeService.h +++ b/src/utility/time/TimeService.h @@ -69,7 +69,7 @@ class TimeServiceClass unsigned long _sync_interval_ms; syncTimeFunctionPtr _sync_func; -#ifdef HAS_TCP +#if defined(HAS_NOTECARD) || defined(HAS_TCP) unsigned long getRemoteTime(); bool connected(); #endif