IndustrialDeviceController/Software/HighLevelApp/iot/azure_iot_utilities.c (293 lines of code) (raw):
/* Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. */
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <applibs/log.h>
#include <applibs/storage.h>
#include <azureiot/azure_sphere_provisioning.h>
#include <azureiot/iothub.h>
#include <azureiot/iothub_client_core_common.h>
#include <azureiot/iothub_client_options.h>
#include <azureiot/iothub_device_client_ll.h>
#include <azureiot/iothubtransportmqtt.h>
#include <init/globals.h>
#include <utils/llog.h>
#include <utils/utils.h>
#include <safeclib/safe_lib.h>
#include <iot/diag.h>
#include <iot/iot.h>
#include <iot/azure_iot_utilities.h>
// Refer to https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-device-sdk-c-intro for more
// information on Azure IoT SDK for C
typedef struct d2c_context_t d2c_context_t;
struct d2c_context_t {
size_t payload_size;
message_delivery_confirmation_func_t delivery_callback;
void *context;
};
typedef struct twin_report_context_t twin_report_context_t;
struct twin_report_context_t {
size_t payload_size;
device_twin_delivery_confirmation_func_t delivery_callback;
void *context;
};
// String containing the scope id of the Device Provisioning Service
// used to provision the app with the IoT hub hostname and the device id.
static const char scopeId[] = "xxxxxxxxxxx";
static device_twin_update_func_t device_twin_update_cb = 0;
static connection_status_func_t connection_status_cb = 0;
static message_received_func_t message_received_cb = 0;
static IOTHUB_DEVICE_CLIENT_LL_HANDLE iothub_client_handle = NULL;
static bool iothub_authenticated = false;
static int keepalive_period_seconds = 240;
static uint32_t inflight_message_quota = 0;
static uint32_t inflight_message_size = 0;
static const char *get_reason_string(IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason)
{
static char *reason_string = "unknown reason";
switch (reason) {
case IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN:
reason_string = "IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN";
break;
case IOTHUB_CLIENT_CONNECTION_DEVICE_DISABLED:
reason_string = "IOTHUB_CLIENT_CONNECTION_DEVICE_DISABLED";
break;
case IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL:
reason_string = "IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL";
break;
case IOTHUB_CLIENT_CONNECTION_RETRY_EXPIRED:
reason_string = "IOTHUB_CLIENT_CONNECTION_RETRY_EXPIRED";
break;
case IOTHUB_CLIENT_CONNECTION_NO_NETWORK:
reason_string = "IOTHUB_CLIENT_CONNECTION_NO_NETWORK";
break;
case IOTHUB_CLIENT_CONNECTION_COMMUNICATION_ERROR:
reason_string = "IOTHUB_CLIENT_CONNECTION_COMMUNICATION_ERROR";
break;
case IOTHUB_CLIENT_CONNECTION_OK:
reason_string = "IOTHUB_CLIENT_CONNECTION_OK";
break;
case IOTHUB_CLIENT_CONNECTION_NO_PING_RESPONSE:
reason_string = "IOTHUB_CLIENT_CONNECTION_NO_PING_RESPONSE";
break;
}
return reason_string;
}
static char *get_azure_sphere_provisioning_result_string(AZURE_SPHERE_PROV_RETURN_VALUE provisioning_result)
{
switch (provisioning_result.result) {
case AZURE_SPHERE_PROV_RESULT_OK:
return "AZURE_SPHERE_PROV_RESULT_OK";
case AZURE_SPHERE_PROV_RESULT_INVALID_PARAM:
return "AZURE_SPHERE_PROV_RESULT_INVALID_PARAM";
case AZURE_SPHERE_PROV_RESULT_NETWORK_NOT_READY:
return "AZURE_SPHERE_PROV_RESULT_NETWORK_NOT_READY";
case AZURE_SPHERE_PROV_RESULT_DEVICEAUTH_NOT_READY:
return "AZURE_SPHERE_PROV_RESULT_DEVICEAUTH_NOT_READY";
case AZURE_SPHERE_PROV_RESULT_PROV_DEVICE_ERROR:
return "AZURE_SPHERE_PROV_RESULT_PROV_DEVICE_ERROR";
case AZURE_SPHERE_PROV_RESULT_GENERIC_ERROR:
return "AZURE_SPHERE_PROV_RESULT_GENERIC_ERROR";
default:
return "UNKNOWN_RETURN_VALUE";
}
}
static void send_reported_state_callback(int result, void *context)
{
twin_report_context_t *ctx = (twin_report_context_t*)context;
inflight_message_size -= ctx->payload_size;
if (inflight_message_size < 0) {
inflight_message_size = 0;
}
if (ctx->delivery_callback) {
ctx->delivery_callback(result == 204, ctx->context);
}
FREE(ctx);
}
static void send_message_callback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void *context)
{
d2c_context_t *ctx = (d2c_context_t *)context;
inflight_message_size -= ctx->payload_size;
if (inflight_message_size < 0) {
inflight_message_size = 0;
}
if (ctx->delivery_callback) {
ctx->delivery_callback(result == IOTHUB_CLIENT_CONFIRMATION_OK, ctx->context);
}
FREE(ctx);
}
static IOTHUBMESSAGE_DISPOSITION_RESULT message_received_callback(IOTHUB_MESSAGE_HANDLE message, void *context)
{
const unsigned char *buffer = NULL;
size_t size = 0;
if (IoTHubMessage_GetByteArray(message, &buffer, &size) != IOTHUB_MESSAGE_OK) {
LOGW("failure performing IoTHubMessage_GetByteArray");
return IOTHUBMESSAGE_REJECTED;
}
const char *message_type = IoTHubMessage_GetProperty(message, "message_type");
if (message_received_cb) {
message_received_cb(buffer, size, message_type, context);
}
return IOTHUBMESSAGE_ACCEPTED;
}
static void device_twin_update_callback(DEVICE_TWIN_UPDATE_STATE update_state,
const unsigned char *properties, size_t properties_len, void *context)
{
if (device_twin_update_cb) {
device_twin_update_cb(properties, properties_len, context);
}
}
static void connection_status_callback(IOTHUB_CLIENT_CONNECTION_STATUS result,
IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void *context)
{
iothub_authenticated = (result == IOTHUB_CLIENT_CONNECTION_AUTHENTICATED);
if (connection_status_cb) {
connection_status_cb(iothub_authenticated, reason, context);
}
const char *reason_string = get_reason_string(reason);
if (!iothub_authenticated) {
LOGI("IoT Hub connection is down (%s), retrying connection...", reason_string);
} else {
LOGI("connection to the IoT Hub has been established (%s).", reason_string);
}
}
// ---------------------------- public interface ------------------------------
bool azure_iot_initialize(void)
{
if (IoTHub_Init() != 0) {
LOGE("failed initializing platform.");
return false;
}
return true;
}
void azure_iot_deinitialize(void)
{
IoTHub_Deinit();
}
void azure_iot_destroy_client(void)
{
if (iothub_client_handle != NULL) {
IoTHubDeviceClient_LL_Destroy(iothub_client_handle);
iothub_client_handle = NULL;
iothub_authenticated = false;
}
}
AZURE_SPHERE_PROV_RESULT azure_iot_setup_client(size_t quota, void *context)
{
LOGI("Connecting to IoTHub...");
azure_iot_destroy_client();
AZURE_SPHERE_PROV_RETURN_VALUE prov_result =
IoTHubDeviceClient_LL_CreateWithAzureSphereDeviceAuthProvisioning(scopeId, 30000, &iothub_client_handle);
LOGI("IoTHubDeviceClient_CreateWithAzureSphereDeviceAuthProvisioning returned '%s'.",
get_azure_sphere_provisioning_result_string(prov_result));
if (prov_result.result != AZURE_SPHERE_PROV_RESULT_OK) {
return prov_result.result;
}
if (iothub_client_handle == NULL) {
LOGE("failed to create handler");
return AZURE_SPHERE_PROV_RESULT_IOTHUB_CLIENT_ERROR;
}
if (IoTHubDeviceClient_LL_SetRetryPolicy(iothub_client_handle, IOTHUB_CLIENT_RETRY_NONE, 0) != IOTHUB_CLIENT_OK) {
LOGE("failed to set retry policy");
azure_iot_destroy_client();
return AZURE_SPHERE_PROV_RESULT_IOTHUB_CLIENT_ERROR;
}
if (IoTHubDeviceClient_LL_SetOption(iothub_client_handle, OPTION_KEEP_ALIVE, &keepalive_period_seconds) !=
IOTHUB_CLIENT_OK) {
LOGE("failed to set keep alive \"%s\"", OPTION_KEEP_ALIVE);
azure_iot_destroy_client();
return AZURE_SPHERE_PROV_RESULT_IOTHUB_CLIENT_ERROR;
}
if (IoTHubDeviceClient_LL_SetMessageCallback(iothub_client_handle, message_received_callback, context) !=
IOTHUB_CLIENT_OK) {
LOGE("failed to set receive message callback");
azure_iot_destroy_client();
return AZURE_SPHERE_PROV_RESULT_IOTHUB_CLIENT_ERROR;
}
if (IoTHubDeviceClient_LL_SetDeviceTwinCallback(iothub_client_handle, device_twin_update_callback, context) !=
IOTHUB_CLIENT_OK) {
LOGE("failed to set device twin callback");
azure_iot_destroy_client();
return AZURE_SPHERE_PROV_RESULT_IOTHUB_CLIENT_ERROR;
}
if (IoTHubDeviceClient_LL_SetConnectionStatusCallback(iothub_client_handle, connection_status_callback, context) !=
IOTHUB_CLIENT_OK) {
LOGE("failed to set connection status callback");
azure_iot_destroy_client();
return AZURE_SPHERE_PROV_RESULT_IOTHUB_CLIENT_ERROR;
}
inflight_message_size = 0;
inflight_message_quota = quota;
return AZURE_SPHERE_PROV_RESULT_OK;
}
void azure_iot_do_periodic_tasks(void)
{
if (iothub_client_handle) {
IoTHubDeviceClient_LL_DoWork(iothub_client_handle);
}
}
void azure_iot_set_message_received_callback(message_received_func_t callback)
{
message_received_cb = callback;
}
void azure_iot_set_device_twin_update_callback(device_twin_update_func_t callback)
{
device_twin_update_cb = callback;
}
void azure_iot_set_connection_status_callback(connection_status_func_t callback)
{
connection_status_cb = callback;
}
int azure_iot_send_message_async(const char *message, const char *message_type,
message_delivery_confirmation_func_t callback, void *context)
{
size_t message_len = strlen(message);
if (inflight_message_quota && (inflight_message_size + message_len > inflight_message_quota)) {
LOGE("Exceed inflight message quota");
return -1;
}
IOTHUB_MESSAGE_HANDLE message_handle = IoTHubMessage_CreateFromString(message);
if (message_handle == 0) {
LOGE("unable to create a new IoTHubMessage");
return -1;
}
// Set the system property of the message
IoTHubMessage_SetContentTypeSystemProperty(message_handle, IOT_MESSAGE_CONTENT_TYPE);
IoTHubMessage_SetContentEncodingSystemProperty(message_handle, IOT_MESSAGE_CONTENT_ENCODING);
// Set the application property of the message
IoTHubMessage_SetProperty(message_handle, "message_type", message_type);
d2c_context_t *ctx = CALLOC(1, sizeof(d2c_context_t));
ctx->payload_size = message_len;
ctx->delivery_callback = callback;
ctx->context = context;
if (IoTHubDeviceClient_LL_SendEventAsync(iothub_client_handle, message_handle, send_message_callback,
(void *)ctx) != IOTHUB_CLIENT_OK) {
LOGE("failed to hand over the message to IoTHubClient");
FREE(ctx);
IoTHubMessage_Destroy(message_handle);
return -1;
}
inflight_message_size += message_len;
IoTHubMessage_Destroy(message_handle);
return 0;
}
int azure_iot_twin_report_async(const char *properties, device_twin_delivery_confirmation_func_t callback, void *context)
{
size_t properties_len = strlen(properties);
if (inflight_message_quota && (inflight_message_size + properties_len > inflight_message_quota)) {
LOGE("Exceed inflight message quota");
return -1;
}
twin_report_context_t *ctx = CALLOC(1, sizeof(twin_report_context_t));
ctx->payload_size = properties_len;
ctx->delivery_callback = callback;
ctx->context = context;
if (IoTHubDeviceClient_LL_SendReportedState(iothub_client_handle, (unsigned char *)properties, properties_len,
send_reported_state_callback, ctx) != IOTHUB_CLIENT_OK) {
LOGE("failed to set reported property: %s", properties);
FREE(ctx);
return -1;
}
inflight_message_size += properties_len;
return 0;
}
bool azure_iot_is_connected(void)
{
return iothub_client_handle && iothub_authenticated;
}