Skip to content

Latest commit

 

History

History
121 lines (110 loc) · 6.7 KB

02-c2d.md

File metadata and controls

121 lines (110 loc) · 6.7 KB

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.