demo/gps/main.c

See examples/demo/gps

/*
* ZentriOS SDK LICENSE AGREEMENT | Zentri.com, 2015.
*
* Use of source code and/or libraries contained in the ZentriOS SDK is
* subject to the Zentri Operating System SDK license agreement and
* applicable open source license agreements.
*
*/
#include "zos.h"
#define DATA_UART PLATFORM_AUX_UART
#define UART_POLL_PERIOD_MS 100
#define MSG_PREFIX "$GPGGA"
#define STREAM_NAME "gpsstream"
static void uart_rx_event_handler(void *arg);
static void initialize_uart(void);
static void parse_message(char *message, zos_hs_handle_t handle);
static zos_result_t send_gps_data(const zos_hs_handle_t handle, int32_t latitude, int32_t longitude);
static zos_result_t gps_stream_callback(zos_hs_handle_t handle, const char *stream, zos_hs_stream_method_t method, void *arg);
static char *strtok_single(char * str, char const * delims);
static uint8_t ring_buffer_data[512];
static uint8_t rx_buffer[256] = {0};
static uint8_t *ptr = rx_buffer;
/*************************************************************************************************/
void zn_app_init(void)
{
zos_result_t result;
char mdns_url[32];
ZOS_LOG("Starting GPS Demo...");
if(zn_load_app_settings("settings.ini") != ZOS_SUCCESS)
{
ZOS_LOG("Failed to load settings");
return;
}
{
ZOS_LOG("Failed to restart network: %d\r\n\r\n", result);
ZOS_LOG("----------------------------------------------------------------------");
ZOS_LOG("This app expects valid network credentials have been configured. ");
ZOS_LOG("Join a network and save credentials to non-volatile memory using the ");
ZOS_LOG("ZentriOS commands shown below ");
ZOS_LOG(" ");
ZOS_LOG("> network_up -s ");
ZOS_LOG("> save ");
ZOS_LOG("----------------------------------------------------------------------");
ZOS_LOG("\r\n ");
}
initialize_uart();
zn_settings_get_str("mdns.name", mdns_url, sizeof(mdns_url));
zn_hs_stream_register_callback(STREAM_NAME, gps_stream_callback, NULL);
ZOS_LOG("To view your current location on map: http://%s.local", mdns_url);
}
/*************************************************************************************************/
void zn_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.
zn_event_unregister(uart_rx_event_handler, NULL);
}
/*************************************************************************************************/
zos_bool_t zn_app_idle(void)
{
// This is called when the event thread has no more pending events.
// This should return ZOS_FALSE if the application should exit,
// this should return ZOS_TRUE if the application should idle and wait for more events.
// This API is optional.
return ZOS_TRUE;
}
/*************************************************************************************************/
static void initialize_uart(void)
{
const zos_uart_config_t config =
{
.baud_rate = 9600,
.data_width = UART_WIDTH_8BIT,
.parity = UART_NO_PARITY,
.stop_bits = UART_STOP_BITS_1,
.flow_control = UART_FLOW_DISABLED
};
const zos_uart_buffer_t uart_buffer =
{
.buffer = ring_buffer_data,
.length = sizeof(ring_buffer_data)
};
zn_uart_configure(DATA_UART, &config, &uart_buffer);
}
/*************************************************************************************************/
static void uart_rx_event_handler(void *arg)
{
const uint8_t *dummy;
uint16_t bytes_read;
// see if the UART had any data available
zn_uart_peek_bytes(DATA_UART, &dummy, &bytes_read);
// each NMEA message begins with a ($) dollar sign.
while((bytes_read > 0) && (rx_buffer[0] != '$'))
{
bytes_read--;
// read and skip all characters till $ is found (record delimiter)
zn_uart_receive_bytes(DATA_UART, rx_buffer, 1, ZOS_NO_WAIT);
}
// reaching here means current buffer starts with $
// each NMEA message terminates with /r/n
while(bytes_read > 0)
{
bytes_read--;
// increment pointer to point to next character
ptr++;
// sanity check
if((ptr - rx_buffer) >= (sizeof(rx_buffer)))
{
ZOS_LOG("Long message.. skip");
// reset rx_buffer and pointer
memset(rx_buffer, 0, sizeof(rx_buffer));
ptr = rx_buffer;
break;
}
zn_uart_receive_bytes(DATA_UART, ptr, 1, ZOS_NO_WAIT);
if((ptr[0] == 13) || (ptr[0] == 10))
{
// reaching here means we have one complete message starts with $
// and terminates with /r or /n
parse_message((char *)rx_buffer, handle);
// reset rx_buffer and pointer
memset(rx_buffer, 0, sizeof(rx_buffer));
ptr = rx_buffer;
break;
}
}
}
/*************************************************************************************************/
static void parse_message(char *message, zos_hs_handle_t handle)
{
//ZOS_LOG("%s", message);
if(strcmp(MSG_PREFIX, strtok_single(message, ",")) == 0)
{
int32_t longitude;
int32_t latitude;
ZOS_LOG(" UTC Time : %s", strtok_single(NULL, ","));
latitude = strtod(strtok_single(NULL, ","), NULL) * 10000;
if(strcmp(strtok_single(NULL, ","), "S") == 0)
{
latitude = -latitude;
}
ZOS_LOG(" Latitude : %d (ddmmmmmm)", latitude);
longitude = strtod(strtok_single(NULL, ","), NULL) * 10000;
if(strcmp(strtok_single(NULL, ","), "W") == 0)
{
longitude = -longitude;
}
ZOS_LOG(" Longitude : %d (dddmmmmmm)", longitude);
ZOS_LOG(" Pos Fix : %s", strtok_single(NULL, ","));
ZOS_LOG(" Satellites : %s", strtok_single(NULL, ","));
ZOS_LOG(" HDOP : %s", strtok_single(NULL, ","));
ZOS_LOG(" Altitude : %s (%s)", strtok_single(NULL, ","), strtok_single(NULL, ","));
ZOS_LOG(" Separation : %s (%s)", strtok_single(NULL, ","), strtok_single(NULL, ","));
ZOS_LOG(" Diff Corr : %s", strtok_single(NULL, ","));
ZOS_LOG(" Checksum : %s", strtok_single(NULL, ","));
ZOS_LOG("----------------------------");
send_gps_data(handle, latitude, longitude);
}
}
/*************************************************************************************************/
// two delimiters in row are not handled by strtok (treat them as only one delimiter)
// this implementation overwrites the standard strtok to handle two delimiter correctly
static char *strtok_single(char * str, char const * delims)
{
static char * src = NULL;
char * p, * ret = 0;
if (str != NULL)
{
src = str;
}
if (src == NULL)
{
return NULL;
}
if ((p = strpbrk (src, delims)) != NULL)
{
*p = 0;
ret = src;
src = ++p;
}
else if (*src)
{
ret = src;
src = NULL;
}
return ret;
}
/*************************************************************************************************/
static zos_result_t gps_stream_callback(zos_hs_handle_t handle, const char *stream, zos_hs_stream_method_t method, void *arg)
{
if(method == HS_STREAM_LISTEN)
{
ZOS_LOG("GPS stream listener registered, streaming data");
// register handler to periodically poll UART data
zn_event_register_periodic(uart_rx_event_handler, handle, UART_POLL_PERIOD_MS, EVENT_FLAGS1(RUN_NOW));
}
else if(method == HS_STREAM_UNLISTEN)
{
ZOS_LOG("GPS stream listener unregistered");
zn_event_unregister(uart_rx_event_handler, handle);
}
else if(method == HS_STREAM_READ)
{
char *update_interval_str;
if(zn_hs_stream_read(handle, &update_interval_str, NULL) == ZOS_SUCCESS)
{
uint32_t update_interval = str_to_uint32(update_interval_str);
zn_event_update_periodic(uart_rx_event_handler, handle, update_interval, EVENT_FLAGS1(RUN_NOW));
ZOS_LOG("GPS update interval: %d", update_interval);
}
}
return ZOS_SUCCESS;
}
/*************************************************************************************************/
static zos_result_t send_gps_data(const zos_hs_handle_t handle, int32_t latitude, int32_t longitude)
{
char buffer[64];
char *ptr = buffer;
ptr += sprintf(ptr, "{\"lat\":%d,", (int)latitude);
ptr += sprintf(ptr, "\"lng\":%d}", (int)longitude);
{
.data = (uint8_t*) buffer,
.size = (uint16_t)(ptr - buffer)
};
// JSON encode the data in-place
return zn_hs_stream_write_listener(handle, STREAM_NAME, buffer, ZOS_TRUE);
}