Weather Application with Cloud Connector Example - Full Source Code

The source code below is the completed main.c from the tutorial Native C Application Development Tutorial with the added cloud connector, as discussed in the tutorial Create a Could Connector. To compile the source code successfully, several steps in the tutorial must be completed.

/*
 * EVALUATION AND USE OF THIS SOFTWARE IS SUBJECT TO THE TERMS AND
 * CONDITIONS OF THE CONTROLLING LICENSE AGREEMENT FOUND AT LICENSE.md
 * IN THIS SDK. IF YOU DO NOT AGREE TO THE LICENSE TERMS AND CONDITIONS,
 * PLEASE RETURN ALL SOURCE FILES TO SILICON LABORATORIES.
 * (c) Copyright 2018, Silicon Laboratories Inc.  All rights reserved.
 */

#include "gos.h"
#include "si7013.h"

#define WEATHER_REPORT_PERIOD_MS         10000
#define SI7021_MEASUREMENT_TIME_MS       50  // the si7021 takes max 50ms to perform measurement

static gos_i2c_device_t si7021_device =
{
    .port    = PLATFORM_STD_I2C,
    .address = SI7021_ADDR >> 1,
    .speed   = GOS_I2C_CLOCK_HIGH_SPEED,
    .read_timeout = 50,     //si7021 can take up to 25ms to finish a conversion
    .retries = 0,
    .flags   = 0
};

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void gos_app_init(void)
{   uint8_t part_id = 0;
    uint8_t part_rev = 0;

    // Print to the serial terminal
    GOS_LOG("Weather Demo");

    gos_load_app_settings_once("settings.ini", 1);

    gos_network_register_event_handler(GOS_INTERFACE_WLAN, wlan_network_event_handler);
    gos_setup_register_finished_event_handler(setup_finished_handler);

    start_network_interface();

    // Detect the Si7021 and read the firmware version
    Si7013_Detect(&si7021_device, 0, &part_id);
    Si7013_GetFirmwareRevision(&si7021_device, 0, &part_rev);
    if(part_id == SI7021_DEVICE_ID)
      GOS_LOG("Detected Si7021 Version %d.%d", ((part_rev >> 4) & 0xF), (part_rev & 0xF));
    else
      GOS_LOG("Si7021 Not Detected");

    gos_event_register_periodic(start_measurement_event_handler, 0, WEATHER_REPORT_PERIOD_MS, 0);

}

// ------------------------------------------------------------------------------------------------
// Try to start WLAN interface
// If fail to start, then start web setup
// NOTE: Web setup will idle timeout after setup.web.idle_timeout seconds
//       Upon timeout, the system is rebooted.
// ------------------------------------------------------------------------------------------------
static void start_network_interface(void)
{
    gos_result_t result;

    GOS_LOG("\r\n\r\nChecking if device is able to connect to local network ...");

    if (GOS_FAILED(result, gos_network_up(GOS_INTERFACE_WLAN, true)))
    {
        GOS_LOG("Failed to join local network, starting Web Setup");

        if (GOS_FAILED(result, gos_setup_start()))
        {
            GOS_LOG("Failed to start Web Setup, rebooting");
            gos_system_reboot();
        }
        else
        {
            char buffer_url[128];
            char buffer_username[64];
            char buffer_passkey[64];
            uint32_t idle_timeout;
            gos_settings_get_uint32("setup.web.idle_timeout", &idle_timeout);
            get_setup_url("setup.web.url", buffer_url, sizeof(buffer_url));
            get_device_name("setup.web.ssid", buffer_username, sizeof(buffer_username));
            gos_settings_get_print_str("setup.web.passkey", buffer_passkey, sizeof(buffer_passkey));

            GOS_LOG("Device Web Setup started");
            GOS_LOG("1. Connect to Wi-Fi network: %s with passkey: %s", buffer_username, buffer_passkey);
            GOS_LOG("2. Open browser to http://%s", buffer_url);
            GOS_LOG("3. Setup the device\r\n");
            if(idle_timeout != 0)
              GOS_LOG("NOTE: Web setup will idle timeout after %d seconds\r\n", idle_timeout);
        }
    }
    else
    {
        GOS_LOG("Device WLAN started");
    }
}

// ------------------------------------------------------------------------------------------------
// Event handler for WLAN interface events
// ------------------------------------------------------------------------------------------------
static void wlan_network_event_handler(bool is_up)
{

    // If the WLAN interface has gone down AND softAP interface is NOT up try to restart the WLAN interface
    if ((is_up == false) && (gos_network_is_up(GOS_INTERFACE_SOFTAP) == false))
    {
        // If the network goes down try to restart it, else start web setup
        start_network_interface();
    }
}

// ------------------------------------------------------------------------------------------------
// Just reboot the system when web setup finishes
// ------------------------------------------------------------------------------------------------
static void setup_finished_handler(void *unsed)
{
    GOS_LOG("Web setup finished, rebooting system");
    gos_system_reboot();
}

