demo/3d_demo/main.c

/*******************************************************************************
* # License
* Copyright 2019 Silicon Laboratories Inc. www.silabs.com
*******************************************************************************
*
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
******************************************************************************/
#include "gos.h"
#include "sensor.h"
#define APPLICATION_START_LINE "\r\n\r\n3D Demo starting..."
#define DEFAULT_ACCELEROMETER_DATA_PERIOD 35
static uint8_t client_count;
static uint32_t update_interval;
/*************************************************************************************************/
void gos_app_init(void)
{
gos_result_t result;
GOS_LOG(APPLICATION_START_LINE);
client_count = 0;
update_interval = DEFAULT_ACCELEROMETER_DATA_PERIOD;
// Load the application settings
if (GOS_FAILED(result, gos_load_app_settings("settings.ini")))
{
GOS_LOG("Failed to load settings, err:%d", result);
return;
}
else if(GOS_FAILED(result, initialize_hardware()))
{
GOS_LOG("Failed to initialize hardware");
return;
}
gos_hs_stream_register("interval", interval_stream_handler, GOS_HS_STREAM_PERMISSION_ALL, NULL);
gos_hs_stream_register("msg", message_stream_handler, GOS_HS_STREAM_PERMISSION_WRITE, NULL);
gos_hs_stream_register("temp", temperature_stream_handler, GOS_HS_STREAM_PERMISSION_READ, NULL);
// Set the websocket connect/disconnect handlers
// These handlers are invoked when a websocket client connects or disconnects
gos_hs_stream_ws_set_handlers(websocket_connect_handler, websocket_disconnect_handler);
// Bring up the softap interface
GOS_LOG("Ready.");
}
/*************************************************************************************************
* This is the 'interval' stream handler.
* This is invoked when a browser wants to read or write the 'interval' stream.
*/
static void interval_stream_handler(const char *stream_name, const gos_msgpack_object_t *in_data,
gos_msgpack_context_t *out_context, void *arg)
{
if(in_data != NULL)
{
const gos_msgpack_object_t *value_obj = MSGPACK_DICT(in_data, "value");
if(value_obj != NULL)
{
const uint32_t new_interval = MSGPACK_UINT(value_obj);
// If the update interval changed
if(new_interval != update_interval)
{
GOS_LOG("Data interval updated to: %d", new_interval);
// Update the value
update_interval = new_interval;
// Broadcast to all clients the new value
gos_event_issue(broadcast_interval_updated, NULL, GOS_EVENT_FLAG_NONE);
}
}
}
else
{
gos_result_t result;
gos_msgpack_write_dict_uint(out_context, "value", update_interval);
if(GOS_FAILED(result, gos_hs_stream_flush(out_context, GOS_NO_WAIT)))
{
GOS_LOG("Failed to flush interval stream, err:%d", result);
}
}
}
/*************************************************************************************************
* This is the 'msg' stream handler.
* This is invoked when the browser writes a message to the device via 'msg' stream.
*/
static void message_stream_handler(const char *stream_name, const gos_msgpack_object_t *in_data,
gos_msgpack_context_t *out_context, void *arg)
{
const gos_msgpack_object_str_t *value_obj = MSGPACK_DICT_STR(in_data, "value");
if(value_obj != NULL)
{
char message_str[256];
MSGPACK_STR(value_obj, message_str, sizeof(message_str));
GOS_LOG("Message from browser: %s", message_str);
}
}
/*************************************************************************************************
* This is the 'temperature' stream handler.
* This is invoked when the browser wants to read the temperature/humidity via 'temp' stream.
*/
static void temperature_stream_handler(const char *stream_name, const gos_msgpack_object_t *in_data,
gos_msgpack_context_t *out_context, void *arg)
{
gos_result_t result;
thermometer_data_t data;
if(GOS_FAILED(result, sensor_get_data(SENSOR_THERMOMETER, &data)))
{
GOS_LOG("Failed to read thermometer, err:%d", result);
data.humidity = 0;
data.temperature = 0;
}
gos_msgpack_write_dict_float(out_context, "temperature", data.temperature);
gos_msgpack_write_dict_float(out_context, "humidity", data.humidity);
if(GOS_FAILED(result, gos_hs_stream_flush(out_context, GOS_NO_WAIT)))
{
GOS_LOG("Failed to flush temperate stream, err:%d", result);
}
}
/*************************************************************************************************
* This is the accelerometer data event handler.
* This is periodically invoked when the device should read the accelerometer
* and broadcast the measurement to all connected clients.
*/
static void accelerometer_data_event_handler(void *unused)
{
gos_result_t result;
accelerometer_data_t data;
gos_msgpack_context_t *out_context;
if(GOS_FAILED(result, sensor_get_data(SENSOR_ACCELEROMETER, &data)))
{
GOS_LOG("Failed to read accelerometer, err:%d", result);
}
else if(GOS_FAILED(result, gos_hs_stream_ws_write(GOS_HS_STREAM_WRITE_ALL, "accel", &out_context)))
{
}
else
{
gos_msgpack_write_dict_float(out_context, "x", data.x);
gos_msgpack_write_dict_float(out_context, "y", data.y);
gos_msgpack_write_dict_float(out_context, "z", data.z);
if(GOS_FAILED(result, gos_hs_stream_flush(out_context, GOS_NO_WAIT)))
{
GOS_LOG("Failed to flush accel stream, err:%d", result);
}
}
gos_event_register_timed(accelerometer_data_event_handler, NULL, update_interval, GOS_EVENT_FLAG_NONE);
}
/*************************************************************************************************
* This is called when by this app when the update interval is updated.
* This broadcasts write the the 'interval' stream which all browsers have registered.
* This ensures all connected browser are sync'd.
*/
static void broadcast_interval_updated(void *unused)
{
gos_result_t result;
gos_msgpack_context_t *out_context;
if(GOS_FAILED(result, gos_hs_stream_ws_write(GOS_HS_STREAM_WRITE_ALL, "interval", &out_context)))
{
}
else
{
gos_msgpack_write_dict_uint(out_context, "value", update_interval);
if(GOS_FAILED(result, gos_hs_stream_flush(out_context, GOS_NO_WAIT)))
{
GOS_LOG("Failed to flush interval stream, err:%d", result);
}
}
}
/*************************************************************************************************
* This is invoked when a browser connects.
* If this is the first client to connect then the accelerometer data loop is started.
*/
static void websocket_connect_handler(void *client)
{
client_count++;
GOS_LOG("Client connected");
GOS_LOG("Client count: %d", client_count);
if(client_count == 1)
{
GOS_LOG("Starting data loop ...");
gos_event_issue(accelerometer_data_event_handler, NULL, GOS_EVENT_FLAG_NONE);
}
}
/*************************************************************************************************
* This is invoked when a client disconnects.
* If no more clients are connected then the accelerometer data loop is stopped.
*/
static void websocket_disconnect_handler(void *client)
{
client_count--;
GOS_LOG("Client disconnected");
GOS_LOG("Client count: %d", client_count);
if(client_count == 0)
{
GOS_LOG("Stopping data loop ...");
gos_event_unregister(accelerometer_data_event_handler, NULL);
}
}
/*************************************************************************************************
* Initialize the thermometer and accelerometer sensors.
*/
static gos_result_t initialize_hardware(void)
{
gos_result_t sensor_result;
const thermometer_config_t thermometer_config =
{
.port = PLATFORM_STD_I2C,
.print_init_details = true,
.block_while_measure = true,
.init_retries = 3
};
const accelerometer_config_t accelerometer_config =
{
.port = PLATFORM_STD_I2C,
.init_retries = 3
};
{
GOS_LOG("Failed to init board specific hw, err:%d", result);
return result;
}
GOS_LOG("Initializing thermometer sensor ...");
if(GOS_FAILED(sensor_result, sensor_init(SENSOR_THERMOMETER, &thermometer_config)))
{
GOS_LOG("WARN: Failed to init thermometer, err:%d", sensor_result);
}
GOS_LOG("Initializing accelerometer ...");
if(GOS_FAILED(sensor_result, sensor_init(SENSOR_ACCELEROMETER, &accelerometer_config)))
{
GOS_LOG("WARN: Failed to init accelerometer, err:%d", sensor_result);
}
// Just return success regardless if a sensor fails
return GOS_SUCCESS;
}
/*************************************************************************************************/
static void network_event_handler(bool is_up)
{
if(is_up)
{
char str_buffer[64];
char password_str[64];
gos_settings_get_print_str("softap.passkey", password_str, sizeof(password_str));
GOS_LOG("Softap interface ready\r\n\r\n");
GOS_LOG("Perform following steps to view demo:");
GOS_LOG("1. Connect your PC/phone to Wi-Fi network: %s", gos_settings_get_print_str("softap.ssid", str_buffer, sizeof(str_buffer)));
if(*password_str != 0)
{
GOS_LOG(" NOTE: The Wi-Fi password is: %s", password_str);
}
GOS_LOG("2. Open browser to: http://%s", gos_settings_get_print_str("softap.static.ip", str_buffer, sizeof(str_buffer)));
GOS_LOG("3. Click the 'connect' button on the webpage");
GOS_LOG("4. Rotate the dev board to see the 3D model update in real-time");
GOS_LOG("5. Enter a message in the text input to see it printed on the device's log bus\r\n\r\n");
}
}