Working Portenta H7 IoT Hub sample
This commit is contained in:
Родитель
73b782a091
Коммит
2d8d44d4db
|
@ -0,0 +1,282 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "AzIoTSasToken.h"
|
||||
#include "SerialLogger.h"
|
||||
#include <az_result.h>
|
||||
#include <mbedtls/base64.h>
|
||||
#include <mbedtls/md.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#define INDEFINITE_TIME ((time_t)-1)
|
||||
|
||||
#define is_az_span_empty(x) (az_span_size(x) == az_span_size(AZ_SPAN_EMPTY) && az_span_ptr(x) == az_span_ptr(AZ_SPAN_EMPTY))
|
||||
|
||||
static uint32_t getSasTokenExpiration(const char* sasToken)
|
||||
{
|
||||
const char SE[] = { '&', 's', 'e', '=' };
|
||||
uint32_t se_as_unix_time = 0;
|
||||
|
||||
int i, j;
|
||||
for (i = 0, j = 0; sasToken[i] != '\0'; i++)
|
||||
{
|
||||
if (sasToken[i] == SE[j])
|
||||
{
|
||||
j++;
|
||||
if (j == sizeof(SE))
|
||||
{
|
||||
// i is still at the '=' position. We must advance it by 1.
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (j != sizeof(SE))
|
||||
{
|
||||
Logger.Error("Failed finding `se` field in SAS token");
|
||||
}
|
||||
else
|
||||
{
|
||||
int k = i;
|
||||
while (sasToken[k] != '\0' && sasToken[k] != '&') { k++; }
|
||||
|
||||
if (az_result_failed(
|
||||
az_span_atou32(az_span_create((uint8_t*)sasToken + i, k - i), &se_as_unix_time)))
|
||||
{
|
||||
Logger.Error("Failed parsing SAS token expiration timestamp");
|
||||
}
|
||||
}
|
||||
|
||||
return se_as_unix_time;
|
||||
}
|
||||
|
||||
static void mbedtls_hmac_sha256(az_span key, az_span payload, az_span signed_payload)
|
||||
{
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
|
||||
|
||||
mbedtls_md_init(&ctx);
|
||||
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
|
||||
mbedtls_md_hmac_starts(&ctx, (const unsigned char*)az_span_ptr(key), az_span_size(key));
|
||||
mbedtls_md_hmac_update(&ctx, (const unsigned char*)az_span_ptr(payload), az_span_size(payload));
|
||||
mbedtls_md_hmac_finish(&ctx, (byte*)az_span_ptr(signed_payload));
|
||||
mbedtls_md_free(&ctx);
|
||||
}
|
||||
|
||||
static void hmac_sha256_sign_signature(
|
||||
az_span decoded_key,
|
||||
az_span signature,
|
||||
az_span signed_signature,
|
||||
az_span* out_signed_signature)
|
||||
{
|
||||
mbedtls_hmac_sha256(decoded_key, signature, signed_signature);
|
||||
*out_signed_signature = az_span_slice(signed_signature, 0, 32);
|
||||
}
|
||||
|
||||
static void base64_encode_bytes(
|
||||
az_span decoded_bytes,
|
||||
az_span base64_encoded_bytes,
|
||||
az_span* out_base64_encoded_bytes)
|
||||
{
|
||||
size_t len;
|
||||
if (mbedtls_base64_encode(
|
||||
az_span_ptr(base64_encoded_bytes),
|
||||
(size_t)az_span_size(base64_encoded_bytes),
|
||||
&len,
|
||||
az_span_ptr(decoded_bytes),
|
||||
(size_t)az_span_size(decoded_bytes))
|
||||
!= 0)
|
||||
{
|
||||
Logger.Error("mbedtls_base64_encode fail");
|
||||
}
|
||||
|
||||
*out_base64_encoded_bytes = az_span_create(az_span_ptr(base64_encoded_bytes), (int32_t)len);
|
||||
}
|
||||
|
||||
static int decode_base64_bytes(
|
||||
az_span base64_encoded_bytes,
|
||||
az_span decoded_bytes,
|
||||
az_span* out_decoded_bytes)
|
||||
{
|
||||
memset(az_span_ptr(decoded_bytes), 0, (size_t)az_span_size(decoded_bytes));
|
||||
|
||||
size_t len;
|
||||
if (mbedtls_base64_decode(
|
||||
az_span_ptr(decoded_bytes),
|
||||
(size_t)az_span_size(decoded_bytes),
|
||||
&len,
|
||||
az_span_ptr(base64_encoded_bytes),
|
||||
(size_t)az_span_size(base64_encoded_bytes))
|
||||
!= 0)
|
||||
{
|
||||
Logger.Error("mbedtls_base64_decode fail");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*out_decoded_bytes = az_span_create(az_span_ptr(decoded_bytes), (int32_t)len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int iot_sample_generate_sas_base64_encoded_signed_signature(
|
||||
az_span sas_base64_encoded_key,
|
||||
az_span sas_signature,
|
||||
az_span sas_base64_encoded_signed_signature,
|
||||
az_span* out_sas_base64_encoded_signed_signature)
|
||||
{
|
||||
// Decode the sas base64 encoded key to use for HMAC signing.
|
||||
char sas_decoded_key_buffer[32];
|
||||
az_span sas_decoded_key = AZ_SPAN_FROM_BUFFER(sas_decoded_key_buffer);
|
||||
|
||||
if (decode_base64_bytes(sas_base64_encoded_key, sas_decoded_key, &sas_decoded_key) != 0)
|
||||
{
|
||||
Logger.Error("Failed generating encoded signed signature");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// HMAC-SHA256 sign the signature with the decoded key.
|
||||
char sas_hmac256_signed_signature_buffer[32];
|
||||
az_span sas_hmac256_signed_signature = AZ_SPAN_FROM_BUFFER(sas_hmac256_signed_signature_buffer);
|
||||
hmac_sha256_sign_signature(
|
||||
sas_decoded_key, sas_signature, sas_hmac256_signed_signature, &sas_hmac256_signed_signature);
|
||||
|
||||
// Base64 encode the result of the HMAC signing.
|
||||
base64_encode_bytes(
|
||||
sas_hmac256_signed_signature,
|
||||
sas_base64_encoded_signed_signature,
|
||||
out_sas_base64_encoded_signed_signature);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t iot_sample_get_epoch_expiration_time_from_minutes(uint32_t minutes)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
return (int64_t)(now + minutes * 60);
|
||||
}
|
||||
|
||||
az_span generate_sas_token(
|
||||
az_iot_hub_client* hub_client,
|
||||
az_span device_key,
|
||||
az_span sas_signature,
|
||||
unsigned int expiryTimeInMinutes,
|
||||
az_span sas_token)
|
||||
{
|
||||
az_result rc;
|
||||
// Create the POSIX expiration time from input minutes.
|
||||
uint64_t sas_duration = iot_sample_get_epoch_expiration_time_from_minutes(expiryTimeInMinutes);
|
||||
|
||||
// Get the signature that will later be signed with the decoded key.
|
||||
// az_span sas_signature = AZ_SPAN_FROM_BUFFER(signature);
|
||||
rc = az_iot_hub_client_sas_get_signature(hub_client, sas_duration, sas_signature, &sas_signature);
|
||||
if (az_result_failed(rc))
|
||||
{
|
||||
Logger.Error("Could not get the signature for SAS key: az_result return code " + rc);
|
||||
return AZ_SPAN_EMPTY;
|
||||
}
|
||||
|
||||
// Generate the encoded, signed signature (b64 encoded, HMAC-SHA256 signing).
|
||||
char b64enc_hmacsha256_signature[64];
|
||||
az_span sas_base64_encoded_signed_signature = AZ_SPAN_FROM_BUFFER(b64enc_hmacsha256_signature);
|
||||
|
||||
if (iot_sample_generate_sas_base64_encoded_signed_signature(
|
||||
device_key,
|
||||
sas_signature,
|
||||
sas_base64_encoded_signed_signature,
|
||||
&sas_base64_encoded_signed_signature) != 0)
|
||||
{
|
||||
Logger.Error("Failed generating SAS token signed signature");
|
||||
return AZ_SPAN_EMPTY;
|
||||
}
|
||||
|
||||
// Get the resulting MQTT password, passing the base64 encoded, HMAC signed bytes.
|
||||
size_t mqtt_password_length;
|
||||
rc = az_iot_hub_client_sas_get_password(
|
||||
hub_client,
|
||||
sas_duration,
|
||||
sas_base64_encoded_signed_signature,
|
||||
AZ_SPAN_EMPTY,
|
||||
(char*)az_span_ptr(sas_token),
|
||||
az_span_size(sas_token),
|
||||
&mqtt_password_length);
|
||||
|
||||
if (az_result_failed(rc))
|
||||
{
|
||||
Logger.Error("Could not get the password: az_result return code " + rc);
|
||||
return AZ_SPAN_EMPTY;
|
||||
}
|
||||
else
|
||||
{
|
||||
return az_span_slice(sas_token, 0, mqtt_password_length);
|
||||
}
|
||||
}
|
||||
|
||||
AzIoTSasToken::AzIoTSasToken(
|
||||
az_iot_hub_client* client,
|
||||
az_span deviceKey,
|
||||
az_span signatureBuffer,
|
||||
az_span sasTokenBuffer)
|
||||
{
|
||||
this->client = client;
|
||||
this->deviceKey = deviceKey;
|
||||
this->signatureBuffer = signatureBuffer;
|
||||
this->sasTokenBuffer = sasTokenBuffer;
|
||||
this->expirationUnixTime = 0;
|
||||
this->sasToken = AZ_SPAN_EMPTY;
|
||||
}
|
||||
|
||||
int AzIoTSasToken::Generate(unsigned int expiryTimeInMinutes)
|
||||
{
|
||||
this->sasToken = generate_sas_token(
|
||||
this->client,
|
||||
this->deviceKey,
|
||||
this->signatureBuffer,
|
||||
expiryTimeInMinutes,
|
||||
this->sasTokenBuffer);
|
||||
|
||||
if (is_az_span_empty(this->sasToken))
|
||||
{
|
||||
Logger.Error("Failed generating SAS token");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->expirationUnixTime = getSasTokenExpiration((const char*)az_span_ptr(this->sasToken));
|
||||
|
||||
if (this->expirationUnixTime == 0)
|
||||
{
|
||||
Logger.Error("Failed getting the SAS token expiration time");
|
||||
this->sasToken = AZ_SPAN_EMPTY;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AzIoTSasToken::IsExpired()
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
|
||||
if (now == INDEFINITE_TIME)
|
||||
{
|
||||
Logger.Error("Failed getting current time");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (now >= this->expirationUnixTime);
|
||||
}
|
||||
}
|
||||
|
||||
az_span AzIoTSasToken::Get() { return this->sasToken; }
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef AZIOTSASTOKEN_H
|
||||
#define AZIOTSASTOKEN_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <az_iot_hub_client.h>
|
||||
#include <az_span.h>
|
||||
|
||||
class AzIoTSasToken
|
||||
{
|
||||
public:
|
||||
AzIoTSasToken(
|
||||
az_iot_hub_client* client,
|
||||
az_span deviceKey,
|
||||
az_span signatureBuffer,
|
||||
az_span sasTokenBuffer);
|
||||
int Generate(unsigned int expiryTimeInMinutes);
|
||||
bool IsExpired();
|
||||
az_span Get();
|
||||
|
||||
private:
|
||||
az_iot_hub_client* client;
|
||||
az_span deviceKey;
|
||||
az_span signatureBuffer;
|
||||
az_span sasTokenBuffer;
|
||||
az_span sasToken;
|
||||
uint32_t expirationUnixTime;
|
||||
};
|
||||
|
||||
#endif // AZIOTSASTOKEN_H
|
|
@ -0,0 +1,250 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/*
|
||||
* This is an Arduino-based Azure IoT Hub sample for Arduino Portenta H7 boards.
|
||||
* It uses our Azure Embedded SDK for C to help interact with Azure IoT.
|
||||
* For reference, please visit https://github.com/azure/azure-sdk-for-c.
|
||||
*
|
||||
* To connect and work with Azure IoT Hub you need an MQTT client, connecting, subscribing
|
||||
* and publishing to specific topics to use the messaging features of the hub.
|
||||
* Our azure-sdk-for-c is an MQTT client support library, helping composing and parsing the
|
||||
* MQTT topic names and messages exchanged with the Azure IoT Hub.
|
||||
*
|
||||
* This sample performs the following tasks:
|
||||
* - Synchronize the device clock with a NTP server;
|
||||
* - Initialize our "az_iot_hub_client" (struct for data, part of our azure-sdk-for-c);
|
||||
* - Initialize the MQTT client (here we use Arduino's ArduinoMqttClient, which also handle the tcp connection and TLS);
|
||||
* - Connect the MQTT client (using server-certificate validation, SAS-tokens for client authentication);
|
||||
* - Periodically send telemetry data to the Azure IoT Hub.
|
||||
*
|
||||
* To properly connect to your Azure IoT Hub, please fill the information in the `iot_configs.h` file.
|
||||
*/
|
||||
|
||||
// C99 libraries
|
||||
#include <cstdlib>
|
||||
#include <cstdarg>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
// Libraries for MQTT client and WiFi connection
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <NTPClient_Generic.h>
|
||||
#include <ArduinoMqttClient.h>
|
||||
|
||||
// Azure IoT SDK for C includes
|
||||
#include <az_core.h>
|
||||
#include <az_iot.h>
|
||||
#include <azure_ca.h>
|
||||
|
||||
// Additional sample headers
|
||||
#include "AzIoTSasToken.h"
|
||||
#include "SerialLogger.h"
|
||||
#include "iot_configs.h"
|
||||
|
||||
// When developing for your own Arduino-based platform,
|
||||
// please follow the format '(ard;<platform>)'.
|
||||
#define AZURE_SDK_CLIENT_USER_AGENT "c/" AZ_SDK_VERSION_STRING "(ard;ststm32)"
|
||||
|
||||
// Utility macros and defines
|
||||
#define sizeofarray(a) (sizeof(a) / sizeof(a[0]))
|
||||
#define NTP_SERVER "pool.ntp.org"
|
||||
#define MQTT_QOS1 1
|
||||
#define DO_NOT_RETAIN_MSG 0
|
||||
#define SAS_TOKEN_DURATION_IN_MINUTES 60
|
||||
#define UNIX_TIME_NOV_13_2017 1510592825
|
||||
|
||||
#define PST_TIME_ZONE -8
|
||||
#define PST_TIME_ZONE_DAYLIGHT_SAVINGS_DIFF 1
|
||||
|
||||
#define GMT_OFFSET_SECS (PST_TIME_ZONE * 3600)
|
||||
#define GMT_OFFSET_SECS_DST ((PST_TIME_ZONE + PST_TIME_ZONE_DAYLIGHT_SAVINGS_DIFF) * 3600)
|
||||
|
||||
// Translate iot_configs.h defines into variables used by the sample
|
||||
static const char *ssid = IOT_CONFIG_WIFI_SSID;
|
||||
static const char *password = IOT_CONFIG_WIFI_PASSWORD;
|
||||
static const char *host = IOT_CONFIG_IOTHUB_FQDN;
|
||||
static const char *mqtt_broker_uri = "mqtts://" IOT_CONFIG_IOTHUB_FQDN;
|
||||
static const char *device_id = IOT_CONFIG_DEVICE_ID;
|
||||
static const int mqtt_port = AZ_IOT_DEFAULT_MQTT_CONNECT_PORT;
|
||||
|
||||
// Memory allocated for the sample's variables and structures.
|
||||
static az_iot_hub_client client;
|
||||
|
||||
static char mqtt_client_id[128];
|
||||
static char mqtt_username[128];
|
||||
static char mqtt_password[200];
|
||||
static uint8_t sas_signature_buffer[256];
|
||||
static unsigned long next_telemetry_send_time_ms = 0;
|
||||
static char telemetry_topic[128];
|
||||
static uint8_t telemetry_payload[100];
|
||||
static uint32_t telemetry_send_count = 0;
|
||||
|
||||
static WiFiClient wifiClient;
|
||||
static MqttClient mqttClient(wifiClient);
|
||||
|
||||
static WiFiUDP ntpUDP;
|
||||
static NTPClient timeClient(ntpUDP, NTP_SERVER, GMT_OFFSET_SECS_DST);
|
||||
|
||||
static AzIoTSasToken sasToken(
|
||||
&client,
|
||||
AZ_SPAN_FROM_STR(IOT_CONFIG_DEVICE_KEY),
|
||||
AZ_SPAN_FROM_BUFFER(sas_signature_buffer),
|
||||
AZ_SPAN_FROM_BUFFER(mqtt_password));
|
||||
|
||||
void onMqttMessage(int length) {
|
||||
|
||||
Serial.print("Received a message with topic '");
|
||||
Serial.print(mqttClient.messageTopic());
|
||||
Serial.print("', duplicate = ");
|
||||
Serial.print(mqttClient.messageDup() ? "true" : "false");
|
||||
Serial.print(", QoS = ");
|
||||
Serial.print(mqttClient.messageQoS());
|
||||
Serial.print(", retained = ");
|
||||
Serial.print(mqttClient.messageRetain() ? "true" : "false");
|
||||
Serial.print("', length ");
|
||||
Serial.print(length);
|
||||
Serial.println(" bytes:");
|
||||
|
||||
while (mqttClient.available()) {
|
||||
Serial.print((char)mqttClient.read());
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
static char *getTelemetryPayload() {
|
||||
az_span temp_span = az_span_create(telemetry_payload, sizeof(telemetry_payload));
|
||||
temp_span = az_span_copy(temp_span, AZ_SPAN_FROM_STR("{ \"msgCount\": "));
|
||||
(void)az_span_u32toa(temp_span, telemetry_send_count++, &temp_span);
|
||||
temp_span = az_span_copy(temp_span, AZ_SPAN_FROM_STR(" }"));
|
||||
temp_span = az_span_copy_u8(temp_span, '\0');
|
||||
|
||||
return (char *)telemetry_payload;
|
||||
}
|
||||
|
||||
static void sendTelemetry() {
|
||||
Logger.Info("Sending telemetry . . . ");
|
||||
if (az_result_failed(az_iot_hub_client_telemetry_get_publish_topic(
|
||||
&client, NULL, telemetry_topic, sizeof(telemetry_topic), NULL))) {
|
||||
Logger.Error("Failed az_iot_hub_client_telemetry_get_publish_topic");
|
||||
return;
|
||||
}
|
||||
char *telemetry = getTelemetryPayload();
|
||||
mqttClient.beginMessage(telemetry_topic, sizeof(telemetry), DO_NOT_RETAIN_MSG, MQTT_QOS1, false);
|
||||
mqttClient.print(telemetry);
|
||||
mqttClient.endMessage();
|
||||
Logger.Info("OK");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
static void initializeIoTHubClient() {
|
||||
az_iot_hub_client_options options = az_iot_hub_client_options_default();
|
||||
options.user_agent = AZ_SPAN_FROM_STR(AZURE_SDK_CLIENT_USER_AGENT);
|
||||
|
||||
if (az_result_failed(az_iot_hub_client_init(
|
||||
&client,
|
||||
az_span_create((uint8_t *)host, strlen(host)),
|
||||
az_span_create((uint8_t *)device_id, strlen(device_id)),
|
||||
&options))) {
|
||||
Logger.Error("Failed initializing Azure IoT Hub client");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t client_id_length;
|
||||
if (az_result_failed(az_iot_hub_client_get_client_id(
|
||||
&client, mqtt_client_id, sizeof(mqtt_client_id) - 1, &client_id_length))) {
|
||||
Logger.Error("Failed getting client id");
|
||||
return;
|
||||
}
|
||||
|
||||
if (az_result_failed(az_iot_hub_client_get_user_name(
|
||||
&client, mqtt_username, sizeofarray(mqtt_username), NULL))) {
|
||||
Logger.Error("Failed to get MQTT clientId, return code");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Info("Client ID: " + String(mqtt_client_id));
|
||||
Logger.Info("Username: " + String(mqtt_username));
|
||||
}
|
||||
|
||||
static int initializeMqttClient() {
|
||||
int result;
|
||||
|
||||
int token_result = sasToken.Generate(SAS_TOKEN_DURATION_IN_MINUTES);
|
||||
if (token_result != 0) {
|
||||
Logger.Error("Failed generating SAS token");
|
||||
result = 1;
|
||||
}
|
||||
|
||||
Logger.Info("MQTT client target uri set to " + String(mqtt_broker_uri));
|
||||
if (!mqttClient.connect(mqtt_broker_uri, mqtt_port)) {
|
||||
Logger.Error("Could not start mqtt client; error code:" + String(mqttClient.connectError()));
|
||||
mqttClient.stop();
|
||||
result = 1;
|
||||
} else {
|
||||
Logger.Info("MQTT client started");
|
||||
mqttClient.setId(mqtt_client_id);
|
||||
mqttClient.setKeepAliveInterval(30);
|
||||
mqttClient.setCleanSession(false);
|
||||
mqttClient.onMessage(onMqttMessage);
|
||||
|
||||
Logger.Info("Subscribing to topic:" + String(telemetry_topic));
|
||||
mqttClient.subscribe(telemetry_topic, MQTT_QOS1);
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void connectToWiFi() {
|
||||
Logger.Info("Connecting to WIFI SSID " + String(ssid));
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(3000);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
Serial.println("");
|
||||
|
||||
Logger.Info("WiFi connected, IP address: " + String(WiFi.localIP()));
|
||||
}
|
||||
|
||||
static void initializeTime() {
|
||||
Logger.Info("Setting time using SNTP");
|
||||
|
||||
timeClient.begin();
|
||||
|
||||
Logger.Info("Time initialized!");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {}
|
||||
|
||||
connectToWiFi();
|
||||
initializeTime();
|
||||
initializeIoTHubClient();
|
||||
initializeMqttClient();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
connectToWiFi();
|
||||
}
|
||||
|
||||
if (sasToken.IsExpired() || !mqttClient.connected()) {
|
||||
Logger.Info("SAS token expired; reconnecting with a new one.");
|
||||
initializeMqttClient();
|
||||
}
|
||||
|
||||
mqttClient.poll();
|
||||
|
||||
timeClient.update();
|
||||
|
||||
if (millis() > next_telemetry_send_time_ms) {
|
||||
sendTelemetry();
|
||||
next_telemetry_send_time_ms = millis() + TELEMETRY_FREQUENCY_MILLISECS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "SerialLogger.h"
|
||||
#include <time.h>
|
||||
|
||||
#define UNIX_EPOCH_START_YEAR 1900
|
||||
|
||||
static void writeTime()
|
||||
{
|
||||
struct tm* ptm;
|
||||
time_t now = time(NULL);
|
||||
|
||||
ptm = gmtime(&now);
|
||||
|
||||
Serial.print(ptm->tm_year + UNIX_EPOCH_START_YEAR);
|
||||
Serial.print("/");
|
||||
Serial.print(ptm->tm_mon + 1);
|
||||
Serial.print("/");
|
||||
Serial.print(ptm->tm_mday);
|
||||
Serial.print(" ");
|
||||
|
||||
if (ptm->tm_hour < 10)
|
||||
{
|
||||
Serial.print(0);
|
||||
}
|
||||
|
||||
Serial.print(ptm->tm_hour);
|
||||
Serial.print(":");
|
||||
|
||||
if (ptm->tm_min < 10)
|
||||
{
|
||||
Serial.print(0);
|
||||
}
|
||||
|
||||
Serial.print(ptm->tm_min);
|
||||
Serial.print(":");
|
||||
|
||||
if (ptm->tm_sec < 10)
|
||||
{
|
||||
Serial.print(0);
|
||||
}
|
||||
|
||||
Serial.print(ptm->tm_sec);
|
||||
}
|
||||
|
||||
SerialLogger::SerialLogger() { Serial.begin(SERIAL_LOGGER_BAUD_RATE); }
|
||||
|
||||
void SerialLogger::Info(String message)
|
||||
{
|
||||
writeTime();
|
||||
Serial.print(" [INFO] ");
|
||||
Serial.println(message);
|
||||
}
|
||||
|
||||
void SerialLogger::Error(String message)
|
||||
{
|
||||
writeTime();
|
||||
Serial.print(" [ERROR] ");
|
||||
Serial.println(message);
|
||||
}
|
||||
|
||||
SerialLogger Logger;
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef SERIALLOGGER_H
|
||||
#define SERIALLOGGER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifndef SERIAL_LOGGER_BAUD_RATE
|
||||
#define SERIAL_LOGGER_BAUD_RATE 115200
|
||||
#endif
|
||||
|
||||
class SerialLogger
|
||||
{
|
||||
public:
|
||||
SerialLogger();
|
||||
void Info(String message);
|
||||
void Error(String message);
|
||||
};
|
||||
|
||||
extern SerialLogger Logger;
|
||||
|
||||
#endif // SERIALLOGGER_H
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Wifi
|
||||
#define IOT_CONFIG_WIFI_SSID "SSID"
|
||||
#define IOT_CONFIG_WIFI_PASSWORD "PWD"
|
||||
|
||||
// Azure IoT
|
||||
#define IOT_CONFIG_IOTHUB_FQDN "[your Azure IoT host name].azure-devices.net"
|
||||
#define IOT_CONFIG_DEVICE_ID "Device ID"
|
||||
#define IOT_CONFIG_DEVICE_KEY "Device Key"
|
||||
|
||||
// Publish 1 message every 2 seconds
|
||||
#define TELEMETRY_FREQUENCY_MILLISECS 2000
|
|
@ -0,0 +1,199 @@
|
|||
---
|
||||
page_type: sample
|
||||
description: Connecting a Arduino Portenta H7 device to Azure IoT using the Azure SDK for Embedded C
|
||||
languages:
|
||||
- c
|
||||
products:
|
||||
- azure-iot
|
||||
- azure-iot-pnp
|
||||
- azure-iot-dps
|
||||
- azure-iot-hub
|
||||
---
|
||||
|
||||
# How to Setup and Run Azure SDK for Embedded C IoT Hub Client on Arduino Portenta H7
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [What is Covered](#what-is-covered)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Setup and Run Instructions](#setup-and-run-instructions)
|
||||
- [Certificates - Important to know](#certificates---important-to-know)
|
||||
- [Additional Information](#additional-information)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
|
||||
## Introduction
|
||||
|
||||
This is a guide outlining how to run an Azure SDK for Embedded C IoT Hub telemetry sample on an Arduino Portenta H7 development board.
|
||||
|
||||
### What is Covered
|
||||
|
||||
- Configuration instructions for the Arduino IDE to compile a sample using the Azure SDK for Embedded C.
|
||||
- Configuration, build, and run instructions for the IoT Hub telemetry sample.
|
||||
|
||||
_The following was run on Windows 11 environments, with Arduino IDE 1.8.19 and Arduino Mbed OS Portenta Boards module 2.8.0._
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Have an [Azure account](https://azure.microsoft.com/) created.
|
||||
- Have an [Azure IoT Hub](https://docs.microsoft.com/azure/iot-hub/iot-hub-create-through-portal) created.
|
||||
- Have a [logical device](https://docs.microsoft.com/azure/iot-hub/iot-hub-create-through-portal#register-a-new-device-in-the-iot-hub) created in your Azure IoT Hub using the authentication type "Symmetric Key".
|
||||
|
||||
NOTE: Device keys are used to automatically generate a SAS token for authentication, which is only valid for one hour.
|
||||
|
||||
- Have the latest [Arduino IDE](https://www.arduino.cc/en/Main/Software) installed.
|
||||
|
||||
- Have the [Arduino Portenta H7 board packages](https://docs.arduino.cc/tutorials/portenta-h7/por-ard-gs) installed on Arduino IDE. Mbed OS boards are not natively supported by Arduino IDE, so you need to add them manually.
|
||||
|
||||
- Have one of the following interfaces to your Azure IoT Hub set up:
|
||||
- [Azure Command Line Interface](https://docs.microsoft.com/cli/azure/install-azure-cli?view=azure-cli-latest) (Azure CLI) utility installed, along with the [Azure IoT CLI extension](https://github.com/Azure/azure-iot-cli-extension).
|
||||
|
||||
On Windows:
|
||||
|
||||
Download and install: https://aka.ms/installazurecliwindows
|
||||
|
||||
```powershell
|
||||
PS C:\>az extension add --name azure-iot
|
||||
```
|
||||
|
||||
On Linux:
|
||||
|
||||
```bash
|
||||
$ curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
|
||||
$ az extension add --name azure-iot
|
||||
```
|
||||
|
||||
A list of all the Azure IoT CLI extension commands can be found [here](https://docs.microsoft.com/cli/azure/iot?view=azure-cli-latest).
|
||||
|
||||
- The most recent version of [Azure IoT Explorer](https://github.com/Azure/azure-iot-explorer/releases) installed. More instruction on its usage can be found [here](https://docs.microsoft.com/azure/iot-pnp/howto-use-iot-explorer).
|
||||
|
||||
NOTE: This guide demonstrates use of the Azure CLI and does NOT demonstrate use of Azure IoT Explorer.
|
||||
|
||||
## Setup and Run Instructions
|
||||
|
||||
1. Run the Arduino IDE.
|
||||
|
||||
2. Install the required libraries:
|
||||
1. In the Arduino IDE, go to menu `Sketch`, `Include Library`, `Manage Libraries...`.
|
||||
1. Search for and install the following:
|
||||
- `Azure SDK for C` by Microsoft ([repo](https://github.com/Azure/azure-sdk-for-c-arduino))
|
||||
- `ArduinoMqttLibrary` by Arduino ([repo](https://github.com/arduino-libraries/ArduinoMqttClient))
|
||||
- `NTPClient_Generic` ([repo](https://github.com/khoih-prog/NTPClient_Generic))
|
||||
3. Open the Arduino Portenta H7 sample.
|
||||
|
||||
1. On the Arduino IDE, go to menu `File`, `Examples`, `azure-sdk-for-c`.
|
||||
1. Click on `Azure_IoT_Hub_PortentaH7` to open the sample.
|
||||
|
||||
4. Configure the Arduino Portenta H7 sample.
|
||||
|
||||
Enter your Azure IoT Hub and device information into the sample's `iot_configs.h`.
|
||||
|
||||
5. Connect the Arduino Portenta H7 board to your USB port.
|
||||
|
||||
6. On the Arduino IDE, select the board and port.
|
||||
|
||||
- Go to menu `Tools`, `Board` and select `Arduino Mbed OS Portenta Boards`/`Arduino Portenta H7 (M7 core)` .
|
||||
- Go to menu `Tools`, `Port` and select the port to which the microcontroller is connected.
|
||||
|
||||
7. Upload the sketch.
|
||||
|
||||
- Go to menu `Sketch` and click on `Upload`.
|
||||
|
||||
8. Monitor the MCU (microcontroller) locally via the Serial Port.
|
||||
|
||||
- Go to menu `Tools`, `Serial Monitor`.
|
||||
|
||||
If you perform this step right away after uploading the sketch, the serial monitor will show an output similar to the following upon success:
|
||||
|
||||
```text
|
||||
Connecting to WIFI SSID buckaroo
|
||||
.......................WiFi connected, IP address:
|
||||
192.168.1.123
|
||||
Setting time using SNTP..............................done!
|
||||
Current time: Thu May 28 02:55:05 2020
|
||||
Client ID: mydeviceid
|
||||
Username: myiothub.azure-devices.net/mydeviceid/?api-version=2018-06-30&DeviceClientType=c%2F1.0.0
|
||||
Password: SharedAccessSignature sr=myiothub.azure-devices.net%2Fdevices%2Fmydeviceid&sig=placeholder-password&se=1590620105
|
||||
MQTT connecting ... connected.
|
||||
```
|
||||
|
||||
9. Monitor the telemetry messages sent to the Azure IoT Hub using the connection string for the policy name `iothubowner` found under "Shared access policies" on your IoT Hub in the Azure portal.
|
||||
|
||||
```bash
|
||||
$ az iot hub monitor-events --login <your Azure IoT Hub owner connection string in quotes> --device-id <your device id>
|
||||
```
|
||||
|
||||
<details><summary><i>Expected telemetry output:</i></summary>
|
||||
<p>
|
||||
|
||||
```bash
|
||||
Starting event monitor, filtering on device: mydeviceid, use ctrl-c to stop...
|
||||
{
|
||||
"event": {
|
||||
"origin": "mydeviceid",
|
||||
"payload": "payload"
|
||||
}
|
||||
}
|
||||
{
|
||||
"event": {
|
||||
"origin": "mydeviceid",
|
||||
"payload": "payload"
|
||||
}
|
||||
}
|
||||
{
|
||||
"event": {
|
||||
"origin": "mydeviceid",
|
||||
"payload": "payload"
|
||||
}
|
||||
}
|
||||
{
|
||||
"event": {
|
||||
"origin": "mydeviceid",
|
||||
"payload": "payload"
|
||||
}
|
||||
}
|
||||
{
|
||||
"event": {
|
||||
"origin": "mydeviceid",
|
||||
"payload": "payload"
|
||||
}
|
||||
}
|
||||
{
|
||||
"event": {
|
||||
"origin": "mydeviceid",
|
||||
"payload": "payload"
|
||||
}
|
||||
}
|
||||
^CStopping event monitor...
|
||||
```
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
## Certificates - Important to know
|
||||
|
||||
The Azure IoT service certificates presented during TLS negotiation shall always be validated, on the device, using the appropriate trusted root CA certificate(s).
|
||||
|
||||
For the Arduino Portenta H7 sample, our script `generate_arduino_zip_library.sh` automatically downloads the root certificate used in the United States regions (Baltimore CA certificate) and adds it to the Arduino sketch project.
|
||||
|
||||
For other regions (and private cloud environments), please use the appropriate root CA certificate.
|
||||
|
||||
### Additional Information
|
||||
|
||||
For important information and additional guidance about certificates, please refer to [this blog post](https://techcommunity.microsoft.com/t5/internet-of-things/azure-iot-tls-changes-are-coming-and-why-you-should-care/ba-p/1658456) from the security team.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- The error policy for the Embedded C SDK client library is documented [here](https://github.com/Azure/azure-sdk-for-c/blob/main/sdk/docs/iot/mqtt_state_machine.md#error-policy).
|
||||
- File an issue via [Github Issues](https://github.com/Azure/azure-sdk-for-c/issues/new/choose).
|
||||
- Check [previous questions](https://stackoverflow.com/questions/tagged/azure+c) or ask new ones on StackOverflow using the `azure` and `c` tags.
|
||||
|
||||
## Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Find more contributing details [here](https://github.com/Azure/azure-sdk-for-c/blob/main/CONTRIBUTING.md).
|
||||
|
||||
### License
|
||||
|
||||
This Azure SDK for C Arduino library is licensed under [MIT](https://github.com/Azure/azure-sdk-for-c-arduino/blob/main/LICENSE) license.
|
||||
|
||||
Azure SDK for Embedded C is licensed under the [MIT](https://github.com/Azure/azure-sdk-for-c/blob/main/LICENSE) license.
|
Загрузка…
Ссылка в новой задаче