Hello,
I want to send data to azure Iot hub less frequent than every 30 min. But after the first 30 min, messages sent by the nrf9160 won't arrive at the server. A disconnection error does not occur during that time (see Log). Disconnection error occurs if device tries to send something after the first 30 min (at 11:30:37:254 it tries to send other ping but fails).
The Log with keepalive set to 1600 sec and event interval 1800 sec: see how the first messages gets achknowledged (at 10:33:57:439) but the last message not (at 11:03:56:962) and see the successful ping (at 11:00:37:353)
10:33:49:500 -> +CEREG: 2,"57F0","02CB7D05",7 10:33:49:679 -> +CSCON: 1 10:33:49:680 -> [00:00:00.481,842] <inf> board_control: eSIM is selected 10:33:49:718 -> [00:00:00.481,872] <inf> board_control: Charger is set to auto 10:33:49:718 -> *** Booting Zephyr OS build v3.2.99-ncs2 *** 10:33:49:718 -> [00:00:00.481,964] <inf> azure_iot_hub_sample: Azure IoT Hub sample started 10:33:49:718 -> [00:00:00.482,055] <inf> azure_iot_hub_sample: Connecting to LTE network 10:33:49:718 -> [00:00:01.317,871] <inf> azure_iot_hub_sample: LTE cell changed: Cell ID: 46890245, Tracking area: 22512 10:33:49:718 -> [00:00:01.461,059] <inf> azure_iot_hub_sample: RRC mode: Connected 10:33:51:846 -> +CEREG: 5,"57F0","02CB7D05",7,,,"00001010","01011111" 10:33:52:936 -> [00:00:03.666,412] <inf> azure_iot_hub_sample: Network registration status: Connected - roaming 10:33:52:936 -> [00:00:03.666,564] <inf> azure_iot_hub_sample: Connected to LTE network 10:33:52:936 -> [00:00:03.666,564] <inf> azure_iot_hub_sample: Azure IoT Hub library initialized 10:33:52:936 -> [00:00:03.666,687] <inf> azure_iot_hub_sample: AZURE_IOT_HUB_EVT_CONNECTING 10:33:52:936 -> [00:00:03.667,846] <inf> azure_iot_hub_sample: PSM parameter update: TAU: 1116000, Active time: 20 10:33:56:354 -> [00:00:07.085,357] <inf> azure_iot_hub_sample: Connection request sent to IoT Hub 10:33:56:354 -> [00:00:07.325,103] <inf> azure_iot_hub_sample: AZURE_IOT_HUB_EVT_CONNECTED 10:33:56:354 -> [00:00:07.556,427] <inf> azure_iot_hub_sample: AZURE_IOT_HUB_EVT_READY 10:33:56:354 -> [00:00:07.556,732] <inf> azure_iot_hub_sample: Sending event:{"temperature":25.6,"timestamp":7556} 10:33:56:354 -> [00:00:07.557,617] <inf> azure_iot_hub_sample: Event was successfully sent 10:33:56:354 -> [00:00:07.557,617] <inf> azure_iot_hub_sample: Next event will be sent in 20 seconds 10:33:56:354 -> [00:00:07.903,808] <inf> azure_iot_hub_sample: AZURE_IOT_HUB_EVT_TWIN_RECEIVED 10:33:56:354 -> [00:00:07.906,280] <inf> azure_iot_hub_sample: Sending event:{"temperature":25.6,"timestamp":7906} 10:33:56:354 -> [00:00:07.906,829] <inf> azure_iot_hub_sample: Event was successfully sent 10:33:56:354 -> [00:00:07.906,829] <inf> azure_iot_hub_sample: Next event will be sent in 1800 seconds 10:33:57:439 -> [00:00:08.235,382] <inf> azure_iot_hub_sample: AZURE_IOT_HUB_EVT_PUBACK 10:33:57:439 -> [00:00:08.482,391] <inf> azure_iot_hub_sample: AZURE_IOT_HUB_EVT_PUBACK 10:34:08:754 -> +CSCON: 0 10:34:09:771 -> [00:00:20.572,998] <inf> azure_iot_hub_sample: RRC mode: Idle 11:00:36:339 -> +CSCON: 1 11:00:37:327 -> [00:26:48.267,150] <inf> azure_iot_hub_sample: RRC mode: Connected 11:00:37:353 -> [00:26:48.573,455] <inf> azure_iot_hub_sample: AZURE_IOT_HUB_EVT_PINGRESP 11:00:37:353 -> 11:00:48:395 -> +CSCON: 0 11:00:49:408 -> [00:27:00.331,756] <inf> azure_iot_hub_sample: RRC mode: Idle 11:03:56:098 -> +CEREG: 5,"57F0","028AB802",7,,,"00001010","01011111" 11:03:56:305 -> +CSCON: 1 11:03:56:962 -> [00:30:07.907,043] <inf> azure_iot_hub_sample: Sending event:{"temperature":25.6,"timestamp":1807906} 11:03:57:015 -> [00:30:07.916,168] <inf> azure_iot_hub_sample: Event was successfully sent 11:03:57:015 -> [00:30:07.916,168] <inf> azure_iot_hub_sample: Next event will be sent in 1800 seconds 11:03:57:015 -> [00:30:08.037,384] <inf> azure_iot_hub_sample: LTE cell changed: Cell ID: 42645506, Tracking area: 22512 11:03:57:015 -> [00:30:08.241,973] <inf> azure_iot_hub_sample: RRC mode: Connected 11:04:08:329 -> +CSCON: 0 11:04:09:347 -> [00:30:20.276,489] <inf> azure_iot_hub_sample: RRC mode: Idle
To make a minimal example I took the azure_iot_hub example from NSC
- set the event_interval to 1800 sec in device twin.
- set the MQTT keepalive time to 1600 sec (instead of 1767 sec) just to be sure
- added the PINGRESP case to azure_event_handler() to see if pings come back
- Changed MQTT QoS to 1 "at least once" to see if message arrived at the server
case AZURE_IOT_HUB_EVT_PINGRESP: LOG_INF("AZURE_IOT_HUB_EVT_PINGRESP\n"); break;
struct azure_iot_hub_msg msg = { .topic.type = AZURE_IOT_HUB_TOPIC_EVENT, .payload.ptr = buf, .qos = MQTT_QOS_1_AT_LEAST_ONCE, .message_id = k_cycle_get_32() };
I played with the MQTT keepalive time:
- When set to 60 sec, it works flawless. The device sends a ping every minute and after 30min the message arrives at the server. Problem is that the modem wont go to sleep here -> high energy consumption
- When set to 240 sec, energy consumption is much better here, but the keepalive time is still to high to keep connection alive
Another thing I found: When event_interval is lower than 1767 sec messages arrive at the server, even if no ping was sent in the period before. That tells me, that the ping/keepalive signal has no effect on resetting timeouts in the whole connection chain. Can that be true?
Hardware: NRF9160 B2 on the actinius Icarus V2
Software: NCS 2.3.0
Thanks a lot for your help!
Best regards, Moritz43080.prj.conf
/* * Copyright (c) 2020 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ #include <zephyr/kernel.h> #include <stdio.h> #include <stdlib.h> #include <net/azure_iot_hub.h> #include <net/azure_iot_hub_dps.h> #include <dk_buttons_and_leds.h> #include <cJSON.h> #include <cJSON_os.h> #include <zephyr/logging/log.h> #if IS_ENABLED(CONFIG_LTE_LINK_CONTROL) #include <modem/lte_lc.h> #endif LOG_MODULE_REGISTER(azure_iot_hub_sample, CONFIG_AZURE_IOT_HUB_SAMPLE_LOG_LEVEL); /* Interval [s] between sending events to the IoT hub. The value can be changed * by setting a new desired value for property 'telemetryInterval' in the * device twin document. */ #define EVENT_INTERVAL 20 #define RECV_BUF_SIZE 1024 #define APP_WORK_Q_STACK_SIZE KB(2) struct method_data { struct k_work work; char request_id[8]; char name[32]; char payload[200]; } method_data; struct k_work twin_report_work; struct k_work_delayable send_event_work; static char recv_buf[RECV_BUF_SIZE]; static void direct_method_handler(struct k_work *work); static K_SEM_DEFINE(network_connected_sem, 0, 1); static K_SEM_DEFINE(recv_buf_sem, 1, 1); static atomic_t event_interval = EVENT_INTERVAL; #ifdef CONFIG_AZURE_IOT_HUB_DPS static bool dps_was_successful; #endif /* A work queue is created to execute potentially blocking calls from. * This is done to avoid blocking for example the system work queue for extended * periods of time. */ static K_THREAD_STACK_DEFINE(application_stack_area, APP_WORK_Q_STACK_SIZE); static struct k_work_q application_work_q; /* Returns a positive integer if the new interval can be parsed, otherwise -1 */ static int event_interval_get(char *buf) { struct cJSON *root_obj, *desired_obj, *event_interval_obj; int new_interval = -1; root_obj = cJSON_Parse(buf); if (root_obj == NULL) { LOG_ERR("Could not parse properties object"); return -1; } /* If the incoming buffer is a notification from the cloud about changes * made to the device twin's "desired" properties, the root object will * only contain the newly changed properties, and can be treated as if * it is the "desired" object. * If the incoming is the response to a request to receive the device * twin, it will contain a "desired" object and a "reported" object, * and we need to access that object instead of the root. */ desired_obj = cJSON_GetObjectItem(root_obj, "desired"); if (desired_obj == NULL) { desired_obj = root_obj; } /* Update only recognized properties. */ event_interval_obj = cJSON_GetObjectItem(desired_obj, "telemetryInterval"); if (event_interval_obj == NULL) { LOG_INF("No 'telemetryInterval' object in the device twin"); goto clean_exit; } if (cJSON_IsString(event_interval_obj)) { new_interval = atoi(event_interval_obj->valuestring); } else if (cJSON_IsNumber(event_interval_obj)) { new_interval = event_interval_obj->valueint; } else { LOG_WRN("Invalid telemetry interval format received"); goto clean_exit; } clean_exit: cJSON_Delete(root_obj); k_sem_give(&recv_buf_sem); return new_interval; } static void event_interval_apply(int interval) { if (interval <= 0) { return; } atomic_set(&event_interval, interval); k_work_reschedule_for_queue(&application_work_q, &send_event_work, K_NO_WAIT); } static void on_evt_twin_desired(char *buf, size_t len) { if (k_sem_take(&recv_buf_sem, K_NO_WAIT) == 0) { if (len > sizeof(recv_buf) - 1) { LOG_ERR("Incoming data too big for buffer"); return; } memcpy(recv_buf, buf, len); recv_buf[len] = '\0'; k_work_submit_to_queue(&application_work_q, &twin_report_work); } else { LOG_WRN("Recv buffer is busy, data was not copied"); } } static void on_evt_direct_method(struct azure_iot_hub_method *method) { size_t request_id_len = MIN(sizeof(method_data.request_id) - 1, method->request_id.size); size_t name_len = MIN(sizeof(method_data.name) - 1, method->name.size); LOG_INF("Method name: %.*s", method->name.size, method->name.ptr); LOG_INF("Payload: %.*s", method->payload.size, method->payload.ptr); memcpy(method_data.request_id, method->request_id.ptr, request_id_len); method_data.request_id[request_id_len] = '\0'; memcpy(method_data.name, method->name.ptr, name_len); method_data.name[name_len] = '\0'; snprintk(method_data.payload, sizeof(method_data.payload), "%.*s", method->payload.size, method->payload.ptr); k_work_submit_to_queue(&application_work_q, &method_data.work); } static void azure_event_handler(struct azure_iot_hub_evt *const evt) { switch (evt->type) { case AZURE_IOT_HUB_EVT_CONNECTING: LOG_INF("AZURE_IOT_HUB_EVT_CONNECTING"); break; case AZURE_IOT_HUB_EVT_CONNECTED: LOG_INF("AZURE_IOT_HUB_EVT_CONNECTED"); break; case AZURE_IOT_HUB_EVT_CONNECTION_FAILED: LOG_INF("AZURE_IOT_HUB_EVT_CONNECTION_FAILED"); LOG_INF("Error code received from IoT Hub: %d", evt->data.err); break; case AZURE_IOT_HUB_EVT_DISCONNECTED: LOG_INF("AZURE_IOT_HUB_EVT_DISCONNECTED"); break; case AZURE_IOT_HUB_EVT_READY: LOG_INF("AZURE_IOT_HUB_EVT_READY"); /* The AZURE_IOT_HUB_EVT_READY event indicates that the * IoT hub connection is established and interaction with the * cloud can begin. * * The below work submission will cause send_event() to be * call after 3 seconds. */ k_work_reschedule_for_queue(&application_work_q, &send_event_work, K_NO_WAIT); break; case AZURE_IOT_HUB_EVT_DATA_RECEIVED: LOG_INF("AZURE_IOT_HUB_EVT_DATA_RECEIVED"); LOG_INF("Received payload: %.*s", evt->data.msg.payload.size, evt->data.msg.payload.ptr); break; case AZURE_IOT_HUB_EVT_TWIN_RECEIVED: LOG_INF("AZURE_IOT_HUB_EVT_TWIN_RECEIVED"); event_interval_apply(event_interval_get(evt->data.msg.payload.ptr)); break; case AZURE_IOT_HUB_EVT_TWIN_DESIRED_RECEIVED: LOG_INF("AZURE_IOT_HUB_EVT_TWIN_DESIRED_RECEIVED"); on_evt_twin_desired(evt->data.msg.payload.ptr, evt->data.msg.payload.size); break; case AZURE_IOT_HUB_EVT_DIRECT_METHOD: LOG_INF("AZURE_IOT_HUB_EVT_DIRECT_METHOD"); on_evt_direct_method(&evt->data.method); break; case AZURE_IOT_HUB_EVT_TWIN_RESULT_SUCCESS: LOG_INF("AZURE_IOT_HUB_EVT_TWIN_RESULT_SUCCESS, ID: %.*s", evt->data.result.request_id.size, evt->data.result.request_id.ptr); break; case AZURE_IOT_HUB_EVT_TWIN_RESULT_FAIL: LOG_INF("AZURE_IOT_HUB_EVT_TWIN_RESULT_FAIL, ID %.*s, status %d", evt->data.result.request_id.size, evt->data.result.request_id.ptr, evt->data.result.status); break; case AZURE_IOT_HUB_EVT_PUBACK: LOG_INF("AZURE_IOT_HUB_EVT_PUBACK"); break; case AZURE_IOT_HUB_EVT_PINGRESP: LOG_INF("AZURE_IOT_HUB_EVT_PINGRESP\n"); break; case AZURE_IOT_HUB_EVT_ERROR: LOG_INF("AZURE_IOT_HUB_EVT_ERROR"); break; default: LOG_ERR("Unknown Azure IoT Hub event type: %d", evt->type); break; } } static void send_event(struct k_work *work) { int err; static char buf[60]; ssize_t len; struct azure_iot_hub_msg msg = { .topic.type = AZURE_IOT_HUB_TOPIC_EVENT, .payload.ptr = buf, .qos = MQTT_QOS_1_AT_LEAST_ONCE, .message_id = k_cycle_get_32() }; len = snprintk(buf, sizeof(buf), "{\"temperature\":%d.%d,\"timestamp\":%d}", 25, k_uptime_get_32() % 10, k_uptime_get_32()); if ((len < 0) || (len > sizeof(buf))) { LOG_ERR("Failed to populate event buffer"); goto exit; } msg.payload.size = len; LOG_INF("Sending event:%s", buf); err = azure_iot_hub_send(&msg); if (err) { LOG_ERR("Failed to send event"); goto exit; } LOG_INF("Event was successfully sent"); exit: if (atomic_get(&event_interval) <= 0) { LOG_ERR("The event reporting stops, interval is set to %ld", atomic_get(&event_interval)); return; } LOG_INF("Next event will be sent in %ld seconds", event_interval); k_work_reschedule_for_queue(&application_work_q, &send_event_work, K_SECONDS(event_interval)); } static void direct_method_handler(struct k_work *work) { int err; static char *response = "{\"it\":\"worked\"}"; /* Status code 200 indicates successful execution of direct method. */ struct azure_iot_hub_result result = { .request_id = { .ptr = method_data.request_id, .size = strlen(method_data.request_id), }, .status = 200, .payload.ptr = response, .payload.size = sizeof(response) - 1, }; bool led_state = strncmp(method_data.payload, "0", 1) ? 1 : 0; if (strcmp(method_data.name, "led") != 0) { LOG_INF("Unknown direct method"); return; } #if defined(CONFIG_DK_LIBRARY) err = dk_set_led(DK_LED1, led_state); if (err) { LOG_ERR("Failed to set LED, error: %d", err); return; } #else LOG_INF("No hardware interface to set LED state to %d", led_state); #endif err = azure_iot_hub_method_respond(&result); if (err) { LOG_ERR("Failed to send direct method response"); } } static void twin_report_work_fn(struct k_work *work) { int err; char buf[100]; ssize_t len; struct azure_iot_hub_msg data = { .topic.type = AZURE_IOT_HUB_TOPIC_TWIN_REPORTED, .payload.ptr = buf, .qos = MQTT_QOS_0_AT_MOST_ONCE, }; int new_interval; new_interval = event_interval_get(recv_buf); if (new_interval < 0) { return; } len = snprintk(buf, sizeof(buf), "{\"telemetryInterval\":%d}", new_interval); if (len <= 0) { LOG_ERR("Failed to create twin report"); return; } data.payload.size = len; err = azure_iot_hub_send(&data); if (err) { LOG_ERR("Failed to send twin report"); return; } /* Note that the new interval value is first applied here, because that * will make the "reported" value in the device twin be in sync with * the reality on the device. Other applications may decide * to apply the desired properties regardless of whether the value is * successfully reported or not. */ event_interval_apply(new_interval); LOG_INF("New telemetry interval has been applied: %d", new_interval); } #if IS_ENABLED(CONFIG_LTE_LINK_CONTROL) static void lte_handler(const struct lte_lc_evt *const evt) { static bool connected; switch (evt->type) { case LTE_LC_EVT_NW_REG_STATUS: if ((evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_HOME) && (evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_ROAMING)) { if (!connected) { break; } connected = false; LOG_INF("LTE network is disconnected."); LOG_INF("Subsequent sending of data may block or fail."); break; } LOG_INF("Network registration status: %s", evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ? "Connected - home network" : "Connected - roaming"); k_sem_give(&network_connected_sem); connected = true; break; case LTE_LC_EVT_PSM_UPDATE: LOG_INF("PSM parameter update: TAU: %d, Active time: %d", evt->psm_cfg.tau, evt->psm_cfg.active_time); break; case LTE_LC_EVT_EDRX_UPDATE: { char log_buf[60]; ssize_t len; len = snprintk(log_buf, sizeof(log_buf), "eDRX parameter update: eDRX: %f, PTW: %f", evt->edrx_cfg.edrx, evt->edrx_cfg.ptw); if (len > 0) { LOG_INF("%s", log_buf); } break; } case LTE_LC_EVT_RRC_UPDATE: LOG_INF("RRC mode: %s", evt->rrc_mode == LTE_LC_RRC_MODE_CONNECTED ? "Connected" : "Idle"); break; case LTE_LC_EVT_CELL_UPDATE: LOG_INF("LTE cell changed: Cell ID: %d, Tracking area: %d", evt->cell.id, evt->cell.tac); break; default: break; } } static void modem_configure(void) { if (IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT)) { /* Do nothing, modem is already configured and LTE connected. */ } else { int err = lte_lc_init_and_connect_async(lte_handler); if (err) { LOG_INF("Modem could not be configured, error: %d", err); return; } } } #endif static void work_init(void) { k_work_init(&method_data.work, direct_method_handler); k_work_init(&twin_report_work, twin_report_work_fn); k_work_init_delayable(&send_event_work, send_event); k_work_queue_start(&application_work_q, application_stack_area, K_THREAD_STACK_SIZEOF(application_stack_area), K_HIGHEST_APPLICATION_THREAD_PRIO, NULL); } #if IS_ENABLED(CONFIG_AZURE_IOT_HUB_DPS) static K_SEM_DEFINE(dps_done_sem, 0, 1); static void dps_handler(enum azure_iot_hub_dps_reg_status status) { switch (status) { case AZURE_IOT_HUB_DPS_REG_STATUS_NOT_STARTED: LOG_INF("DPS registration status: AZURE_IOT_HUB_DPS_REG_STATUS_NOT_STARTED"); break; case AZURE_IOT_HUB_DPS_REG_STATUS_ASSIGNING: LOG_INF("DPS registration status: AZURE_IOT_HUB_DPS_REG_STATUS_ASSIGNING"); break; case AZURE_IOT_HUB_DPS_REG_STATUS_ASSIGNED: LOG_INF("DPS registration status: AZURE_IOT_HUB_DPS_REG_STATUS_ASSIGNED"); dps_was_successful = true; k_sem_give(&dps_done_sem); break; case AZURE_IOT_HUB_DPS_REG_STATUS_FAILED: LOG_INF("DPS registration status: AZURE_IOT_HUB_DPS_REG_STATUS_FAILED"); dps_was_successful = false; k_sem_give(&dps_done_sem); break; default: LOG_WRN("Unhandled DPS status: %d", status); break; } } /* Run DPS using the provided device_id as registration ID. Upon success, the function will return * 0 and populate the hostname and device_id buffers with the assigned values. * The return value will be a negative integer in case of failure. */ static int dps_run(struct azure_iot_hub_buf *hostname, struct azure_iot_hub_buf *device_id) { int err; struct azure_iot_hub_dps_config dps_cfg = { .handler = dps_handler, .reg_id = { .ptr = device_id->ptr, .size = strlen(device_id->ptr), }, /* Default ID scope provided by Kconfig is used, so we will not change that. */ }; LOG_INF("Starting DPS"); err = azure_iot_hub_dps_init(&dps_cfg); if (err) { LOG_ERR("azure_iot_hub_dps_init failed, error: %d", err); return err; } err = azure_iot_hub_dps_start(); if (err == 0) { LOG_INF("The DPS process has started, timeout is set to %d seconds", CONFIG_AZURE_IOT_HUB_DPS_TIMEOUT_SEC); /* If DPS was started successfully, we wait for the semaphore that is given when the * provisioning completes. */ (void)k_sem_take(&dps_done_sem, K_FOREVER); if (!dps_was_successful) { return -EFAULT; } /* The device was assigned, continue to retrieve the hostname. */ } else if (err == -EALREADY) { LOG_INF("Already assigned to an IoT hub, skipping DPS"); } else { LOG_ERR("DPS failed to start, error: %d", err); return err; } err = azure_iot_hub_dps_hostname_get(hostname); if (err) { LOG_ERR("Failed to get hostname, error: %d", err); return err; } err = azure_iot_hub_dps_device_id_get(device_id); if (err) { LOG_ERR("Failed to get device ID, error: %d", err); return err; } LOG_INF("Device ID \"%.*s\" assigned to IoT hub with hostname \"%.*s\"", device_id->size, device_id->ptr, hostname->size, hostname->ptr); return 0; } #endif /* CONFIG_AZURE_IOT_HUB_DPS && !CONFIG_AZURE_IOT_HUB_DPS_AUTO */ void main(void) { int err; char device_id[128] = CONFIG_AZURE_IOT_HUB_DEVICE_ID; char hostname[128] = CONFIG_AZURE_IOT_HUB_HOSTNAME; struct azure_iot_hub_config cfg = { .device_id = { .ptr = device_id, .size = sizeof(CONFIG_AZURE_IOT_HUB_DEVICE_ID) - 1, }, .hostname = { .ptr = hostname, .size = sizeof(CONFIG_AZURE_IOT_HUB_HOSTNAME) - 1, }, .use_dps = true, }; LOG_INF("Azure IoT Hub sample started"); #if IS_ENABLED(CONFIG_DK_LIBRARY) dk_leds_init(); #endif work_init(); cJSON_Init(); #if IS_ENABLED(CONFIG_LTE_LINK_CONTROL) LOG_INF("Connecting to LTE network"); modem_configure(); k_sem_take(&network_connected_sem, K_FOREVER); LOG_INF("Connected to LTE network"); #endif #if IS_ENABLED(CONFIG_AZURE_IOT_HUB_DPS) /* Using the device ID as DPS registration ID. */ err = dps_run(&cfg.hostname, &cfg.device_id); if (err) { LOG_ERR("Failed to run DPS, error: %d, terminating connection attempt", err); return; } #endif err = azure_iot_hub_init(azure_event_handler); if (err) { LOG_ERR("Azure IoT Hub could not be initialized, error: %d", err); return; } LOG_INF("Azure IoT Hub library initialized"); err = azure_iot_hub_connect(&cfg); if (err < 0) { LOG_ERR("azure_iot_hub_connect failed: %d", err); return; } LOG_INF("Connection request sent to IoT Hub"); /* After the connection to the IoT hub has been established, the * Azure IoT Hub library will generate events when data is received. * See azure_event_handler() for which actions will be taken on the * various events. */ }