I’m using the grove SCD30 sensor module on WIO Terminal and want to send the data to Azure IOT Central.
I’m adapting the code from here
Releases · SeeedJP/wioterminal-aziot-example · GitHub,
followed the WIO Terminal Classroom #4 on Youtube Wio Terminal Classroom with IoT #4 | Microsoft Azure IoT Central with IoT Plug and Play (youtube.com)
and used the example code from the seeeds SCD30 libary (SCD30_Example) . This code contains a delay(2000) in the main loop.
here is my merged code, which runs. However, the serial printing of the SCD30 values in the loop() is not working. The serial output is just "… Sent Telemetry 104, Sent Telemetry 105, …) Why is this serial printing not working?
Why do I need to add this delay(2000)? In the WIO Classroom example with the gardening system, they don’t have to add it, however there they use an analog RH/T sensor DHT11. So is this delay necessary for I2C?
#include <Arduino.h>
#include "Config.h"
#include "Storage.h"
#include "Signature.h"
#include "AzureDpsClient.h"
#include "CliMode.h"
#include "Bitmap.h"
#include "SPI.h"
#include "SCD30.h"
#include "TFT_eSPI.h"
#include <rpcWiFiClientSecure.h>
#include <PubSubClient.h>
#include <WiFiUdp.h>
#include <NTP.h>
#include <az_json.h>
#include <az_result.h>
#include <az_span.h>
#include <az_iot_hub_client.h>
#define MQTT_PACKET_SIZE 1024
#if defined(ARDUINO_ARCH_AVR)
#pragma message("Defined architecture for ARDUINO_ARCH_AVR.")
#define SERIAL Serial
#elif defined(ARDUINO_ARCH_SAM)
#pragma message("Defined architecture for ARDUINO_ARCH_SAM.")
#define SERIAL SerialUSB
#elif defined(ARDUINO_ARCH_SAMD)
#pragma message("Defined architecture for ARDUINO_ARCH_SAMD.")
#define SERIAL SerialUSB
#elif defined(ARDUINO_ARCH_STM32F4)
#pragma message("Defined architecture for ARDUINO_ARCH_STM32F4.")
#define SERIAL SerialUSB
#else
#pragma message("Not found any architecture.")
#define SERIAL Serial
#endif
TFT_eSPI tft; //Initializing TFT LCD
TFT_eSprite spr = TFT_eSprite(&tft); //Initializing buffer
//variable definitions
int temp;
int humi;
int co2;
int light;
const char CA_CERTS[] =
//////////////////////////////////////
// Common Name: Baltimore CyberTrust Root
// Organizational Name: Baltimore
// From Date: 2000-05-13 03:46:00
// To Date: 2025-05-13 08:59:00
"-----BEGIN CERTIFICATE-----\n"
"MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ\n"
"RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD\n"
"VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX\n"
"DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y\n"
"ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy\n"
"VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr\n"
"mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr\n"
"IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK\n"
"mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\n"
"XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy\n"
"dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye\n"
"jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1\n"
"BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3\n"
"DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92\n"
"9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx\n"
"jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0\n"
"Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz\n"
"ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\n"
"R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\n"
"-----END CERTIFICATE-----\n"
//////////////////////////////////////
// Common Name: DigiCert Global Root G2
// Organizational Name: DigiCert Inc
// From Date: 2013-8-1 12:00:00
// To Date: 2038-1-15 12:00:00
"-----BEGIN CERTIFICATE-----\n"
"MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\n"
"MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT\n"
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG\n"
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI\n"
"2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx\n"
"1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ\n"
"q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz\n"
"tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ\n"
"vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP\n"
"BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV\n"
"5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY\n"
"1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4\n"
"NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG\n"
"Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91\n"
"8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe\n"
"pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl\n"
"MrY=\n"
"-----END CERTIFICATE-----\n"
;
WiFiClientSecure wifi_client;
PubSubClient mqtt_client(wifi_client);
WiFiUDP wifi_udp;
NTP ntp(wifi_udp);
std::string HubHost;
std::string DeviceId;
#define AZ_RETURN_IF_FAILED(exp) \
do \
{ \
az_result const _result = (exp); \
if (az_result_failed(_result)) \
{ \
return _result; \
} \
} while (0)
////////////////////////////////////////////////////////////////////////////////
//
#define DLM "\r\n"
static String StringVFormat(const char* format, va_list arg)
{
const int len = vsnprintf(nullptr, 0, format, arg);
char str[len + 1];
vsnprintf(str, sizeof(str), format, arg);
return String{ str };
}
static void Abort(const char* format, ...)
{
va_list arg;
va_start(arg, format);
String str{ StringVFormat(format, arg) };
va_end(arg);
Serial.print(String::format("ABORT: %s" DLM, str.c_str()));
while (true) {}
}
static void Log(const char* format, ...)
{
va_list arg;
va_start(arg, format);
String str{ StringVFormat(format, arg) };
va_end(arg);
Serial.print(str);
}
////////////////////////////////////////////////////////////////////////////////
// Display
static void DisplayPrintf(const char* format, ...)
{
va_list arg;
va_start(arg, format);
String str{ StringVFormat(format, arg) };
va_end(arg);
Log("%s\n", str.c_str());
}
////////////////////////////////////////////////////////////////////////////////
// Button
#include <AceButton.h>
using namespace ace_button;
enum class ButtonId
{
RIGHT = 0,
CENTER,
LEFT,
};
static const int ButtonNumber = 3;
static AceButton Buttons[ButtonNumber];
static bool ButtonsClicked[ButtonNumber];
static void ButtonEventHandler(AceButton* button, uint8_t eventType, uint8_t buttonState)
{
const uint8_t id = button->getId();
if (ButtonNumber <= id) return;
switch (eventType)
{
case AceButton::kEventClicked:
switch (static_cast<ButtonId>(id))
{
case ButtonId::RIGHT:
DisplayPrintf("Right button was clicked");
break;
case ButtonId::CENTER:
DisplayPrintf("Center button was clicked");
break;
case ButtonId::LEFT:
DisplayPrintf("Left button was clicked");
break;
}
ButtonsClicked[id] = true;
break;
}
}
static void ButtonInit()
{
Buttons[static_cast<int>(ButtonId::RIGHT)].init(WIO_KEY_A, HIGH, static_cast<uint8_t>(ButtonId::RIGHT));
Buttons[static_cast<int>(ButtonId::CENTER)].init(WIO_KEY_B, HIGH, static_cast<uint8_t>(ButtonId::CENTER));
Buttons[static_cast<int>(ButtonId::LEFT)].init(WIO_KEY_C, HIGH, static_cast<uint8_t>(ButtonId::LEFT));
ButtonConfig* buttonConfig = ButtonConfig::getSystemButtonConfig();
buttonConfig->setEventHandler(ButtonEventHandler);
buttonConfig->setFeature(ButtonConfig::kFeatureClick);
for (int i = 0; i < ButtonNumber; ++i) ButtonsClicked[i] = false;
}
static void ButtonDoWork()
{
for (int i = 0; static_cast<size_t>(i) < std::extent<decltype(Buttons)>::value; ++i)
{
Buttons[i].check();
}
}
////////////////////////////////////////////////////////////////////////////////
// Azure IoT DPS
static AzureDpsClient DpsClient;
static unsigned long DpsPublishTimeOfQueryStatus = 0;
static void MqttSubscribeCallbackDPS(char* topic, byte* payload, unsigned int length);
static int RegisterDeviceToDPS(const std::string& endpoint, const std::string& idScope, const std::string& registrationId, const std::string& symmetricKey, const uint64_t& expirationEpochTime, std::string* hubHost, std::string* deviceId)
{
std::string endpointAndPort{ endpoint };
endpointAndPort += ":";
endpointAndPort += std::to_string(8883);
if (DpsClient.Init(endpointAndPort, idScope, registrationId) != 0) return -1;
const std::string mqttClientId = DpsClient.GetMqttClientId();
const std::string mqttUsername = DpsClient.GetMqttUsername();
const std::vector<uint8_t> signature = DpsClient.GetSignature(expirationEpochTime);
const std::string encryptedSignature = GenerateEncryptedSignature(symmetricKey, signature);
const std::string mqttPassword = DpsClient.GetMqttPassword(encryptedSignature, expirationEpochTime);
const std::string registerPublishTopic = DpsClient.GetRegisterPublishTopic();
const std::string registerSubscribeTopic = DpsClient.GetRegisterSubscribeTopic();
Log("DPS:" DLM);
Log(" Endpoint = %s" DLM, endpoint.c_str());
Log(" Id scope = %s" DLM, idScope.c_str());
Log(" Registration id = %s" DLM, registrationId.c_str());
Log(" MQTT client id = %s" DLM, mqttClientId.c_str());
Log(" MQTT username = %s" DLM, mqttUsername.c_str());
//Log(" MQTT password = %s" DLM, mqttPassword.c_str());
wifi_client.setCACert(CA_CERTS);
mqtt_client.setBufferSize(MQTT_PACKET_SIZE);
mqtt_client.setServer(endpoint.c_str(), 8883);
mqtt_client.setCallback(MqttSubscribeCallbackDPS);
DisplayPrintf("Connecting to Azure IoT Hub DPS...");
if (!mqtt_client.connect(mqttClientId.c_str(), mqttUsername.c_str(), mqttPassword.c_str())) return -2;
mqtt_client.subscribe(registerSubscribeTopic.c_str());
mqtt_client.publish(registerPublishTopic.c_str(), "{payload:{\"modelId\":\"" IOT_CONFIG_MODEL_ID "\"}}");
while (!DpsClient.IsRegisterOperationCompleted())
{
mqtt_client.loop();
if (DpsPublishTimeOfQueryStatus > 0 && millis() >= DpsPublishTimeOfQueryStatus)
{
mqtt_client.publish(DpsClient.GetQueryStatusPublishTopic().c_str(), "");
Log("Client sent operation query message" DLM);
DpsPublishTimeOfQueryStatus = 0;
}
}
if (!DpsClient.IsAssigned()) return -3;
mqtt_client.disconnect();
*hubHost = DpsClient.GetHubHost();
*deviceId = DpsClient.GetDeviceId();
Log("Device provisioned:" DLM);
Log(" Hub host = %s" DLM, hubHost->c_str());
Log(" Device id = %s" DLM, deviceId->c_str());
return 0;
}
static void MqttSubscribeCallbackDPS(char* topic, byte* payload, unsigned int length)
{
Log("Subscribe:" DLM " %s" DLM " %.*s" DLM, topic, length, (const char*)payload);
if (DpsClient.RegisterSubscribeWork(topic, std::vector<uint8_t>(payload, payload + length)) != 0)
{
Log("Failed to parse topic and/or payload" DLM);
return;
}
if (!DpsClient.IsRegisterOperationCompleted())
{
const int waitSeconds = DpsClient.GetWaitBeforeQueryStatusSeconds();
Log("Querying after %u seconds..." DLM, waitSeconds);
DpsPublishTimeOfQueryStatus = millis() + waitSeconds * 1000;
}
}
////////////////////////////////////////////////////////////////////////////////
// Azure IoT Hub
static az_iot_hub_client HubClient;
static int SendCommandResponse(az_iot_hub_client_method_request* request, uint16_t status, az_span response);
static void MqttSubscribeCallbackHub(char* topic, byte* payload, unsigned int length);
static int ConnectToHub(az_iot_hub_client* iot_hub_client, const std::string& host, const std::string& deviceId, const std::string& symmetricKey, const uint64_t& expirationEpochTime)
{
static std::string deviceIdCache;
deviceIdCache = deviceId;
const az_span hostSpan{ az_span_create((uint8_t*)&host[0], host.size()) };
const az_span deviceIdSpan{ az_span_create((uint8_t*)&deviceIdCache[0], deviceIdCache.size()) };
az_iot_hub_client_options options = az_iot_hub_client_options_default();
options.model_id = AZ_SPAN_LITERAL_FROM_STR(IOT_CONFIG_MODEL_ID);
if (az_result_failed(az_iot_hub_client_init(iot_hub_client, hostSpan, deviceIdSpan, &options))) return -1;
char mqttClientId[128];
size_t client_id_length;
if (az_result_failed(az_iot_hub_client_get_client_id(iot_hub_client, mqttClientId, sizeof(mqttClientId), &client_id_length))) return -4;
char mqttUsername[256];
if (az_result_failed(az_iot_hub_client_get_user_name(iot_hub_client, mqttUsername, sizeof(mqttUsername), NULL))) return -5;
char mqttPassword[300];
uint8_t signatureBuf[256];
az_span signatureSpan = az_span_create(signatureBuf, sizeof(signatureBuf));
az_span signatureValidSpan;
if (az_result_failed(az_iot_hub_client_sas_get_signature(iot_hub_client, expirationEpochTime, signatureSpan, &signatureValidSpan))) return -2;
const std::vector<uint8_t> signature(az_span_ptr(signatureValidSpan), az_span_ptr(signatureValidSpan) + az_span_size(signatureValidSpan));
const std::string encryptedSignature = GenerateEncryptedSignature(symmetricKey, signature);
az_span encryptedSignatureSpan = az_span_create((uint8_t*)&encryptedSignature[0], encryptedSignature.size());
if (az_result_failed(az_iot_hub_client_sas_get_password(iot_hub_client, expirationEpochTime, encryptedSignatureSpan, AZ_SPAN_EMPTY, mqttPassword, sizeof(mqttPassword), NULL))) return -3;
Log("Hub:" DLM);
Log(" Host = %s" DLM, host.c_str());
Log(" Device id = %s" DLM, deviceIdCache.c_str());
Log(" MQTT client id = %s" DLM, mqttClientId);
Log(" MQTT username = %s" DLM, mqttUsername);
//Log(" MQTT password = %s" DLM, mqttPassword);
wifi_client.setCACert(CA_CERTS);
mqtt_client.setBufferSize(MQTT_PACKET_SIZE);
mqtt_client.setServer(host.c_str(), 8883);
mqtt_client.setCallback(MqttSubscribeCallbackHub);
if (!mqtt_client.connect(mqttClientId, mqttUsername, mqttPassword)) return -6;
mqtt_client.subscribe(AZ_IOT_HUB_CLIENT_METHODS_SUBSCRIBE_TOPIC);
mqtt_client.subscribe(AZ_IOT_HUB_CLIENT_C2D_SUBSCRIBE_TOPIC);
return 0;
}
static az_result SendTelemetry()
{
int light;
light = analogRead(WIO_LIGHT) * 100 / 1023;
char creationTime[20 + 1]; // yyyy-mm-ddThh:mm:ss.sssZ
{
const time_t t = ntp.epoch();
struct tm tm;
gmtime_r(&t, &tm);
const int len = snprintf(creationTime, sizeof(creationTime), "%d-%02d-%02dT%02d:%02d:%02dZ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
if (len != sizeof(creationTime) - 1)
{
Log("Failed snprintf" DLM);
return AZ_ERROR_NOT_SUPPORTED;
}
}
az_iot_message_properties props;
uint8_t propsBuffer[128];
if (az_result_failed(az_iot_message_properties_init(&props, az_span_create(propsBuffer, sizeof(propsBuffer)), 0)))
{
Log("Failed az_iot_message_properties_init" DLM);
return AZ_ERROR_NOT_SUPPORTED;
}
if (az_result_failed(az_iot_message_properties_append(&props, AZ_SPAN_FROM_STR("iothub-creation-time-utc"), az_span_create(reinterpret_cast<uint8_t*>(creationTime), strlen(creationTime)))))
{
Log("Failed az_iot_message_properties_append" DLM);
return AZ_ERROR_NOT_SUPPORTED;
}
char telemetry_topic[128];
if (az_result_failed(az_iot_hub_client_telemetry_get_publish_topic(&HubClient, &props, telemetry_topic, sizeof(telemetry_topic), NULL)))
{
Log("Failed az_iot_hub_client_telemetry_get_publish_topic" DLM);
return AZ_ERROR_NOT_SUPPORTED;
}
az_json_writer json_builder;
char telemetry_payload[200];
AZ_RETURN_IF_FAILED(az_json_writer_init(&json_builder, AZ_SPAN_FROM_BUFFER(telemetry_payload), NULL));
AZ_RETURN_IF_FAILED(az_json_writer_append_begin_object(&json_builder));
AZ_RETURN_IF_FAILED(az_json_writer_append_property_name(&json_builder, AZ_SPAN_LITERAL_FROM_STR("co2")));
AZ_RETURN_IF_FAILED(az_json_writer_append_int32(&json_builder, co2));
AZ_RETURN_IF_FAILED(az_json_writer_append_property_name(&json_builder, AZ_SPAN_LITERAL_FROM_STR("humi")));
AZ_RETURN_IF_FAILED(az_json_writer_append_int32(&json_builder, humi));
AZ_RETURN_IF_FAILED(az_json_writer_append_property_name(&json_builder, AZ_SPAN_LITERAL_FROM_STR("temp")));
AZ_RETURN_IF_FAILED(az_json_writer_append_int32(&json_builder, temp));
AZ_RETURN_IF_FAILED(az_json_writer_append_property_name(&json_builder, AZ_SPAN_LITERAL_FROM_STR("light")));
AZ_RETURN_IF_FAILED(az_json_writer_append_int32(&json_builder, light));
AZ_RETURN_IF_FAILED(az_json_writer_append_end_object(&json_builder));
const az_span out_payload{ az_json_writer_get_bytes_used_in_destination(&json_builder) };
static int sendCount = 0;
if (!mqtt_client.publish(telemetry_topic, az_span_ptr(out_payload), az_span_size(out_payload), false))
{
DisplayPrintf("ERROR: Send telemetry %d", sendCount);
}
else
{
++sendCount;
DisplayPrintf("Sent telemetry %d", sendCount);
}
return AZ_OK;
}
static az_result SendButtonTelemetry(ButtonId id)
{
char creationTime[20 + 1]; // yyyy-mm-ddThh:mm:ss.sssZ
{
const time_t t = ntp.epoch();
struct tm tm;
gmtime_r(&t, &tm);
const int len = snprintf(creationTime, sizeof(creationTime), "%d-%02d-%02dT%02d:%02d:%02dZ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
if (len != sizeof(creationTime) - 1)
{
Log("Failed snprintf" DLM);
return AZ_ERROR_NOT_SUPPORTED;
}
}
az_iot_message_properties props;
uint8_t propsBuffer[128];
if (az_result_failed(az_iot_message_properties_init(&props, az_span_create(propsBuffer, sizeof(propsBuffer)), 0)))
{
Log("Failed az_iot_message_properties_init" DLM);
return AZ_ERROR_NOT_SUPPORTED;
}
if (az_result_failed(az_iot_message_properties_append(&props, AZ_SPAN_FROM_STR("iothub-creation-time-utc"), az_span_create(reinterpret_cast<uint8_t*>(creationTime), strlen(creationTime)))))
{
Log("Failed az_iot_message_properties_append" DLM);
return AZ_ERROR_NOT_SUPPORTED;
}
char telemetry_topic[128];
if (az_result_failed(az_iot_hub_client_telemetry_get_publish_topic(&HubClient, &props, telemetry_topic, sizeof(telemetry_topic), NULL)))
{
Log("Failed az_iot_hub_client_telemetry_get_publish_topic" DLM);
return AZ_ERROR_NOT_SUPPORTED;
}
az_json_writer json_builder;
char telemetry_payload[200];
AZ_RETURN_IF_FAILED(az_json_writer_init(&json_builder, AZ_SPAN_FROM_BUFFER(telemetry_payload), NULL));
AZ_RETURN_IF_FAILED(az_json_writer_append_begin_object(&json_builder));
switch (id)
{
case ButtonId::RIGHT:
AZ_RETURN_IF_FAILED(az_json_writer_append_property_name(&json_builder, AZ_SPAN_LITERAL_FROM_STR(TELEMETRY_RIGHT_BUTTON)));
AZ_RETURN_IF_FAILED(az_json_writer_append_string(&json_builder, AZ_SPAN_LITERAL_FROM_STR("click")));
break;
case ButtonId::CENTER:
AZ_RETURN_IF_FAILED(az_json_writer_append_property_name(&json_builder, AZ_SPAN_LITERAL_FROM_STR(TELEMETRY_CENTER_BUTTON)));
AZ_RETURN_IF_FAILED(az_json_writer_append_string(&json_builder, AZ_SPAN_LITERAL_FROM_STR("click")));
break;
case ButtonId::LEFT:
AZ_RETURN_IF_FAILED(az_json_writer_append_property_name(&json_builder, AZ_SPAN_LITERAL_FROM_STR(TELEMETRY_LEFT_BUTTON)));
AZ_RETURN_IF_FAILED(az_json_writer_append_string(&json_builder, AZ_SPAN_LITERAL_FROM_STR("click")));
break;
default:
return AZ_ERROR_ARG;
}
AZ_RETURN_IF_FAILED(az_json_writer_append_end_object(&json_builder));
const az_span out_payload{ az_json_writer_get_bytes_used_in_destination(&json_builder) };
if (!mqtt_client.publish(telemetry_topic, az_span_ptr(out_payload), az_span_size(out_payload), false))
{
DisplayPrintf("ERROR: Send button telemetry");
}
else
{
DisplayPrintf("Sent button telemetry");
}
return AZ_OK;
}
static void HandleCommandMessage(az_span payload, az_iot_hub_client_method_request* command_request)
{
int command_res_code = 200;
az_result rc = AZ_OK;
if (az_span_is_content_equal(AZ_SPAN_LITERAL_FROM_STR(COMMAND_RING_BUZZER), command_request->name))
{
// Parse the command payload (it contains a 'duration' field)
Log("Processing command 'ringBuzzer'" DLM);
char buffer[32];
az_span_to_str(buffer, 32, payload);
Log("Raw command payload: %s" DLM, buffer);
az_json_reader json_reader;
uint32_t duration;
if (az_json_reader_init(&json_reader, payload, NULL) == AZ_OK)
{
if (az_json_reader_next_token(&json_reader) == AZ_OK)
{
if (az_result_failed(rc = az_json_token_get_uint32(&json_reader.token, &duration)))
{
Log("Couldn't parse JSON token res=%d" DLM, rc);
}
else
{
Log("Duration: %dms" DLM, duration);
}
}
// Invoke command
analogWrite(WIO_BUZZER, 128);
delay(duration);
analogWrite(WIO_BUZZER, 0);
int rc;
if (az_result_failed(rc = SendCommandResponse(command_request, command_res_code, AZ_SPAN_LITERAL_FROM_STR("{}"))))
{
Log("Unable to send %d response, status 0x%08x" DLM, command_res_code, rc);
}
}
}
else
{
// Unsupported command
Log("Unsupported command received: %.*s." DLM, az_span_size(command_request->name), az_span_ptr(command_request->name));
int rc;
if (az_result_failed(rc = SendCommandResponse(command_request, 404, AZ_SPAN_LITERAL_FROM_STR("{}"))))
{
printf("Unable to send %d response, status 0x%08x\n", 404, rc);
}
}
}
static int SendCommandResponse(az_iot_hub_client_method_request* request, uint16_t status, az_span response)
{
az_result rc = AZ_OK;
// Get the response topic to publish the command response
char commands_response_topic[128];
if (az_result_failed(rc = az_iot_hub_client_methods_response_get_publish_topic(&HubClient, request->request_id, status, commands_response_topic, sizeof(commands_response_topic), NULL)))
{
Log("Unable to get method response publish topic" DLM);
return rc;
}
Log("Status: %u\tPayload: '", status);
char* payload_char = (char*)az_span_ptr(response);
if (payload_char != NULL)
{
for (int32_t i = 0; i < az_span_size(response); i++)
{
Log("%c", *(payload_char + i));
}
}
Log("'" DLM);
// Send the commands response
if (mqtt_client.publish(commands_response_topic, az_span_ptr(response), az_span_size(response), false))
{
Log("Sent response" DLM);
}
return rc;
}
static void MqttSubscribeCallbackHub(char* topic, byte* payload, unsigned int length)
{
az_span topic_span = az_span_create((uint8_t *)topic, strlen(topic));
az_iot_hub_client_method_request command_request;
if (az_result_succeeded(az_iot_hub_client_methods_parse_received_topic(&HubClient, topic_span, &command_request)))
{
DisplayPrintf("Command arrived!");
// Determine if the command is supported and take appropriate actions
HandleCommandMessage(az_span_create(payload, length), &command_request);
}
Log(DLM);
}
////////////////////////////////////////////////////////////////////////////////
// setup and loop
void setup()
{
////////////////////
// Load storage
Storage::Load();
////////////////////
// Init I/O
Serial.begin(115200);
pinMode(WIO_BUZZER, OUTPUT);
// Enter configuration mode
pinMode(WIO_KEY_A, INPUT_PULLUP);
pinMode(WIO_KEY_B, INPUT_PULLUP);
pinMode(WIO_KEY_C, INPUT_PULLUP);
delay(100);
if (digitalRead(WIO_KEY_A) == LOW &&
digitalRead(WIO_KEY_B) == LOW &&
digitalRead(WIO_KEY_C) == LOW )
{
DisplayPrintf("In configuration mode");
CliMode();
}
////////////////////
// Init sensor
Wire.begin();
scd30.initialize();
ButtonInit();
////////////////////
// Connect Wi-Fi
DisplayPrintf("Connecting to SSID: %s", IOT_CONFIG_WIFI_SSID);
do
{
Log(".");
WiFi.begin(IOT_CONFIG_WIFI_SSID, IOT_CONFIG_WIFI_PASSWORD);
delay(500);
}
while (WiFi.status() != WL_CONNECTED);
DisplayPrintf("Connected");
////////////////////
// Sync time server
ntp.begin();
////////////////////
//LCD Setup
tft.begin(); //start TFT LCD
tft.setRotation(3); //set TFT LCD rotation
//Setting the title header
tft.fillScreen(TFT_WHITE); //Fill background with white color
tft.fillRect(0,0,320,50,TFT_DARKGREEN); //Rectangle fill with dark green
tft.setTextColor(TFT_WHITE); //Setting text color
tft.setTextSize(3); //Setting text size
tft.drawString("SCD30",50,15); //Drawing a text string
tft.drawFastVLine(150,50,190,TFT_DARKGREEN); //Drawing verticle line
tft.drawFastHLine(0,140,320,TFT_DARKGREEN); //Drawing horizontal line
//Setting temperature
tft.setTextColor(TFT_BLACK);
tft.setTextSize(2);
tft.drawString("Temperature",10,65);
tft.setTextSize(3);
tft.drawString("dC",100,95);
//Setting humidity
tft.setTextSize(2);
tft.drawString("Humidity",10,160);
tft.setTextSize(3);
tft.drawString("%RH",100,190);
//Setting CO2
tft.setTextSize(2);
tft.drawString("CO2",160,65);
tft.setTextSize(3);
tft.drawString("ppm",260,95);
//Setting light
tft.setTextSize(2);
tft.drawString("Light",160,160);
tft.setTextSize(3);
tft.drawString("%",260,190);
// Provisioning
#if defined(USE_CLI) || defined(USE_DPS)
if (RegisterDeviceToDPS(IOT_CONFIG_GLOBAL_DEVICE_ENDPOINT, IOT_CONFIG_ID_SCOPE, IOT_CONFIG_REGISTRATION_ID, IOT_CONFIG_SYMMETRIC_KEY, ntp.epoch() + TOKEN_LIFESPAN, &HubHost, &DeviceId) != 0)
{
Abort("RegisterDeviceToDPS()");
}
#else
HubHost = IOT_CONFIG_IOTHUB;
DeviceId = IOT_CONFIG_DEVICE_ID;
#endif // USE_CLI || USE_DPS
}
void loop()
{
float result[3] = {0};
if (scd30.isAvailable()) {
scd30.getCarbonDioxideConcentration(result);
}
temp = result[1];
humi = result[2];
co2 = result[0];
light = analogRead(WIO_LIGHT);
light = map(light,0,1023,0,100); //Map sensor values
if (scd30.isAvailable()) {
scd30.getCarbonDioxideConcentration(result);
Serial.print("Carbon Dioxide Concentration is: ");
Serial.print(result[0]);
Serial.println(" ppm");
Serial.println(" ");
Serial.print("Temperature = ");
Serial.print(result[1]);
Serial.println(" ℃");
Serial.println(" ");
Serial.print("Humidity = ");
Serial.print(result[2]);
Serial.println(" %");
Serial.println(" ");
Serial.println(" ");
Serial.println(" ");
}
//sprite buffer for temperature
spr.createSprite(60,25);
spr.fillSprite(TFT_WHITE);
spr.setTextColor(TFT_BLACK);
spr.setTextSize(3);
spr.drawNumber(temp,0,0);
spr.pushSprite(30,95);
spr.deleteSprite();
//sprite buffer for humidity
spr.createSprite(60,25);
spr.fillSprite(TFT_WHITE);
spr.setTextColor(TFT_BLACK);
spr.setTextSize(3);
spr.drawNumber(humi,0,0);
spr.pushSprite(30,190);
spr.deleteSprite();
//sprite buffer for light
spr.createSprite(60,25);
spr.fillSprite(TFT_WHITE);
spr.setTextColor(TFT_BLACK);
spr.setTextSize(3);
spr.drawNumber(light,0,0);
spr.pushSprite(180,190);
spr.deleteSprite();
//sprite buffer for co2
spr.createSprite(60,25);
spr.fillSprite(TFT_WHITE);
spr.setTextColor(TFT_BLACK);
spr.setTextSize(3);
spr.drawNumber(co2,0,0);
spr.pushSprite(180,95);
spr.deleteSprite();
ButtonDoWork();
static uint64_t reconnectTime;
if (!mqtt_client.connected())
{
DisplayPrintf("Connecting to Azure IoT Hub...");
const uint64_t now = ntp.epoch();
if (ConnectToHub(&HubClient, HubHost, DeviceId, IOT_CONFIG_SYMMETRIC_KEY, now + TOKEN_LIFESPAN) != 0)
{
DisplayPrintf("> ERROR.");
Log("> ERROR. Status code =%d. Try again in 5 seconds." DLM, mqtt_client.state());
delay(5000);
return;
}
DisplayPrintf("> SUCCESS.");
reconnectTime = now + TOKEN_LIFESPAN * 0.85;
}
else
{
if ((uint64_t)ntp.epoch() >= reconnectTime)
{
DisplayPrintf("Disconnect");
mqtt_client.disconnect();
return;
}
mqtt_client.loop();
static unsigned long nextTelemetrySendTime = 0;
if (millis() > nextTelemetrySendTime)
{
SendTelemetry();
nextTelemetrySendTime = millis() + TELEMETRY_FREQUENCY_MILLISECS;
}
for (int i = 0; i < ButtonNumber; ++i)
{
if (ButtonsClicked[i])
{
SendButtonTelemetry(static_cast<ButtonId>(i));
ButtonsClicked[i] = false;
}
}
}
delay(2000);
}