dms/messages/main.c

/*
* 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"
// Timeout value in milliseconds when waiting for a response from the DMS
#define MESSAGE_RESPONSE_TIMEOUT_MS 3000
// Update this value anytime settings.ini changes
// This will cause Gecko OS to reload the settings
#define SETTINGS_MAGIC_NUMBER 0x01
/*************************************************************************************************/
void gos_app_init(void)
{
gos_result_t result;
GOS_LOG("\r\nDMS Messages example starting ...");
// 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);
}
// 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 DMS fallback handler
// This will catch any messages that aren't handled internally
// i.e. Message that don't have a registered 'action' listener OR
// are not a response to a message sent from the device.
gos_dms_set_message_fallback_handler(dms_message_fallback_handler);
// Register a 'ping' action listener
// This will be invoked for received messages with:
// {
// action : "ping"
// ...
// }
gos_dms_message_register_action_listener("ping", dms_ping_action_listener);
// Bring up the network if it isn't already
// This also prints an error if no network credentials are set
example_app_util_network_up(GOS_INTERFACE_WLAN, false, NULL);
}
/*************************************************************************************************/
static void message_stream_connection_handler(bool is_up)
{
if(is_up == true)
{
GOS_LOG("Connection stream up");
GOS_LOG("Starting periodic message loop");
gos_event_issue(send_message_handler, NULL, GOS_EVENT_FLAG_NONE);
}
else
{
GOS_LOG("Connection stream down");
GOS_LOG("Stopping periodic message loop");
gos_event_unregister(send_message_handler, NULL);
}
}
/*************************************************************************************************/
static void send_message_handler(void *unused)
{
gos_result_t result;
gos_msgpack_context_t *msgpack = NULL;
{
.length = 0,
.response.handler = message_response_handler,
.response.timeout_ms = MESSAGE_RESPONSE_TIMEOUT_MS
};
GOS_LOG("Sending message ...");
// Initialize the write context
// Note: msgpack is dynamically allocated and gets freed if gos_dms_message_write_flush() succeeds
// if this is not the case, it needs to be freed by calling gos_dms_message_context_destroy()
if(GOS_FAILED(result, gos_dms_message_write_init(&msgpack, &config)))
{
GOS_LOG("Failed to init write context");
}
// Write the message data
else if(GOS_FAILED(result, populate_message(msgpack)))
{
GOS_LOG("Failed to populate message");
}
// Send the message to the DMS
else if(GOS_FAILED(result, gos_dms_message_write_flush(msgpack)))
{
GOS_LOG("Failed to flush message");
}
else
{
GOS_LOG("Message sent");
// Now wait for the response
}
// On failure clean up
if(result != GOS_SUCCESS)
{
GOS_LOG("Failed to send message, err:%d", result);
GOS_LOG("Re-invoking message send");
gos_event_issue(send_message_handler, NULL, GOS_EVENT_FLAG_NONE);
}
}
/*************************************************************************************************
* The message has the following format:
* {
* request : "echo",
* uptime : <uptime number>,
* rtc : <rtc number>,
* utc : "<UTC string>",
* version : "<version string>",
* buttons : [
* { btn1: <true/false> },
* { btn2: <true/false> }
* ]
* }
*/
static gos_result_t populate_message(gos_msgpack_context_t *msgpack)
{
uint64_t temp_uint64;
char temp_str[128];
// The root dictionary will have 6 key/value pairs
// Add: request : "echo"
gos_msgpack_write_dict_str(msgpack, "request", "echo");
// Add: uptime : <uptime number>,
gos_time_get_uptime(&temp_uint64);
gos_msgpack_write_dict_ulong(msgpack, "uptime", temp_uint64);
// Add: rtc : <rtc number>
gos_time_get_current_utc_ms(&temp_uint64, false);
gos_msgpack_write_dict_ulong(msgpack, "rtc", temp_uint64);
// Add: utc : "<UTC string>"
gos_msgpack_write_dict_str(msgpack, "utc", (const char*)temp_str);
// Add: version : "<version string>"
// Add: buttons [
gos_msgpack_write_dict_array(msgpack, "buttons", 2);
// Add: { btn1: <true/false> }
gos_msgpack_write_dict_bool(msgpack, "btn1", gos_gpio_get(PLATFORM_BUTTON1) == PLATFORM_BUTTON_ACTIVE_STATE);
// Add: { btn2: <true/false> }
gos_msgpack_write_dict_bool(msgpack, "btn2", gos_gpio_get(PLATFORM_BUTTON2) == PLATFORM_BUTTON_ACTIVE_STATE);
return GOS_SUCCESS;
}
/*************************************************************************************************/
static void message_response_handler(void *msg)
{
gos_result_t result;
// Recursively iterate through all elements in the message and print to log
if(GOS_FAILED(result, MSGPACK_FOREACH_RECURSIVE(msg, msgpack_iterator, NULL)))
{
GOS_LOG("Failed to iterate msg, err:%d", result);
}
// Clean up message
// Randomly generate a timeout between 0-15s for the next message to send
const uint32_t next_timeout = gos_get_random_uint32(0, 15000);
GOS_LOG("Sending next message in %d seconds", next_timeout/1000);
gos_event_register_timed(send_message_handler, NULL, next_timeout, GOS_EVENT_FLAG_NONE);
}
/*************************************************************************************************/
static gos_result_t msgpack_iterator(const gos_msgpack_object_t *key, const gos_msgpack_object_t *value, void *arg)
{
char value_str[128];
// If we're iterating a dictionary
if(key != NULL)
{
char key_str[128];
GOS_LOG("%s : %s", MSGPACK_STR(key, key_str, sizeof(key_str)), MSGPACK_TO_STR(value, value_str, sizeof(value_str)));
}
// Else we're iterating an array
else
{
GOS_LOG("%s", MSGPACK_TO_STR(value, value_str, sizeof(value_str)));
}
return GOS_SUCCESS;
}
/*************************************************************************************************/
static void dms_message_fallback_handler(void *msg)
{
GOS_LOG("Unhandled message received");
// Clean up message
}
/*************************************************************************************************/
static void dms_ping_action_listener(void *msg)
{
GOS_LOG("Ping action received");
// Clean up message
}