// ------------------------------------------------------------------------------------------------
// Convert '#' to last three chars of device's MAC address
// ------------------------------------------------------------------------------------------------
static const char* get_device_name(const char *setting, char *buffer, size_t buffer_length)
{
    int setting_len;
    char *setting_ptr;

    gos_settings_get_print_str(setting, buffer, buffer_length);
    setting_len = strlen(buffer);
    setting_ptr = &buffer[setting_len-1];

    if (*setting_ptr == '#')
    {
        char mac_str[20];

        // Example output:
        // 4C:55:CC:10:78:9B
        gos_settings_get_print_str("softap.mac", mac_str, sizeof(mac_str));


        *setting_ptr++ = mac_str[13];
        *setting_ptr++ = mac_str[15];
        *setting_ptr++ = mac_str[16];
        *setting_ptr = 0;
    }

    return buffer;
}

// ------------------------------------------------------------------------------------------------
// When the first entry in the setup.web.url domain list
// ------------------------------------------------------------------------------------------------
static const char* get_setup_url(const char *setting, char *buffer, size_t buffer_length)
{
    gos_settings_get_print_str("setup.web.url", buffer, buffer_length);

    char *comma_ptr = strchr(buffer, ',');
    *comma_ptr = 0;

    return buffer;
}


// ------------------------------------------------------------------------------------------------
// Start the measurement then schedule a timed event to read the measurment
// ------------------------------------------------------------------------------------------------
static void start_measurement_event_handler(void *arg)
{
    //Initiate the measurment
    Si7013_StartNoHoldMeasureRHAndTemp(&si7021_device, 0);

    // The Si7013 takes a maximum of 50ms to complete a measurement
    // therefore we register an event to read the results 50ms later
    gos_event_register_timed(read_and_report_event_handler, arg, SI7021_MEASUREMENT_TIME_MS, 0);
}

// ------------------------------------------------------------------------------------------------
// Read the measurement then report the results
// ------------------------------------------------------------------------------------------------
static void read_and_report_event_handler(void *arg)
{   gos_result_t result = GOS_SUCCESS;
    uint32_t rh_data;
    int32_t temp_data;
    char temp_float_str[10];
    char rh_float_str[10];
    gos_msgpack_context_t *weather_msgpack = NULL;
    const gos_dms_messsage_config_t config =
    {
      .length                 = 0,
      .is_response            = false,
      .response.handler       = 0,
      .response.timeout_ms    = 90000     //90 second timeout
    };

    //Read the measurement results
    Si7013_ReadNoHoldRHAndTemp(&si7021_device, 0, &rh_data, &temp_data);

    //Format the results to a floating point number and output to the console
    GOS_LOG("Temp: %s C | RH: %s %%",
             float_to_str((float)(temp_data/1000.0), temp_float_str, 1),
             float_to_str((float)(rh_data/1000.0), rh_float_str, 1));

    // Initialize the write context
    if(GOS_FAILED(result, gos_dms_message_write_init(&weather_msgpack, &config)))
    {
      return;
    }

    gos_msgpack_write_dict_marker(weather_msgpack, 3);
    gos_msgpack_write_dict_str(weather_msgpack, "request", "webhook");   // the connector is of type webhook
    gos_msgpack_write_dict_str(weather_msgpack, "code", "03MWMCWEATHER");   // code must match connector setup on DMS
    gos_msgpack_write_dict_dict(weather_msgpack, "data", 2);             // container for the data

    // Add ambient light data to the msgpack
    gos_msgpack_write_dict_dict(weather_msgpack, "Temperature", 5);
    gos_msgpack_write_dict_str(weather_msgpack, "value", temp_float_str);
    gos_msgpack_write_dict_str(weather_msgpack, "min", "0");
    gos_msgpack_write_dict_str(weather_msgpack, "max", "50");
    gos_msgpack_write_dict_str(weather_msgpack, "units", "");
    gos_msgpack_write_dict_str(weather_msgpack, "precision", "1");

    // Add prox data to the msgpack
    gos_msgpack_write_dict_dict(weather_msgpack, "Humidity", 5);
    gos_msgpack_write_dict_str(weather_msgpack, "value", rh_float_str);
    gos_msgpack_write_dict_str(weather_msgpack, "min", "0");
    gos_msgpack_write_dict_str(weather_msgpack, "max", "50");
    gos_msgpack_write_dict_str(weather_msgpack, "units", "");
    gos_msgpack_write_dict_str(weather_msgpack, "precision", "1");

    // Send the message to the DMS
    if(GOS_FAILED(result, gos_dms_message_write_flush(weather_msgpack)))
    {
      gos_dms_message_context_destroy(weather_msgpack);
    }

    GOS_LOG("Posting sensor data to the cloud via a DMS Cloud Connector");


}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void gos_app_deinit(void)
{
    // This API is called just before the application finishes.
    // Anything initialized or allocated by the app should be cleaned here.
    // This API is optional.
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void gos_app_idle(void)
{
    // This is called when the event thread has no more pending events.
    // This API is optional.
}