diff --git a/build_all/base-libraries/AzureIoTUtility/src/sam/NTPClientAz.cpp b/build_all/base-libraries/AzureIoTUtility/src/sam/NTPClientAz.cpp new file mode 100644 index 0000000..43f7dc5 --- /dev/null +++ b/build_all/base-libraries/AzureIoTUtility/src/sam/NTPClientAz.cpp @@ -0,0 +1,92 @@ +// Copyright (c) Arduino. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if defined(ARDUINO_ARCH_SAM) +#include "NTPClientAz.h" + +#define LOCAL_UDP_PORT 2390 + +NTPClientAz::NTPClientAz() : + _udp() +{ +} + +int NTPClientAz::begin() +{ + return _udp.begin(LOCAL_UDP_PORT); +} + +uint32_t NTPClientAz::getEpochTime(const char* host, int port, unsigned long timeout) +{ + if (host == NULL || port < 1) { + return (uint32_t)-1; + } + + prepareRequest(); + sendRequest(host, port); + + if (!receiveResponse(timeout)) { + return (uint32_t)-1; + } + + return parseResponse(); +} + +void NTPClientAz::end() +{ + _udp.stop(); +} + +void NTPClientAz::prepareRequest() +{ + memset(_buffer, 0, NTP_PACKET_SIZE); + + // Initialize values needed to form NTP request + _buffer[0] = 0b11100011; // LI, Version, Mode + _buffer[1] = 0; // Stratum, or type of clock + _buffer[2] = 6; // Polling Interval + _buffer[3] = 0xEC; // Peer Clock Precision + + // 8 bytes of zero for Root Delay & Root Dispersion + + _buffer[12] = 49; + _buffer[13] = 0x4E; + _buffer[14] = 49; + _buffer[15] = 52; +} + +void NTPClientAz::sendRequest(const char* host, int port) +{ + _udp.beginPacket(host, port); + _udp.write(_buffer, NTP_PACKET_SIZE); + _udp.endPacket(); +} + +int NTPClientAz::receiveResponse(unsigned long timeout) +{ + long start = millis(); + int size = 0; + + while(size == 0 && (millis() - start) < timeout) { + size = _udp.parsePacket(); + } + + if (size != NTP_PACKET_SIZE) { + return 0; + } + + _udp.read(_buffer, NTP_PACKET_SIZE); + + return 1; +} + +uint32_t NTPClientAz::parseResponse() +{ + uint16_t high = word(_buffer[40], _buffer[41]); + uint16_t low = word(_buffer[42], _buffer[43]); + uint32_t ntpTime = high << 16 | low; // since 1900 + uint32_t epoch = ntpTime - 2208988800UL; // since 1970 + + return epoch; +} +#endif diff --git a/build_all/base-libraries/AzureIoTUtility/src/sam/NTPClientAz.h b/build_all/base-libraries/AzureIoTUtility/src/sam/NTPClientAz.h new file mode 100644 index 0000000..2a8a04e --- /dev/null +++ b/build_all/base-libraries/AzureIoTUtility/src/sam/NTPClientAz.h @@ -0,0 +1,38 @@ +// Copyright (c) Arduino. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if defined(ARDUINO_ARCH_SAM) + +#ifndef NTPCLIENT_AZ_H +#define NTPCLIENT_AZ_H + +#include +#include +#include + +#define NTP_PACKET_SIZE 48 +#define NTP_PORT 123 + +#define DEFAULT_NTP_TIMEOUT 10000 + +class NTPClientAz +{ + public: + NTPClientAz(); + int begin(); + uint32_t getEpochTime(const char* host, int port = NTP_PORT, unsigned long timeout = DEFAULT_NTP_TIMEOUT); + void end(); + + private: + void prepareRequest(); + void sendRequest(const char* host, int port); + int receiveResponse(unsigned long timeout); + uint32_t parseResponse(); + + char _buffer[NTP_PACKET_SIZE]; + EthernetUDP _udp; +}; + +#endif // NTPCLIENT_AZ_H + +#endif // ARDUINO_ARCH_SAM diff --git a/build_all/base-libraries/AzureIoTUtility/src/sam/pem_to_ta.cpp b/build_all/base-libraries/AzureIoTUtility/src/sam/pem_to_ta.cpp new file mode 100644 index 0000000..0711ccb --- /dev/null +++ b/build_all/base-libraries/AzureIoTUtility/src/sam/pem_to_ta.cpp @@ -0,0 +1,214 @@ + +// Code here is pulled from curl sources, with the copyright and license +// shown below. I've extracted just one function (load_cafile) and converted +// it to read the certificate from a char array instead of a file. + + +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2019 - 2020, Michael Forney, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include +#include +#include "bearssl.h" +#include +#include +#include + +#include "pem_to_ta.h" + +struct cafile_parser +{ + int err; + bool in_cert; + br_x509_decoder_context xc; + /* array of trust anchors loaded from CAfile */ + br_x509_trust_anchor *anchors; + size_t anchors_len; + /* buffer for DN data */ + unsigned char dn[1024]; + size_t dn_len; +}; + +static void append_dn(void *ctx, const void *buf, size_t len) +{ + struct cafile_parser *ca = (struct cafile_parser *)ctx; + + if (ca->err != 0 || !ca->in_cert) + return; + if (sizeof(ca->dn) - ca->dn_len < len) + { + ca->err = CONV_FAILED_INIT; + return; + } + memcpy(ca->dn + ca->dn_len, buf, len); + ca->dn_len += len; +} + +static void x509_push(void *ctx, const void *buf, size_t len) +{ + struct cafile_parser *ca = (struct cafile_parser *)ctx; + + if (ca->in_cert) + br_x509_decoder_push(&ca->xc, buf, len); +} + +int pem_to_ta(const char *cer, + size_t cer_len, + br_x509_trust_anchor **anchors, + size_t *anchors_len) +{ + struct cafile_parser ca; + br_pem_decoder_context pc; + br_x509_trust_anchor *ta; + size_t ta_size; + br_x509_trust_anchor *new_anchors; + size_t new_anchors_len; + br_x509_pkey *pkey; + const char *p; + const char *name; + size_t n, i, pushed; + + ca.err = CONV_OK; + ca.in_cert = 0; + ca.anchors = NULL; + ca.anchors_len = 0; + br_pem_decoder_init(&pc); + br_pem_decoder_setdest(&pc, x509_push, &ca); + n = cer_len; + p = cer; + + while (n) + { + pushed = br_pem_decoder_push(&pc, p, n); + if (ca.err) + goto fail; + p += pushed; + n -= pushed; + switch (br_pem_decoder_event(&pc)) + { + case 0: + break; + case BR_PEM_BEGIN_OBJ: + name = br_pem_decoder_name(&pc); + //if(strcmp(name, "CERTIFICATE") && strcmp(name, "X509 CERTIFICATE")) + // break; + br_x509_decoder_init(&ca.xc, append_dn, &ca); + if (ca.anchors_len == SIZE_MAX / sizeof(ca.anchors[0])) + { + ca.err = CONV_OUT_OF_MEMORY; + goto fail; + } + new_anchors_len = ca.anchors_len + 1; + new_anchors = (br_x509_trust_anchor *)realloc(ca.anchors, + new_anchors_len * sizeof(ca.anchors[0])); + if (!new_anchors) + { + ca.err = CONV_OUT_OF_MEMORY; + goto fail; + } + ca.anchors = new_anchors; + ca.anchors_len = new_anchors_len; + ca.in_cert = 1; + ca.dn_len = 0; + ta = &ca.anchors[ca.anchors_len - 1]; + ta->dn.data = NULL; + break; + case BR_PEM_END_OBJ: + if (!ca.in_cert) + break; + ca.in_cert = 0; + if (br_x509_decoder_last_error(&ca.xc)) + { + ca.err = CONV_SSL_CACERT_BADFILE; + goto fail; + } + ta->flags = 0; + if (br_x509_decoder_isCA(&ca.xc)) + ta->flags |= BR_X509_TA_CA; + pkey = br_x509_decoder_get_pkey(&ca.xc); + if (!pkey) + { + ca.err = CONV_SSL_CACERT_BADFILE; + goto fail; + } + ta->pkey = *pkey; + + /* calculate space needed for trust anchor data */ + ta_size = ca.dn_len; + switch (pkey->key_type) + { + case BR_KEYTYPE_RSA: + ta_size += pkey->key.rsa.nlen + pkey->key.rsa.elen; + break; + case BR_KEYTYPE_EC: + ta_size += pkey->key.ec.qlen; + break; + default: + ca.err = CONV_FAILED_INIT; + goto fail; + } + + /* fill in trust anchor DN and public key data */ + ta->dn.data = (unsigned char *)malloc(ta_size); + if (!ta->dn.data) + { + ca.err = CONV_OUT_OF_MEMORY; + goto fail; + } + memcpy(ta->dn.data, ca.dn, ca.dn_len); + ta->dn.len = ca.dn_len; + switch (pkey->key_type) + { + case BR_KEYTYPE_RSA: + ta->pkey.key.rsa.n = ta->dn.data + ta->dn.len; + memcpy(ta->pkey.key.rsa.n, pkey->key.rsa.n, pkey->key.rsa.nlen); + ta->pkey.key.rsa.e = ta->pkey.key.rsa.n + ta->pkey.key.rsa.nlen; + memcpy(ta->pkey.key.rsa.e, pkey->key.rsa.e, pkey->key.rsa.elen); + break; + case BR_KEYTYPE_EC: + ta->pkey.key.ec.q = ta->dn.data + ta->dn.len; + memcpy(ta->pkey.key.ec.q, pkey->key.ec.q, pkey->key.ec.qlen); + break; + } + break; + default: + ca.err = CONV_SSL_CACERT_BADFILE; + goto fail; + } + } + //} + +fail: + if (ca.err == CONV_OK) + { + *anchors = ca.anchors; + *anchors_len = ca.anchors_len; + } + else + { + for (i = 0; i < ca.anchors_len; ++i) + free(ca.anchors[i].dn.data); + free(ca.anchors); + } + + return ca.err; +} \ No newline at end of file diff --git a/build_all/base-libraries/AzureIoTUtility/src/sam/pem_to_ta.h b/build_all/base-libraries/AzureIoTUtility/src/sam/pem_to_ta.h new file mode 100644 index 0000000..03d75d6 --- /dev/null +++ b/build_all/base-libraries/AzureIoTUtility/src/sam/pem_to_ta.h @@ -0,0 +1,18 @@ + +#ifndef PEM_TO_TA_H +#define PEM_TO_TA_H + +#include "bearssl.h" + +#define CONV_OK (0) +#define CONV_FAILED_INIT (2) +#define CONV_OUT_OF_MEMORY (27) +#define CONV_SSL_CACERT_BADFILE (77) + +int pem_to_ta(const char *cer, + size_t cer_len, + br_x509_trust_anchor **anchors, + size_t *anchors_len); + + +#endif \ No newline at end of file diff --git a/build_all/base-libraries/AzureIoTUtility/src/sam/sample_init.cpp b/build_all/base-libraries/AzureIoTUtility/src/sam/sample_init.cpp new file mode 100644 index 0000000..4c42016 --- /dev/null +++ b/build_all/base-libraries/AzureIoTUtility/src/sam/sample_init.cpp @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifdef ARDUINO_ARCH_SAM + +#include +#include +#include +#include +#include + +#include +#include +#include "NTPClientAz.h" + +#include +#include + +#include "certs/certs.h" +#include "pem_to_ta.h" + + +br_x509_trust_anchor *g_anchors = NULL; +size_t g_anchors_len = 0; +int g_rand_pin = A11; + + +static void initEthernet(byte *mac) +{ + bool not_connected = true; + // start the Ethernet connection: + Serial.println("Initialize Ethernet with DHCP:"); + do + { + if (Ethernet.begin(mac) == 0) + { + Serial.println("Failed to configure Ethernet using DHCP"); + // Check for Ethernet hardware present + if (Ethernet.hardwareStatus() == EthernetNoHardware) + { + Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :("); + while (true) + { + delay(1); // do nothing, no point running without Ethernet hardware + } + } + if (Ethernet.linkStatus() == LinkOFF) + { + Serial.println("Ethernet cable is not connected."); + } + // try to configure using IP address instead of DHCP: + // Ethernet.begin(mac, ip, myDns); + delay(500); + } + else + { + not_connected = false; + } + }while(not_connected); + + Serial.print(" DHCP assigned IP "); + Serial.println(Ethernet.localIP()); + + // give the Ethernet shield a second to initialize: + delay(1000); +} + +static void initTime() +{ + EthernetUDP _udp; + + time_t epochTime = (time_t)-1; + + NTPClientAz ntpClient; + Serial.println("Fetching NTP epoch time"); + ntpClient.begin(); + + while (true) + { + epochTime = ntpClient.getEpochTime("0.pool.ntp.org"); + + if (epochTime == (time_t)-1) + { + Serial.println("Fetching NTP epoch time failed! Waiting 2 seconds to retry."); + delay(2000); + } + else + { + Serial.print("Fetched NTP epoch time is: "); + Serial.println((uint32_t)epochTime); + break; + } + } + + ntpClient.end(); + + struct timeval tv; + tv.tv_sec = epochTime; + tv.tv_usec = 0; + + settimeofday(&tv, NULL); + +} + +void due_sample_init(byte *mac, + const int rand_pin) +{ + + + Serial.begin(115200); + initEthernet(mac); + initTime(); + + // Certificate conversion + pem_to_ta(certificates, strlen(certificates), &g_anchors, &g_anchors_len); + g_rand_pin = rand_pin; + delay(1000); +} + +#endif // ARDUINO_ARCH_SAM diff --git a/build_all/base-libraries/AzureIoTUtility/src/sam/sample_init.h b/build_all/base-libraries/AzureIoTUtility/src/sam/sample_init.h new file mode 100644 index 0000000..6febd75 --- /dev/null +++ b/build_all/base-libraries/AzureIoTUtility/src/sam/sample_init.h @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SAMPLE_INIT_H +#define SAMPLE_INIT_H + +#ifdef ARDUINO_ARCH_SAM +#include + + +extern br_x509_trust_anchor *g_anchors; +extern size_t g_anchors_len; +extern int g_rand_pin; + +void due_sample_init(byte *mac, + const int rand_pin); +#endif + +#endif // SAMPLE_INIT_H diff --git a/build_all/base-libraries/AzureIoTUtility/src/sam/stdio.cpp b/build_all/base-libraries/AzureIoTUtility/src/sam/stdio.cpp new file mode 100644 index 0000000..fd81e5f --- /dev/null +++ b/build_all/base-libraries/AzureIoTUtility/src/sam/stdio.cpp @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if defined(ARDUINO_ARCH_SAM) +#include + +#include "Arduino.h" + +// these are needed by the serializer, so force link +asm(".global _printf_float"); +asm(".global _scanf_float"); + +extern "C" { + // size_t _write(int handle, const unsigned char *buf, size_t bufSize) + // { + // /* Check for the command to flush all handles */ + // if (handle == -1) { + // return 0; + // } + + // /* Check for stdout and stderr (only necessary if FILE descriptors are enabled.) */ + // if (handle != 1 && handle != 2) { + // return -1; + // } + + // size_t nChars = 0; + // for (; bufSize > 0; --bufSize, ++buf, ++nChars) { + // Serial.write(*buf); + // } + // return nChars; + // } +} +#endif \ No newline at end of file diff --git a/build_all/base-libraries/AzureIoTUtility/src/sam/time.cpp b/build_all/base-libraries/AzureIoTUtility/src/sam/time.cpp new file mode 100644 index 0000000..42ecfc1 --- /dev/null +++ b/build_all/base-libraries/AzureIoTUtility/src/sam/time.cpp @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if defined(ARDUINO_ARCH_SAM) +#include +#include + +#include + +RTCDue rtc(XTAL); + +extern "C" { + int _gettimeofday(struct timeval* tp, void* /*tzvp*/) + { + tp->tv_sec = rtc.unixtime(); + tp->tv_usec = 0; + + return 0; + } + + int settimeofday(const struct timeval* tp, const struct timezone* /*tzp*/) + { + rtc.begin(); + rtc.setClock(tp->tv_sec); + return 0; + } +} +#endif \ No newline at end of file diff --git a/pal/samples/sam/iothub_ll_telemetry_sample/README.md b/pal/samples/sam/iothub_ll_telemetry_sample/README.md new file mode 100644 index 0000000..9c1aab4 --- /dev/null +++ b/pal/samples/sam/iothub_ll_telemetry_sample/README.md @@ -0,0 +1,72 @@ +#### Azure IoT Hub and Arduino Due + Ethernet Shield 2 Demo + +Currently supported hardware: + +- [Arduino Due board](https://store.arduino.cc/arduino-due) with [Ethernet Shield 2](https://store.arduino.cc/arduino-ethernet-shield-2) on top. + +## Prerequisites + +You should have the following ready before beginning with any board: + +- [Setup your IoT hub](https://github.com/Azure/azure-iot-device-ecosystem/blob/master/setup_iothub.md) + +- [Provision your device and get its credentials](https://github.com/Azure/azure-iot-device-ecosystem/blob/master/setup_iothub.md#create-new-device-in-the-iot-hub-device-identity-registry) + +- [Arduino IDE](https://www.arduino.cc/en/Main/Software) + +- Install the Azure IoT C SDK libraries by one of two options: + 1. Generate the Libraries by executing the [`make_sdk.py`](https://github.com/Azure/azure-iot-pal-arduino/blob/master/build_all/make_sdk.py) script within the `build_all` folder, E.x.: `python3 make_sdk.py -o ` + - Note: this is also currently the ONLY way to build the `AzureIoTSocket_WiFi` library for using the esp32. + + 2. Install the following libraries through the Arduino IDE Library Manager: + - `AzureIoTHub`, `AzureIoTUtility`, `AzureIoTProtocol_MQTT`, `AzureIoTProtocol_HTTP` + +## Additional Dependencies + +This demo requires the following libraries installed: + - RTCDue (>=1.1.0) + - SSLClient (>=1.6.10) + - EthernetLarge (>=2.0.0) + +The latest is not available in the Arduino IDE Library Manager. Installation instructions can be found [here](https://github.com/OPEnSLab-OSU/SSLClient#sslclient-with-ethernet). + +## Sample Instructions + +1. Install Arduino Due board support package into your Arduino IDE. + - Open Boards Manager from Tools > Board menu and install Arduino SAM boards platform 1.6.12 or later. + - Select your Arduino Due (Programming Port) board from Tools > Board menu after installation. + +2. Navigate to where your Arduino Due board package is located, typically in `C:\Users\\AppData\Local\Arduino15\packages\arduino\hardware\sam\\` on Windows and `~/.arduino15/packages/arduino/hardware/sam//` on Linux. + +3. Copy the boards.local.txt file from the sample directory to this location. This will add the required defines for the compilation. + + - Note1: If your device is not intended to connect to the global portal.azure.com, please change the CERT define to the appropriate cert define as laid out in [`certs.c`](https://github.com/Azure/azure-iot-sdk-c/blob/master/certs/certs.c). + Otherwise, do not change the content of boards.local.txt file. + + - Note2: Due to RAM limits, you must select just one CERT define. + +4. Open the iothub_ll_telemetry_sample example from the Arduino IDE File->Examples->AzureIoTHub->sam menu. + +5. Add your configuration to iot_configs.h + - Update IoT Hub Connection string. + - Select SAMPLE_MQTT or SAMPLE_HTTP. + - Set the right mac address in assigned_mac. Your Ethernet Shield mac address is usually printed on a label on the bottom of the Shield. + - Assign a floating Arduino Due analog pin for random number generation. +6. Apply required changes to SSLClient library + - Locate library directory, usually in `C:\Users\\Documents\Arduino\libraries\SSLClient`. + - In src\config.h, change `#define BR_USE_UNIX_TIME 0` to `#define BR_USE_UNIX_TIME 1`. + - In src\SSlClient.h change `unsigned char m_iobuf[2048];` to `unsigned char m_iobuf[BR_SSL_BUFSIZE_BIDI];`. + - In src\SSLClient.cpp change costructor lines from this: + ```C++ + br_client_init_TLS12_only(&m_sslctx, &m_x509ctx, m_trust_anchors, m_trust_anchors_num); + // comment the above line and uncomment the line below if you're having trouble connecting over SSL + // br_ssl_client_init_full(&m_sslctx, &m_x509ctx, m_trust_anchors, m_trust_anchors_num); + ``` + to this: + ```C++ + // br_client_init_TLS12_only(&m_sslctx, &m_x509ctx, m_trust_anchors, m_trust_anchors_num); + // comment the above line and uncomment the line below if you're having trouble connecting over SSL + br_ssl_client_init_full(&m_sslctx, &m_x509ctx, m_trust_anchors, m_trust_anchors_num); + ``` + +7. Verify and Upload the sketch. diff --git a/pal/samples/sam/iothub_ll_telemetry_sample/boards.local.txt b/pal/samples/sam/iothub_ll_telemetry_sample/boards.local.txt new file mode 100644 index 0000000..bd93841 --- /dev/null +++ b/pal/samples/sam/iothub_ll_telemetry_sample/boards.local.txt @@ -0,0 +1,2 @@ +arduino_due_x_dbg.build.extra_flags=-D__SAM3X8E__ -mthumb {build.usb_flags} -DDONT_USE_UPLOADTOBLOB -DUSE_BALTIMORE_CERT +arduino_due_x.build.extra_flags=-D__SAM3X8E__ -mthumb {build.usb_flags} -DDONT_USE_UPLOADTOBLOB -DUSE_BALTIMORE_CERT diff --git a/pal/samples/sam/iothub_ll_telemetry_sample/iot_configs.h b/pal/samples/sam/iothub_ll_telemetry_sample/iot_configs.h new file mode 100644 index 0000000..0440201 --- /dev/null +++ b/pal/samples/sam/iothub_ll_telemetry_sample/iot_configs.h @@ -0,0 +1,28 @@ +#ifndef IOT_CONFIGS_H +#define IOT_CONFIGS_H + +#include +#include + + +/** + * IoT Hub Device Connection String setup + * Find your Device Connection String by going to your Azure portal, creating (or navigating to) an IoT Hub, + * navigating to IoT Devices tab on the left, and creating (or selecting an existing) IoT Device. + * Then click on the named Device ID, and you will have able to copy the Primary or Secondary Device Connection String to this sample. + */ +#define DEVICE_CONNECTION_STRING "your-iothub-DEVICE-connection-string" + +// The protocol you wish to use should be uncommented +// +#define SAMPLE_MQTT +//#define SAMPLE_HTTP + +// The following mac address will be assigned to the Ethernet Shield. +// Please use the one written on the label on the bottom of the Shield. +static byte assigned_mac[] = {0x0A,0x0B,0x0C,0x0D,0x0E,0x0F}; + +// Analog Pin assigned to SSLClient library for generating random number. The pin must be floating and not used in your application. +#define RAND_PIN A11 + +#endif /* IOT_CONFIGS_H */ diff --git a/pal/samples/sam/iothub_ll_telemetry_sample/iothub_ll_telemetry_sample.ino b/pal/samples/sam/iothub_ll_telemetry_sample/iothub_ll_telemetry_sample.ino new file mode 100644 index 0000000..933b95a --- /dev/null +++ b/pal/samples/sam/iothub_ll_telemetry_sample/iothub_ll_telemetry_sample.ino @@ -0,0 +1,254 @@ +#include +#include +#include +#include "iothubtransportmqtt.h" + +#include "iot_configs.h" + +void due_sample_init(byte *mac, const int rand_pin); + + +#ifdef SAMPLE_MQTT + #include "AzureIoTProtocol_MQTT.h" + #include "iothubtransportmqtt.h" +#endif // SAMPLE_MQTT +#ifdef SAMPLE_HTTP + #include "AzureIoTProtocol_HTTP.h" + #include "iothubtransporthttp.h" +#endif // SAMPLE_HTTP + + +static const char* connectionString = DEVICE_CONNECTION_STRING; + +static bool g_continueRunning = true; // defines whether or not the device maintains its IoT Hub connection after sending (think receiving messages from the cloud) +static size_t g_message_count_send_confirmations = 0; +static bool g_run_demo = true; +IOTHUB_MESSAGE_HANDLE message_handle; +size_t messages_sent = 0; +#define MESSAGE_COUNT 5 // determines the number of times the device tries to send a message to the IoT Hub in the cloud. +const char* telemetry_msg = "test_message"; +const char* quit_msg = "quit"; +const char* exit_msg = "exit"; + +IOTHUB_DEVICE_CLIENT_LL_HANDLE device_ll_handle; + +static int callbackCounter; +int receiveContext = 0; + +/* -- receive_message_callback -- + * Callback method which executes upon receipt of a message originating from the IoT Hub in the cloud. + * Note: Modifying the contents of this method allows one to command the device from the cloud. + */ +static IOTHUBMESSAGE_DISPOSITION_RESULT receive_message_callback(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback) +{ + int* counter = (int*)userContextCallback; + const unsigned char* buffer; + size_t size; + const char* messageId; + + // Message properties + if ((messageId = IoTHubMessage_GetMessageId(message)) == NULL) + { + messageId = ""; + } + + // Message content + if (IoTHubMessage_GetByteArray(message, (const unsigned char**)&buffer, &size) != IOTHUB_MESSAGE_OK) + { + LogInfo("unable to retrieve the message data\r\n"); + } + else + { + LogInfo("Received Message [%d]\r\n Message ID: %s\r\n Data: <<<%.*s>>> & Size=%d\r\n", *counter, messageId, (int)size, buffer, (int)size); + // If we receive the word 'quit' then we stop running + if (size == (strlen(quit_msg) * sizeof(char)) && memcmp(buffer, quit_msg, size) == 0) + { + g_continueRunning = false; + } + } + + /* Some device specific action code goes here... */ + (*counter)++; + return IOTHUBMESSAGE_ACCEPTED; +} + + +/* -- send_confirm_callback -- + * Callback method which executes upon confirmation that a message originating from this device has been received by the IoT Hub in the cloud. + */ +static void send_confirm_callback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) +{ + (void)userContextCallback; + // When a message is sent this callback will get envoked + g_message_count_send_confirmations++; + LogInfo("Confirmation callback received for message %lu with result %s\r\n", (unsigned long)g_message_count_send_confirmations, MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result)); +} + +/* -- connection_status_callback -- + * Callback method which executes on receipt of a connection status message from the IoT Hub in the cloud. + */ +static void connection_status_callback(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* user_context) +{ + (void)reason; + (void)user_context; + // This sample DOES NOT take into consideration network outages. + if (result == IOTHUB_CLIENT_CONNECTION_AUTHENTICATED) + { + LogInfo("The device client is connected to iothub\r\n"); + } + else + { + LogInfo("The device client has been disconnected\r\n"); + switch(reason){ + case IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN: + LogInfo("Reason EXPIRED_SAS_TOKEN %i\n",reason); + break; + case IOTHUB_CLIENT_CONNECTION_DEVICE_DISABLED: + LogInfo("Reason DEVICE_DISABLED %i\n",reason); + break; + case IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL: + LogInfo("Reason BAD_CREDENTIAL %i\n",reason); + break; + case IOTHUB_CLIENT_CONNECTION_RETRY_EXPIRED: + LogInfo("Reason RETRY_EXPIRED %i\n",reason); + break; + case IOTHUB_CLIENT_CONNECTION_NO_NETWORK: + LogInfo("Reason NO_NETWORK %i\n",reason); + break; + case IOTHUB_CLIENT_CONNECTION_COMMUNICATION_ERROR: + LogInfo("Reason COMMUNICATION_ERROR %i\n",reason); + break; + case IOTHUB_CLIENT_CONNECTION_OK: + LogInfo("Reason CONNECTION_OK %i\n",reason); + break; + case IOTHUB_CLIENT_CONNECTION_NO_PING_RESPONSE: + LogInfo("Reason NO_PING_RESPONSE %i\n",reason); + break; + } + + } +} + + +/* -- run_demo -- + * Runs active task of sending telemetry to IoTHub + * WARNING: only call this function once, as it includes steps to destroy handles and clean up at the end. + */ +static void run_demo() +{ + int result = 0; + + // action phase of the program, sending messages to the IoT Hub in the cloud. + do + { + if (messages_sent < MESSAGE_COUNT) + { + // Construct the iothub message from a string or a byte array + message_handle = IoTHubMessage_CreateFromString(telemetry_msg); + //message_handle = IoTHubMessage_CreateFromByteArray((const unsigned char*)msgText, strlen(msgText))); + + // Set Message property + /*(void)IoTHubMessage_SetMessageId(message_handle, "MSG_ID"); + (void)IoTHubMessage_SetCorrelationId(message_handle, "CORE_ID"); + (void)IoTHubMessage_SetContentTypeSystemProperty(message_handle, "application%2fjson"); + (void)IoTHubMessage_SetContentEncodingSystemProperty(message_handle, "utf-8");*/ + + // Add custom properties to message + // (void)IoTHubMessage_SetProperty(message_handle, "property_key", "property_value"); + + LogInfo("Sending message %d to IoTHub\r\n", (int)(messages_sent + 1)); + result = IoTHubDeviceClient_LL_SendEventAsync(device_ll_handle, message_handle, send_confirm_callback, NULL); + // The message is copied to the sdk so the we can destroy it + IoTHubMessage_Destroy(message_handle); + + messages_sent++; + } + else if (g_message_count_send_confirmations >= MESSAGE_COUNT) + { + // After all messages are all received stop running + g_continueRunning = false; + } + + IoTHubDeviceClient_LL_DoWork(device_ll_handle); + ThreadAPI_Sleep(3); + + + } while (g_continueRunning); + + // Clean up the iothub sdk handle + IoTHubDeviceClient_LL_Destroy(device_ll_handle); + // Free all the sdk subsystem + IoTHub_Deinit(); + + LogInfo("done with sending"); + return; +} + +void setup() { + + // Select the Protocol to use with the connection +#ifdef SAMPLE_MQTT + IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol = MQTT_Protocol; +#endif // SAMPLE_MQTT +#ifdef SAMPLE_HTTP + IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol = HTTP_Protocol; +#endif // SAMPLE_HTTP + + due_sample_init(assigned_mac,RAND_PIN); + + // Used to initialize IoTHub SDK subsystem + (void)IoTHub_Init(); + // Create the iothub handle here + device_ll_handle = IoTHubDeviceClient_LL_CreateFromConnectionString(connectionString, protocol); + LogInfo("Creating IoTHub Device handle\r\n"); + + if (device_ll_handle == NULL) + { + LogInfo("Error AZ002: Failure creating Iothub device. Hint: Check you connection string.\r\n"); + } + else + { + // Set any option that are neccessary. + // For available options please see the iothub_sdk_options.md documentation in the main C SDK + // turn off diagnostic sampling + int diag_off = 0; + IoTHubDeviceClient_LL_SetOption(device_ll_handle, OPTION_DIAGNOSTIC_SAMPLING_PERCENTAGE, &diag_off); + +#ifndef SAMPLE_HTTP + // Example sdk status tracing for troubleshooting + bool traceOn = true; + IoTHubDeviceClient_LL_SetOption(device_ll_handle, OPTION_LOG_TRACE, &traceOn); +#endif // SAMPLE_HTTP + + // Setting the Trusted Certificate. + IoTHubDeviceClient_LL_SetOption(device_ll_handle, OPTION_TRUSTED_CERT, certificates); + +#if defined SAMPLE_MQTT + //Setting the auto URL Encoder (recommended for MQTT). Please use this option unless + //you are URL Encoding inputs yourself. + //ONLY valid for use with MQTT + bool urlEncodeOn = true; + IoTHubDeviceClient_LL_SetOption(device_ll_handle, OPTION_AUTO_URL_ENCODE_DECODE, &urlEncodeOn); + /* Setting Message call back, so we can receive Commands. */ + if (IoTHubClient_LL_SetMessageCallback(device_ll_handle, receive_message_callback, &receiveContext) != IOTHUB_CLIENT_OK) + { + LogInfo("ERROR: IoTHubClient_LL_SetMessageCallback..........FAILED!\r\n"); + } +#endif // SAMPLE_MQTT + + // Setting connection status callback to get indication of connection to iothub + (void)IoTHubDeviceClient_LL_SetConnectionStatusCallback(device_ll_handle, connection_status_callback, NULL); + + } + +} + +void loop(void) +{ + if (g_run_demo) + { + run_demo(); + g_run_demo = false; + } + +} diff --git a/pal/src/sslClient_arduino.cpp b/pal/src/sslClient_arduino.cpp index 7cebc7a..f318f2d 100644 --- a/pal/src/sslClient_arduino.cpp +++ b/pal/src/sslClient_arduino.cpp @@ -19,6 +19,17 @@ static WiFiClientSecure sslClient; // for ESP32 #include "WiFi.h" #include "WiFiClientSecure.h" static WiFiClientSecure sslClient; // for Wio Terminal variant of SAMD +#elif ARDUINO_ARCH_SAM +#include +#include +#include +#include +#include +#include "sam/sample_init.h" + +static EthernetClient baseClient; +SSLClient *p_sslClient = NULL; +#define sslClient (*p_sslClient) #else #include "WiFi101.h" #include "WiFiSSLClient.h" @@ -39,6 +50,16 @@ int sslClient_connect(const char* name, uint16_t port) { #ifdef ARDUINO_ARCH_ESP8266 sslClient.setTrustAnchors(&cert); +#endif +#ifdef ARDUINO_ARCH_SAM + time_t current_time = time(NULL); + //g_ variables declared as extern in sample_init + if(!p_sslClient) + p_sslClient = new SSLClient(baseClient, g_anchors, g_anchors_len, g_rand_pin); + sslClient.setVerificationTime(// days since 1970 + days from 1970 to year 0 + (current_time / SEC_PER_DAY) + 719528UL, + // seconds over start of day + current_time % SEC_PER_DAY); #endif return (int)sslClient.connect(name, port); } @@ -50,7 +71,20 @@ void sslClient_stop(void) size_t sslClient_write(const uint8_t *buf, size_t size) { - return sslClient.write(buf, size); + size_t ret; +#ifdef ARDUINO_ARCH_SAM + // If there is data to read, stop write operation and continue + if(sslClient.available()) + return 0; +#endif + ret = sslClient.write(buf, size); +#ifdef ARDUINO_ARCH_SAM + // Do not flush if there is pending data to read, flush will happens during available call. + if(!sslClient.available()) { + sslClient.flush(); + } +#endif + return ret; } size_t sslClient_print(const char* str) @@ -60,7 +94,18 @@ size_t sslClient_print(const char* str) int sslClient_read(uint8_t *buf, size_t size) { +#ifdef ARDUINO_ARCH_SAM + int ret; + int read_size = 0; + // Read until there is data available or buffer is full + do{ + ret = sslClient.read(&buf[read_size], size-read_size); + read_size += ret; + }while(sslClient.available() && read_size < size); + return read_size; +#else return sslClient.read(buf, size); +#endif } int sslClient_available(void) @@ -71,8 +116,22 @@ int sslClient_available(void) uint8_t sslClient_hostByName(const char* hostName, uint32_t* ipAddress) { IPAddress ip; + #ifdef ARDUINO_ARCH_SAM + + DNSClient dns; + dns.begin(Ethernet.dnsServerIP()); + + uint8_t result = dns.getHostByName(hostName,ip); + (*ipAddress) = (uint32_t)ip; + return result; + #else + uint8_t result = WiFi.hostByName(hostName, ip); (*ipAddress) = (uint32_t)ip; return result; + + #endif + + }