From 41971a95b2d1516e948ca561cd1d1c14c08c23d5 Mon Sep 17 00:00:00 2001 From: Deomid Ryabkov Date: Thu, 24 Mar 2016 12:58:26 +0000 Subject: [PATCH] Add LED status and accelerator data feed It is also possible to control LED by sending a command: {"t": 1, "v": 0/1/2} 0/1 = off/on, 2 = toggle PUBLISHED_FROM=da176b70c4cd356c5d56eca58c2926619748d895 --- examples/CC3200/Makefile.build | 4 +- examples/CC3200/bm222.c | 104 ++++++++++++++++++ examples/CC3200/bm222.h | 28 +++++ examples/CC3200/main.c | 191 +++++++++++++++++++++++++++------ examples/CC3200/tmp006.c | 9 +- examples/CC3200/tmp006.h | 8 +- 6 files changed, 305 insertions(+), 39 deletions(-) create mode 100644 examples/CC3200/bm222.c create mode 100644 examples/CC3200/bm222.h diff --git a/examples/CC3200/Makefile.build b/examples/CC3200/Makefile.build index 06f45fb4..c3d07cdd 100644 --- a/examples/CC3200/Makefile.build +++ b/examples/CC3200/Makefile.build @@ -92,7 +92,7 @@ CC3200_PLATFORM_SRCS = cc3200_fs.c cc3200_fs_slfs.c cc3200_libc.c cc3200_socket. VPATH += $(COMMON_PATH)/platforms/cc3200 FREERTOS_SRCS = timers.c list.c queue.c tasks.c port.c heap_3.c osi_freertos.c -DRIVER_SRCS = cpu.c i2c.c i2c_if.c interrupt.c pin.c prcm.c spi.c uart.c udma.c utils.c +DRIVER_SRCS = cpu.c gpio_if.c i2c_if.c interrupt.c pin.c prcm.c spi.c uart.c udma.c utils.c SL_SRCS = socket.c wlan.c driver.c device.c netapp.c netcfg.c cc_pal.c fs.c SDK_SRCS = startup_gcc.c $(FREERTOS_SRCS) $(DRIVER_SRCS) $(SL_SRCS) IPATH += $(SDK_PATH) $(SDK_PATH)/inc $(SDK_PATH)/driverlib \ @@ -107,7 +107,7 @@ VPATH += $(SDK_PATH)/driverlib $(SDK_PATH)/example/common $(SDK_PATH)/oslib \ $(SDK_PATH)/third_party/FreeRTOS/source/portable/GCC/ARM_CM4 \ $(SDK_PATH)/third_party/FreeRTOS/source/portable/MemMang \ -APP_SRCS = main.c tmp006.c mongoose.c $(CC3200_PLATFORM_SRCS) $(SDK_SRCS) +APP_SRCS = main.c bm222.c tmp006.c mongoose.c $(CC3200_PLATFORM_SRCS) $(SDK_SRCS) APP_OBJS = $(addprefix $(OBJDIR)/,$(patsubst %.c,%.o,$(APP_SRCS))) $(FW_ELF): $(APP_OBJS) diff --git a/examples/CC3200/bm222.c b/examples/CC3200/bm222.c new file mode 100644 index 00000000..e62dc8e3 --- /dev/null +++ b/examples/CC3200/bm222.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#include "bm222.h" + +#include "mongoose.h" + +#include "i2c_if.h" + +#define BM222_REG_FIFO_STATUS 0x0e + +#define BM222_REG_PMU_BW 0x10 +#define BM222_PMU_BW_7_81_HZ 0x8 +#define BM222_PMU_BW_31_25_HZ 0xA +#define BM222_PMU_BW_62_5_HZ 0xB +#define BM222_PMU_BW_125_HZ 0xC + +#define BM222_REG_BGW_SOFTRESET 0x14 +#define BM222_DO_SOFT_RESET 0xB6 + +#define BM222_REG_FIFO_CONFIG_1 0x3e +#define BM222_FIFO_MODE_FIFO 0x40 +#define BM222_FIFO_DATA_ALL 0 + +#define BM222_REG_FIFO_DATA 0x3f + +struct bm222_ctx *bm222_init(uint8_t addr) { + struct bm222_ctx *ctx = (struct bm222_ctx *) calloc(1, sizeof(*ctx)); + if (ctx == NULL) return NULL; + ctx->addr = addr; + { + unsigned char val[2] = {BM222_REG_BGW_SOFTRESET, BM222_DO_SOFT_RESET}; + if (I2C_IF_Write(addr, val, 2, 1) != 0) goto out_err; + osi_Sleep(2 /* ms */); /* t_w,up1 = 1.8 ms */ + } + if (!bm222_fifo_init(ctx)) return false; + { + unsigned char val[2] = {BM222_REG_PMU_BW, BM222_PMU_BW_125_HZ}; + if (I2C_IF_Write(addr, val, 2, 1) != 0) goto out_err; + } + return ctx; +out_err: + free(ctx); + return NULL; +} + +bool bm222_fifo_init(struct bm222_ctx *ctx) { + unsigned char val[2] = {BM222_REG_FIFO_CONFIG_1, + BM222_FIFO_MODE_FIFO | BM222_FIFO_DATA_ALL}; + return (I2C_IF_Write(ctx->addr, val, 2, 1) == 0); +} + +bool bm222_get_data(struct bm222_ctx *ctx) { + unsigned char reg = BM222_REG_FIFO_STATUS; + unsigned char val; + if (I2C_IF_ReadFrom(ctx->addr, ®, 1, &val, 1) < 0) return false; + unsigned char len = (val & 0x7f); + unsigned char overflow = (val & 0x80 ? 1 : 0); + LOG(LL_DEBUG, ("FIFO len: %d (ovf? %d)", len, overflow)); + reg = BM222_REG_FIFO_DATA; + int8_t fifo[32 * 6], *v = fifo; + if (I2C_IF_ReadFrom(ctx->addr, ®, 1, (unsigned char *) fifo, len * 6) < + 0) { + return false; + } + double now = mg_time(); + /* Of potentially multiple samples, pick one with maximum difference. */ + int32_t max_d = 0; + bool sampled = false; + struct bm222_sample *ls = ctx->data + ctx->last_index, *s = NULL; + if (ls->ts == 0) { + /* The very first sample. */ + ls->ts = now; + ls->x = v[1]; + ls->y = v[3]; + ls->z = v[5]; + v += 6; + len--; + sampled = true; + s = ls; + } + for (; len > 0; v += 6, len--) { + int32_t dx = ((int32_t) v[1]) - ((int32_t) ls->x); + int32_t dy = ((int32_t) v[3]) - ((int32_t) ls->y); + int32_t dz = ((int32_t) v[5]) - ((int32_t) ls->z); + int32_t d = dx * dx + dy * dy + dz * dz; + if ((d > 2 && d > max_d) || (!sampled && now - ls->ts > 1.0)) { + if (!sampled) { + ctx->last_index = (ctx->last_index + 1) % BM222_NUM_SAMPLES; + s = ctx->data + ctx->last_index; + sampled = true; + } + s->ts = now; + s->x = v[1]; + s->y = v[3]; + s->z = v[5]; + if (d > max_d) max_d = d; + LOG(LL_VERBOSE_DEBUG, ("dx %d dy %d dz %d d %d", dx, dy, dz, d)); + } + } + return (overflow ? bm222_fifo_init(ctx) : true); /* Clear the ovf flag. */ +} diff --git a/examples/CC3200/bm222.h b/examples/CC3200/bm222.h new file mode 100644 index 00000000..58e44c11 --- /dev/null +++ b/examples/CC3200/bm222.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#ifndef CS_MONGOOSE_EXAMPLES_CC3200_BM222_H_ +#define CS_MONGOOSE_EXAMPLES_CC3200_BM222_H_ + +#include +#include + +#define BM222_NUM_SAMPLES 64 +struct bm222_ctx { + uint8_t addr; + struct bm222_sample { + double ts; + int8_t x; + int8_t y; + int8_t z; + } data[BM222_NUM_SAMPLES]; + int last_index; +}; + +struct bm222_ctx *bm222_init(uint8_t addr); +bool bm222_fifo_init(struct bm222_ctx *ctx); +bool bm222_get_data(struct bm222_ctx *ctx); + +#endif /* CS_MONGOOSE_EXAMPLES_CC3200_BM222_H_ */ diff --git a/examples/CC3200/main.c b/examples/CC3200/main.c index 6efa8280..673902b2 100644 --- a/examples/CC3200/main.c +++ b/examples/CC3200/main.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -21,7 +22,8 @@ #include "uart.h" #include "utils.h" -#include +#include "gpio.h" +#include "gpio_if.h" #include "i2c_if.h" #include "simplelink.h" @@ -30,16 +32,20 @@ #include "oslib/osi.h" #include "mongoose.h" + +#include "bm222.h" #include "tmp006.h" +#define WIFI_SSID "YourWiFi" +#define WIFI_PASS "YourPassword" + +#define DATA_SAMPLING_INTERVAL_MS 20 #define CONSOLE_BAUD_RATE 115200 #define CONSOLE_UART UARTA0_BASE #define CONSOLE_UART_PERIPH PRCM_UARTA0 #define SYS_CLK 80000000 -#define WIFI_SSID "YourWiFi" -#define WIFI_PASS "YourPassword" - +#define BM222_ADDR 0x18 #define TMP006_ADDR 0x41 struct sj_event { @@ -50,6 +56,8 @@ struct sj_event { OsiMsgQ_t s_v7_q; struct mg_mgr mg_mgr; +static struct bm222_ctx *s_accel_ctx; + void SimpleLinkWlanEventHandler(SlWlanEvent_t *e) { if (e->Event == SL_WLAN_CONNECT_EVENT) { LOG(LL_INFO, ("WiFi: connected")); @@ -62,8 +70,9 @@ void SimpleLinkNetAppEventHandler(SlNetAppEvent_t *e) { if (e->Event == SL_NETAPP_IPV4_IPACQUIRED_EVENT) { SlIpV4AcquiredAsync_t *ed = &e->EventData.ipAcquiredV4; LOG(LL_INFO, ("IP: %lu.%lu.%lu.%lu", SL_IPV4_BYTE(ed->ip, 3), - SL_IPV4_BYTE(ed->ip, 2), SL_IPV4_BYTE(ed->ip, 1), - SL_IPV4_BYTE(ed->ip, 0))); + SL_IPV4_BYTE(ed->ip, 2), SL_IPV4_BYTE(ed->ip, 1), + SL_IPV4_BYTE(ed->ip, 0))); + GPIO_IF_LedOff(MCU_RED_LED_GPIO); } } @@ -108,7 +117,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *p) { case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { LOG(LL_INFO, ("%p switching to data mode", nc)); nc->handler = data_ev_handler; - nc->ev_timer_time = mg_time(); /* Immediately */ + nc->ev_timer_time = mg_time(); /* Immediately */ break; } case MG_EV_TIMER: { @@ -120,50 +129,149 @@ static void ev_handler(struct mg_connection *nc, int ev, void *p) { s_temp_data.ts = mg_time(); LOG(LL_DEBUG, ("V = %lf mV, T = %lf C", volt, temp)); } - nc->ev_timer_time = mg_time() + 0.05; + bm222_get_data(s_accel_ctx); + nc->ev_timer_time = mg_time() + (DATA_SAMPLING_INTERVAL_MS * 0.001); } } } -struct conn_data { - double last_ts; - double last_temp; - double last_volt; +struct conn_state { + struct temp_data td; + double last_sent_td; + unsigned char led; + double last_sent_led; + double last_sent_acc; }; -static void send_temp(struct mg_connection *nc) { - LOG(LL_DEBUG, ("%p sending temp data", nc)); - mg_printf_websocket_frame( - nc, WEBSOCKET_OP_TEXT, - "{\"type\": \"temp\", \"ts\": %lf, \"v\": %lf, \"t\": %lf}", - s_temp_data.ts, s_temp_data.volt, s_temp_data.temp); +static void send_temp(struct mg_connection *nc, const struct temp_data *td) { + if (td->ts == 0) return; + mg_printf_websocket_frame(nc, WEBSOCKET_OP_TEXT, + "{\"t\": 0, \"ts\": %lf, \"sv\": %lf, \"dt\": %lf}", + td->ts, td->volt, td->temp); } -static void data_ev_handler(struct mg_connection *nc, int ev, void *p) { - struct conn_data *cd = (struct conn_data *) nc->user_data; - if (cd == NULL) { - cd = (struct conn_data *) calloc(1, sizeof(*cd)); - nc->user_data = cd; +static void send_led(struct mg_connection *nc, double ts, unsigned char led) { + if (ts == 0) return; + mg_printf_websocket_frame(nc, WEBSOCKET_OP_TEXT, + "{\"t\": 1, \"ts\": %lf, \"v\": %d}", ts, led); +} + +static void send_acc_sample(struct mg_connection *nc, + const struct bm222_sample *s) { + if (s->ts == 0) return; + mg_printf_websocket_frame( + nc, WEBSOCKET_OP_TEXT, + "{\"t\": 2, \"ts\": %lf, \"x\": %d, \"y\": %d, \"z\": %d}", s->ts, s->x, + s->y, s->z); +} + +static double send_acc_data_since(struct mg_connection *nc, + const struct bm222_ctx *ctx, double since) { + int i = (ctx->last_index + 1) % BM222_NUM_SAMPLES, n = BM222_NUM_SAMPLES; + double last_sent_ts = 0; + for (; n > 0; i = (i + 1) % BM222_NUM_SAMPLES, n--) { + const struct bm222_sample *s = ctx->data + i; + if (s->ts <= since) continue; + send_acc_sample(nc, s); + last_sent_ts = s->ts; + } + return last_sent_ts; +} + +static void process_command(struct mg_connection *nc, unsigned char *data, + size_t len) { + struct json_token *toks = parse_json2((const char *) data, len); + if (toks == NULL) { + LOG(LL_ERROR, ("Invalid command: %.*s", (int) len, data)); + return; + } + struct json_token *t = find_json_token(toks, "t"); + if (t == NULL) { + LOG(LL_ERROR, ("Missing type field: %.*s", (int) len, data)); + goto out_free; + } + if (t->len == 1 && *t->ptr == '1') { + struct json_token *v = find_json_token(toks, "v"); + if (v == NULL) { + LOG(LL_ERROR, ("Missing value: %.*s", (int) len, data)); + goto out_free; + } + if (v->len != 1) { + LOG(LL_ERROR, ("Invalid value: %.*s", (int) len, data)); + goto out_free; + } + switch (*v->ptr) { + case '0': { + GPIO_IF_LedOff(MCU_RED_LED_GPIO); + break; + } + case '1': { + GPIO_IF_LedOn(MCU_RED_LED_GPIO); + break; + } + case '2': { + GPIO_IF_LedToggle(MCU_RED_LED_GPIO); + break; + } + default: { + LOG(LL_ERROR, ("Invalid value: %.*s", (int) len, data)); + goto out_free; + } + } + } else { + LOG(LL_ERROR, ("Unknown command: %.*s", (int) t->len, t->ptr)); + goto out_free; + } +out_free: + free(toks); +} + +static void data_ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + struct conn_state *cs = (struct conn_state *) nc->user_data; + if (cs == NULL) { + cs = (struct conn_state *) calloc(1, sizeof(*cs)); + nc->user_data = cs; } switch (ev) { case MG_EV_POLL: case MG_EV_TIMER: { const double now = mg_time(); + const unsigned char led = GPIO_IF_LedStatus(MCU_RED_LED_GPIO); /* Send if there was a change or repeat last data every second. */ - if (s_temp_data.volt != cd->last_volt || - s_temp_data.temp != cd->last_temp || - now > cd->last_ts + 1.0) { - send_temp(nc); - cd->last_ts = now; - cd->last_volt = s_temp_data.volt; - cd->last_temp = s_temp_data.temp; + if (s_temp_data.volt != cs->td.volt || s_temp_data.temp != cs->td.temp || + now > cs->last_sent_td + 1.0) { + memcpy(&cs->td, &s_temp_data, sizeof(cs->td)); + send_temp(nc, &cs->td); + cs->last_sent_td = now; + } + if (led != cs->led || now > cs->last_sent_led + 1.0) { + send_led(nc, now, led); + cs->led = led; + cs->last_sent_led = now; + } + if (s_accel_ctx != NULL) { + const struct bm222_sample *ls = + s_accel_ctx->data + s_accel_ctx->last_index; + if (cs->last_sent_acc == 0) { + send_acc_sample(nc, ls); + cs->last_sent_acc = ls->ts; + } else { + double last_sent_ts = + send_acc_data_since(nc, s_accel_ctx, cs->last_sent_acc); + if (last_sent_ts > 0) cs->last_sent_acc = last_sent_ts; + } } nc->ev_timer_time = now + 0.05; break; } + case MG_EV_WEBSOCKET_FRAME: { + struct websocket_message *wm = (struct websocket_message *) ev_data; + process_command(nc, wm->data, wm->size); + break; + } case MG_EV_CLOSE: { LOG(LL_INFO, ("%p closed", nc)); - free(cd); + free(cs); break; } } @@ -175,8 +283,16 @@ static void mg_task(void *arg) { osi_MsgQCreate(&s_v7_q, "V7", sizeof(struct sj_event), 32 /* len */); sl_Start(NULL, NULL, NULL); - if (!tmp006_set_config(TMP006_ADDR, TMP006_CONV_2, false)) { + if (!tmp006_init(TMP006_ADDR, TMP006_CONV_2, false)) { LOG(LL_ERROR, ("Failed to init temperature sensor")); + } else { + LOG(LL_INFO, ("Temperature sensor initialized")); + } + s_accel_ctx = bm222_init(BM222_ADDR); + if (s_accel_ctx == NULL) { + LOG(LL_ERROR, ("Failed to init accelerometer")); + } else { + LOG(LL_INFO, ("Accelerometer initialized")); } if (strlen(WIFI_SSID) > 0) { @@ -212,7 +328,7 @@ static void mg_task(void *arg) { struct mg_connection *nc = mg_bind(&mg_mgr, "80", ev_handler); if (nc != NULL) { mg_set_protocol_http_websocket(nc); - nc->ev_timer_time = mg_time(); /* Start data collection */ + nc->ev_timer_time = mg_time(); /* Start data collection */ } else { LOG(LL_ERROR, ("Failed to create listener: %s", err)); } @@ -252,6 +368,14 @@ int main() { MAP_PinTypeI2C(PIN_02, PIN_MODE_1); /* SCL */ I2C_IF_Open(I2C_MASTER_MODE_FST); + /* Set up the red LED. Note that amber and green cannot be used as they share + * pins with I2C. */ + MAP_PRCMPeripheralClkEnable(PRCM_GPIOA1, PRCM_RUN_MODE_CLK); + MAP_PinTypeGPIO(PIN_64, PIN_MODE_0, false); + MAP_GPIODirModeSet(GPIOA1_BASE, 0x2, GPIO_DIR_MODE_OUT); + GPIO_IF_LedConfigure(LED1); + GPIO_IF_LedOn(MCU_RED_LED_GPIO); + VStartSimpleLinkSpawnTask(8); osi_TaskCreate(mg_task, (const signed char *) "mg", 8192, NULL, 3, NULL); osi_start(); @@ -275,4 +399,3 @@ void SimpleLinkHttpServerCallback(SlHttpServerEvent_t *e, void SimpleLinkSockEventHandler(SlSockEvent_t *e) { } - diff --git a/examples/CC3200/tmp006.c b/examples/CC3200/tmp006.c index 0e794aa0..d5402e19 100644 --- a/examples/CC3200/tmp006.c +++ b/examples/CC3200/tmp006.c @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + #include "tmp006.h" #include "mongoose.h" @@ -8,8 +13,8 @@ #define TMP006_REG_DIE_TEMP 0x01 #define TMP006_REG_CONFIG 0x02 -bool tmp006_set_config( - uint8_t addr, enum tmp006_conversion_rate conv_rate, bool drdy_en) { +bool tmp006_init(uint8_t addr, enum tmp006_conversion_rate conv_rate, + bool drdy_en) { unsigned char val[3] = {TMP006_REG_CONFIG, 0x80, 0}; /* Reset first */ if (I2C_IF_Write(addr, val, 3, 1) != 0) return false; diff --git a/examples/CC3200/tmp006.h b/examples/CC3200/tmp006.h index 39f1d3b8..25336345 100644 --- a/examples/CC3200/tmp006.h +++ b/examples/CC3200/tmp006.h @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + #ifndef CS_MONGOOSE_EXAMPLES_CC3200_TMP006_H_ #define CS_MONGOOSE_EXAMPLES_CC3200_TMP006_H_ @@ -12,7 +17,8 @@ enum tmp006_conversion_rate { TMP006_CONV_1_4, }; -bool tmp006_set_config(uint8_t addr, enum tmp006_conversion_rate conv_rate, bool drdy_en); +bool tmp006_init(uint8_t addr, enum tmp006_conversion_rate conv_rate, + bool drdy_en); double tmp006_read_sensor_voltage(uint8_t addr); double tmp006_read_die_temp(uint8_t addr);