Configure the Event module, process raw mqtt json strings and receive commands and OTA download URLs in your configured callback functions. We are showcasing a share AWS instance configuration in this example with iotcl_c2d_example_entry() being the example's starting call.
In this example shows common basic steps that are required to use the library with C2D messages:
- Configure the library module once. Use my_transport_send() as mqtt_send_cb. Provide on_cmd and on_ota and OTA implementations as library's event callbacks.
- Connect your MQTT client (in the example: mqtt_client_* functions), subscribe and route received messages to the library via
- Extract values from Event Data and process them accordingly in your callbacks.
- Provide acknowledgements upon successful or failed processing.
static void on_cmd(IotclC2dEventData data) {
// to get the ACK ID and be able to send acknowledgements, ensure that you configure your command
// in the template in the IoTConnect Web UI with the "Require Acknowledgement" option.
const char *ack_id = iotcl_c2d_get_ack_id(data);
const char *command = iotcl_c2d_get_command(data);
if (!ack_id || !command) {
// by default, the library will print the error in the log if there was an OOM or a parsing error.
// so we can simply return without loggin any messages internally
return;
}
if (0 == (strcmp(command, "motor on"))) {
int status = motor_enable();
if (MOTOR_OK != status) {
iotcl_mqtt_send_cmd_ack(ack_id, IOTCL_C2D_EVT_CMD_SUCCESS_WITH_ACK, NULL);
} else {
printf("Motor failed with status %d\n", status);
// We will be able to see this status in the Web UI along with the internal motor error.
iotcl_mqtt_send_cmd_ack(ack_id, IOTCL_C2D_EVT_CMD_FAILED, "Motor failed with status %d", status);
}
} else if (strcmp(command, "alert"))) {
buzzer_enable();
iotcl_mqtt_send_cmd_ack(ack_id, IOTCL_C2D_EVT_CMD_SUCCESS_WITH_ACK, "Alert is on"); // unconditional
}
// NOTE: Any values obtained with iotcl_c2d_get_* function will become invalid upon
// returning from this callback so make copies of values, if necessary.
}
static void on_ota(IotclC2dEventData data) {
const char *ack_id = iotcl_c2d_get_ack_id(data);
const char *hostname = iotcl_c2d_get_ota_url_hostname(data, 0); // URL at index zero should always exist.
const char *resource = iotcl_c2d_get_ota_url_resource(data, 0);
const char *filename = iotcl_c2d_get_ota_original_filename(data, 0);
const char *sw_ver = iotcl_c2d_get_ota_sw_version(data);
if (!ack_id || !hostname || !resource || !filename || !sw_ver) {
return; // by default, the library will print the error in the log if there was an OOM or a any kind of error
}
if (0 == strcmp(sw_ver, MY_APP_VERSION)) {
iotcl_mqtt_send_ota_ack(ack_id, IOTCL_C2D_EVT_OTA_SUCCESS, "Already running version %s", MY_APP_VERSION);
return;
}
printf("Downloading %d ...\n", filename);
int status = my_http_client_download(hostname, resource);
if(status != HTTP_DOWNLOAD_OK) {
// try generating a failure
iotcl_mqtt_send_ota_ack(ack_id, IOTCL_C2D_EVT_OTA_DOWNLOAD_FAILED, "Failed to download with status %s", MY_APP_VERSION);
return;
}
iotcl_mqtt_send_ota_ack(ack_id, IOTCL_C2D_EVT_OTA_SUCCESS, NULL);
sleep(5000); // allow the message to go out
my_device_apply_firmware_and_reset();
// NOTE: Any values obtained with iotcl_c2d_get_* function will become invalid upon
// returning from this callback so make copies of values, if necessary.
}
static void my_transport_send(const char *topic, size_t topic_len, const char *json_str) {
// possible mqtt client send function:
mqtt_client_send(topic, topic_len, json_str, strlen(json_str));
}
// possible MQTT client callback setup where we know that we got data on the c2d topic
// and receiving data buffer with data length.
static void on_mqtt_client_c2d_topic_data(const uint8 *data, size_t data_len) {
iotcl_mqtt_receive_c2d_with_length(data, data_len);
}
void iotcl_c2d_example_entry(void) {
IotclClientConfig config;
iotcl_init_client_config(&config);
config.device.instance_type = IOTCL_DCT_AWS_DEDICATED;
config.device.duid = "mydevice";
config.mqtt_send_cb = my_transport_send;
iotcl_init(&config);
mqtt_client_connect(...); // connect using your client
mqtt_client_subscribe(iotcl_get_mqtt_config()->sub_c2d, on_mqtt_client_c2d_topic_data);
while (mqtt_client_is_connected()) {
mqtt_client_loop();
sleep(1000);
}
}
Depending on your scenario, consider the following variations/extensions to this example that will better fit your needs:
- Setup the library's device configuration per your own instance setup, rather than IOTCL_DCT_AWS_DEDICATED. or use the discovery module to set up your MQTT config.
- The library provides default error handling (printing to logs and optional error hooks), so check return values from iotcl_mqtt* and library init calls if you wish to add additional error handling.
- If your mqtt client provides a single entry point with topic name as an argument, instead of calling iotcl_mqtt_receive_c2d_with_length, call call iotcl_mqtt_receive* (without c2d) functions to ensure that the messages are properly routed.
- If your HTTP client uses a full URL instead of host and resource path, obtain the full URL with iotcl_c2d_get_ota_url(data, 0) instead of using the host and port breakdown functions.
- If you need to modify the requests made to the OTA URl(s), consider using the DRA URL from device-rest-api module.
- If you need to free up dynamic memory taken up by the library's message processing, consider calling iotcl_c2d_destroy_event() to destroy it early during the callback. Be mindful of the fact that any references to obtained values with iotcl_c2d_get_* functions will become invalid.
- If you have the ability to store the ACK ID into persistent storage and self-test the newly downloaded firmware with it comes up, consider a more in-depth OTA reporting methods described in the iotcl_c2d.h header comments section OTA USE CASES WITH C2D PROCESSING
- If you are sending more than one file with a single OTA or have different filenames intended for different purposes:
- You can get the number of OTA files with iotcl_c2d_get_ota_url_count()
- You can access different download URLs by providing a different index to the iotcl_c2d_get_ota* functions.
- You can examine the file extensions or patterns (for example, *.bin are firmware files and *.xml are config files) and handle them differently.