Main.c for the Hurricane Telemetry Tutorial

/*
 * 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 "example_app_util.h"
#include "hurricane.h"


// Update this value anytime settings.ini changes
// This will cause Gecko OS to reload the settings
#define SETTINGS_MAGIC_NUMBER 0x8


static void start_demo_handler(void *arg);
static void stop_demo_handler(void *arg);

hurricane_demo_t demo_info =
{ .demo_name = "Telemetry",                                   // Demo name used on LCD
  .start_demo_callback = start_demo_handler,                  // Function called when user selects Run
  .stop_demo_callback = stop_demo_handler,                    // Function called when leaving the demo and returning to hurricane setup
  .draw_splash_screen_callback = NULL,                        // NULL = Do not override splash screen display function
  .demo_splash_screen_arg = NULL,                             // Splash screen jpeg file (NULL = text only)
  .splash_font_color = HURRICANE_SPLASH_DEFAULT_FONT_COLOR,   // Color of the splash screen navigation text
  .splash_screen_timeout_ms = 0                               // Splash screen timeout (0 = no timeout)
};


/*************************************************************************************************/
void gos_app_init(void)
{
    gos_result_t result;

    // Load the application settings if necessary
    // NOTE: If the 'magic number' is different, then
    // 1. The settings.ini is load
    // 2. Settings are saved to NVM
    // 3. The device is rebooted
    //
    // Upon reboot the device will automatically bringup the WLAN inteface and open the DMS websocket
    if(GOS_FAILED(result, gos_load_app_settings_once("settings.ini", SETTINGS_MAGIC_NUMBER)))
    {
      GOS_LOG("Failed to load settings, err:%d", result);
    }

    hurricane_init(&demo_info);

}


/*************************************************************************************************/
static void start_demo_handler(void *arg)
{
    GOS_LOG("\r\nDMS Telemetry example starting ...");

    hurricane_draw_message_screen("Telemetry and Cloud Demo", GUI_TA_HCENTER | GUI_TA_VCENTER, "", "");

    // Initialize the button GPIOs
    gos_gpio_init(PLATFORM_BUTTON1, GOS_GPIO_INPUT_HIGH_IMPEDANCE, false);
    gos_gpio_init(PLATFORM_BUTTON2, GOS_GPIO_INPUT_HIGH_IMPEDANCE, false);

    // Set the connection handler
    gos_dms_set_message_stream_state_handler(message_stream_connection_handler);

    // Set the telemetry callback
    gos_dms_set_telemetry_callback(telemetry_callback);

    // Bring up the network if it isn't already
    // This also pretty prints an error if no network credentials are set
    example_app_util_network_up(GOS_INTERFACE_WLAN, false, NULL);

    gos_event_register_periodic(sensor_report_event_handler, 0, 5000, 0);
}


/*************************************************************************************************/
static void stop_demo_handler(void *arg)
{ GOS_LOG("Exiting Hurricane Demo");
}


/*************************************************************************************************/
static gos_result_t telemetry_callback(gos_msgpack_context_t *msgpack, uint32_t *pair_count_ptr)
{
    char temp_str[128];

    // NOTE: The msgpack already has the root dictionary marker written
    //       We just need to add to the dictionary

    // Add: buttons [
    gos_msgpack_write_dict_array(msgpack, "buttons", 2);

    // Add: { btn1: <true/false> }
    gos_msgpack_write_dict_marker(msgpack, 1);
    gos_msgpack_write_dict_bool(msgpack, "btn1", gos_gpio_get(PLATFORM_BUTTON1) == PLATFORM_BUTTON_ACTIVE_STATE);

    // Add: { btn2: <true/false> }
    gos_msgpack_write_dict_marker(msgpack, 1);
    gos_msgpack_write_dict_bool(msgpack, "btn2", gos_gpio_get(PLATFORM_BUTTON2) == PLATFORM_BUTTON_ACTIVE_STATE);

    // Add: utc       : "<UTC string>"
    gos_time_get_current_utc_as_iso8601_str((gos_iso8601_str_t*)temp_str, true);
    gos_msgpack_write_dict_str(msgpack, "utc", (const char*)temp_str);

    *pair_count_ptr = 2; // we added 2 entries to the root dictionary

    GOS_LOG("Posting telemetry to DMS for device: %s", gos_system_get_uuid_str(temp_str));

    return GOS_SUCCESS;
}


/*************************************************************************************************/
static void message_stream_connection_handler(bool is_up)
{

    if (is_up == true)
    {
        char buffer[32];

        GOS_LOG("DMS connection up");

        GOS_LOG("Posting telemetry every %s seconds", gos_settings_get_print_str("dms.telemetry.interval", buffer, sizeof(buffer)));
        GOS_LOG("NOTE: You can also issue the command:");
        GOS_LOG("> dms_telemetry");
        GOS_LOG("To immediately post telemetry to the DMS");
    }
    else
    {
        GOS_LOG("DMS connection down");
    }
}


/*************************************************************************************************/
static void sensor_report_event_handler(void *arg)
{
  gos_result_t result;
  char als_float_str[10], prox_float_str[10];
  char display_message[128];
  float als, prox;
  gos_msgpack_context_t *sensor_msgpack = NULL;
  const gos_dms_messsage_config_t config =
  {
    .length                 = 0,
    .is_response            = false,
    .response.handler       = 0,
    .response.timeout_ms    = 90000     //90 second timeout
  };

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

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

// Add ambient light data to the msgpack
  als = hurricane_sensor_get_ALS();
  float_to_str(als, als_float_str, 2);
  gos_msgpack_write_dict_dict(sensor_msgpack, "Ambient Light", 5);
  gos_msgpack_write_dict_str(sensor_msgpack, "value", als_float_str);
  gos_msgpack_write_dict_str(sensor_msgpack, "min", "0");
  gos_msgpack_write_dict_str(sensor_msgpack, "max", "500");
  gos_msgpack_write_dict_str(sensor_msgpack, "units", "");
  gos_msgpack_write_dict_str(sensor_msgpack, "precision", "1");

// Add prox data to the msgpack
  prox = hurricane_sensor_get_prox();
  float_to_str(prox, prox_float_str, 2);
  gos_msgpack_write_dict_dict(sensor_msgpack, "Proximity", 5);
  gos_msgpack_write_dict_str(sensor_msgpack, "value", prox_float_str);
  gos_msgpack_write_dict_str(sensor_msgpack, "min", "0");
  gos_msgpack_write_dict_str(sensor_msgpack, "max", "1000");
  gos_msgpack_write_dict_str(sensor_msgpack, "units", "");
  gos_msgpack_write_dict_str(sensor_msgpack, "precision", "1");

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

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

  sprintf(display_message, "Telemetry and Could Demo\nALS: %s\nProx: %s", als_float_str, prox_float_str);
  hurricane_draw_message_screen(display_message, GUI_TA_HCENTER | GUI_TA_VCENTER, "", "");

}