Ship IoT with Mediatek LinkIt One - Motorcycle Crash Alert
A Bike and an Idea
A very good friend of mine recently picked up a motorcycle as a first-time rider. It's a nice bike, a Honda CBR250 in gloss black, with under 3000 miles on it. She's smart, and took the safety class offered by the Motorcycle Safety Foundation, but I was still looking for ways to make sure she was going to be ok, and that I could quickly help if ever needed.
We started out by having her send me an SMS message whenever she arrived at her destination. "Made it," she would send, which worked ok. My inner sysadmin thought, "process seems repetitive; shouldn't this be automated?" And so was born this demo: a motorcycle crash alert that will both quickly transmit and permanently log a help message and GPS location if there is ever trouble!
Already having access to wot.io data service integrations like Twitter and Google Sheets, and the quick power of a bip.io workflow, I needed some hardware. Mediatek offers a dev board called the LinkIt ONE, available from seeedstudio.com. The LinkIt ONE integrates a heap of features for anyone making an Internet of Things prototype, including WiFi, Bluetooth LE, GPS, GSM, GPRS, audio, and SD card storage, all tied together by an ARM7 micro controller.
It's largely compatible with the Arduino pin headers, and can interface with Grove modules, SPI, I2C, and more. We'll be using their Arduino SDK and HDK to create our demo app, and hook this board up to the wot.io-powered bip.io data service exchange and do some really cool stuff with Twitter and Google Sheets. The only other bit I needed to add was an accelerometer, and I found a three-axis module in my parts bin.
Let's get started!
Prerequisites
- A Mediatek LinkIt ONE board
- Our example code: https://github.com/wotio/shipiot-mediatek-linkit-one
- A computer that can run the Arduino IDE
- Ardiuno version 1.5.7 Beta (Mediatek requirement, check their page for updated SDK compatibility info - and get the one with Java included if you are on a Mac)
- Read the Getting Started guide over at Mediatek to get oriented
- An accelerometer with analog outputs (or digital if you want to adapt it - see below)
- A (full-size) SIM card with a GSM data plan. You can get one from Ting, T-Mobile, and others, just make sure it's GSM!
Optional:
- One motorcycle with rider.
Note! The SDK does not currently work with the latest Arduino IDE (v1.6.5 as of this writing)
Also note! If you have multiple installs of Arduino IDE like I do, you can simply rename the app with the version number to help keep them straight.
Updating Firmware
This is part of the Getting Started guide from Mediatek, but it's important so I'm calling it out specifically here. Make sure you update the firmware on your board before you begin. It's easy!
Building the Hardware
The setup for this is simple. Connect the GPS and GSM antennas to the LinkIt ONE board. Make sure to use the right antennas, and hook them to the proper plugs on the bottom, like it shows in the picture. We don't need the WiFi antenna for this demo, so I left it disconnected.
Hook the Li-Ion battery to the board so we can run it without a power cable. (Make sure it's charged; check Mediatek's docs).
And finally, we connect the accelerometer board. I used a tiny breadboard to make this easy.
Oh, and make sure the tiny switch in the middle is flipped over to SPI, not SD, or the green LED won't behave and other things may not work for this demo. Check the close-up image below.
Here you can see the detail of how we hook up the accelerometer. There are three pins connected as configuration options, using jumpers on the breadboard. These are for the self-test (disabled), g-force sensitivity (high sensitivity), and sleep mode (disabled). (Obviously you'd want to control the sleep mode programmatically on an actual product for lower power consumption, but we're keeping it simple here.)
We then have some flying leads over to the LinkIt board, for ground, +3.3v power, and the three analog outputs for x, y, and z axes.
The accelerometer breakout board I used for this demo is the MMA7361, which has three analog outputs. The chip was discontinued by Freescale, and Sparkfun no longer sells the breakout board. They have a similar one you could use, the ADXL335, which should work great. You can adapt this demo for whatever kind of accelerometer you are using, maybe even change it to a digital interface, since the LinkIt ONE board speaks I2C and SPI with ease.
Here we can see exactly where the flying leads come in for power and to the three analog inputs, A0, A1, and A2.
And finally, we neatly package up the prototype so it is self-contained and will fit under the motorcycle's seat:
That's it! The LinkIt ONE board has all the rest of the fun stuff already integrated and ready to use. Combined with some data services available from wot.io, we'll be up and running in no time!
Writing the Code
Let's walk through the code for this demo. You can get the code from our github repo to follow along.
Headers and Configuration
First, we need to include some standard headers from the Mediatek SDK. These give us access to the GPS module, and the GSM/GPRS radio for cellular data communications.
We also set up some #define
statements to configure the GSM APN, the API hostname, and the Auth header.
#include <LGPS.h>
#include <LGPRS.h>
#include <LGPRSClient.h>
// Change these to match your own config
#define GPRS_APN "fast.t-mobile.com"
#define API_HOSTNAME "your_hostname_here.api.shipiot.net"
#define AUTH_HEADER "your_auth_header_here"
Now lets define some variables we'll use later. These will include the HTTP POST request template, and the json data structure template. We surround these by the F();
function, which in Arudino-speak, means "store this string in Flash, not RAM". This is good practice for long static strings to save you some RAM space, but not strictly required for this small example.
We also have some global variables for building the request, for the GPRS cellular data client session, for the GPS data, and a C-style string buffer for sending the request.
String request_template = F("POST /bip/http/mediatek HTTP/1.1\r\n"
"Host: " API_HOSTNAME "\r\n"
AUTH_HEADER "\r\n"
"Content-Type: application/json\r\n"
"Content-Length: ");
String data_template = F("{\"x\":X,\"y\":Y,\"z\":Z,\"nmea\":\"NMEA\"}");
String request;
String data;
String nmea;
LGPRSClient c;
gpsSentenceInfoStruct gpsDataStruct;
char request_buf[512];
Initializing the Board
Now on to the setup()
function. This is your standard Arduino init function that will do all the one-time stuff at boot. We start out by setting the serial debug console speed to 115200 baud, call pinMode()
to configure some digital output pins for the three LEDs on the board, and then set the LEDs to both red on, and the green one off.
The idea here is to use the LEDs as status info for the user. The first red LED turns off when the GPRS connects. The second one will start blinking while the GPS is acquiring a fix. And finally, the red LEDs will be off and the green LED will turn on to indicate that the system is read. We also blink off the green LED one time for every call to the HTTP endpoint, so the user knows it's working. (It would be good to check the return value from the API for this, but we don't do that in this simple demo)
void setup() {
Serial.begin(115200);
Serial.println("Starting up...");
pinMode(0,OUTPUT);
pinMode(1,OUTPUT);
pinMode(13,OUTPUT);
// Turn on red LEDs, turn off green
digitalWrite(0,LOW);
digitalWrite(1,LOW);
digitalWrite(13,LOW);
You'll see throughout the code that there are Serial.print()
calls, which report the status to the debug console. This is handy during development, but you'd probably remove these for a production system to save space and power.
For setting up the GSM data communications, we need to connect to the GPRS APN. The actual APN name you need to use is defined by your cellular carrier, and you set it in the `#define
statements at the top of the code.
Serial.print("Connecting to GPRS APN...");
while (!LGPRS.attachGPRS(GPRS_APN, NULL, NULL)) {
Serial.print(".");
delay(500);
}
Serial.println("Success");
Now we turn on the GPS chip, and delay for a second so it can get its bearings. (get it? get its bearings? ha.)
Serial.print("GPS Powering up...");
LGPS.powerOn();
delay(1000);
Serial.println("done");
So we've powered up the GPS and attached to the GPRS system, and we're going to call that Phase 1 complete. We turn off the first red LED, and move on to getting a GPS fix. That's the last bit of initialization to do, and we'll flip the LEDs to green when it's all done.
// Phase 1 init complete,
// so turn off first red LED
digitalWrite(0,HIGH);
waitForGPSFix();
// Phase 2 init complete, LEDs to green
digitalWrite(0,HIGH);
digitalWrite(1,HIGH);
digitalWrite(13,HIGH);
Serial.println("Setup done");
}
Getting a GPS Fix
Let's take a look at the waitForGPSFix()
function. It's simple, and simply checks the GPS data for indication of a good lock. We toggle the second red LED on every check to let the user know we're doing something and still waiting.
The GPS returns data formatted as NMEA sentences. The one we are interested in is the GPGGA sentence, which contains the location fix information. We are checking the char at offset 43 to see if it's a 1 - this magic number is from the GPS Quality Indicator field, and 0 means not locked, 1 means locked.
Later on when we're initialized, we simply pass along the raw NMEA data for processing by the bip.io data workflow; you'll see that later on.
void waitForGPSFix() {
byte i = 0;
Serial.print("Getting GPS fix...");
while (gpsDataStruct.GPGGA[43] != '1') { // 1 indicates a good fix
LGPS.getData( &gpsDataStruct );
delay(250);
// toggle the red LED during GPS init...
digitalWrite(1, i++ == 0 ? LOW : HIGH);
i = (i > 1 ? 0 : i);
}
Serial.println("GPS locked.");
}
Great! Now that we're all initialized, we'll have a look at the main loop()
function.
The Main Loop
First off in the main loop, we read the accelerometer, and we print the accelerometer outputs to the debug console. You'll need to check your outputs and see what z-axis threshold makes sense for your particular chip. (A more sophisticated system would have an auto-calibration routine, and probably use data from all three axes.)
void loop() {
Serial.println("Reading accelerometer...");
int accel_x = analogRead(A0);
int accel_y = analogRead(A1);
int accel_z = analogRead(A2);
Serial.print("x: "); Serial.print(accel_x);
Serial.print(" \ty: "); Serial.print(accel_y);
Serial.print(" \tz: "); Serial.println(accel_z);
Now we read the GPS data.
Serial.print("Reading GPS: ");
LGPS.getData( &gpsDataStruct );
Serial.print( (char *)gpsDataStruct.GPGGA );
We take the GPS data and the accelerometer data, and insert it into our json data template. Then, we insert the json data into the HTTP request template, and finally turn it into a plain old C string. There's some commented code that will print the fully-formatted HTTP request to your debug console, for help if things aren't working.
// Format the HTTP API request template
nmea = String( (char *)(gpsDataStruct.GPGGA) );
nmea.trim();
data = data_template;
data.replace("X", String(accel_x,DEC) );
data.replace("Y", String(accel_y,DEC) );
data.replace("Z", String(accel_z,DEC) );
data.replace("NMEA", nmea );
request = request_template + data.length() + "\r\n\r\n" + data;
request.toCharArray(request_buf,512);
// Uncomment this to print the full request to debug console
// Serial.print(request);
Sending the Telemetry
Now it's time to send the data to the bio.io HTTP endpoint. We connect to the API, and if successful, write the request buffer out. Then we blink the green LED off for 250ms to give the user some feedback.
Serial.print("Connecting to api... ");
c.connect(API_HOSTNAME, 80);
Serial.print("checking connection.. ");
if (c.connected()) {
Serial.print("Sending...");
c.write((uint8_t *)request_buf, strlen(request_buf));
}
Serial.print("waiting...");
digitalWrite(13,LOW);
delay(250);
digitalWrite(13,HIGH);
Finally, we optionally receive any data returned from the API and print it to debug, close the connection, and delay for our next cycle. (You could do some cool stuff in the data workflow, processing the telemetry and returning something useful, and this is where you'd check the response.) We're just delaying for a brief time, and not accounting for the time it takes to contact the API, because we don't need a precise cadence of telemetry transmission, just periodic checks.
Serial.print("receiving...");
// uncomment this to print the API response to debug console
/*
while (!c.available()) {
delay(1);
}
while (c.available()) {
char output = c.read();
Serial.print( output );
}
*/
Serial.println("Closing.");
c.stop();
delay(5000);
}
That's it! Now we'll take a look in the video below at how to create the bip.io workflow, and attach the telemetry output to more than one data service.
Creating the Workflow, and Testing
In this video, I will show you how to create a data workflow that accepts NMEA and accelerometer data into an HTTP endpoint, transforms the data, tests a condition, and if the condition passes, sends a fan-out of messages to both Twitter and Google Sheets endpoints.
And then, I'll show you a test of it all working!
Conclusion
So now we have a working crash alert prototype! I think I'll work on this some more and wire it into the bike's electrical system, so it automatically runs and always has power. I've already got some ideas for additional features and improvements, and I'm sure you've thought of some of your own.
There's a lot of great hardware out there these days, but we need to be able to use the data that all these IoT devices produce for us. Easy access to data services through a unified interface is a powerful productivity catalyst, and enables even more people to bring their ideas to life.
I knew that Twitter was reliable and worked well for sending me alerts, and Google Sheets makes a good data store for later processing. I didn't have to spend any time reading their API documentation or experimenting, because wot.io's data service integrations and the bip.io workflow made it all just work. Plus, if this turns into a product, I know the support is there to scale up massively. Even better, I can add features and intelligence to it by changing the server-side workflow without ever touching deployed hardware!
Grab yourself a LinkIt ONE board, and sign up for some data services, and you'll be well on your way to shipping your own Internet of Things device like this!
- Mediatek LinkIt ONE board
- Code: https://github.com/wotio/shipiot-mediatek-linkit-one
- Free data services for prototyping
Who would have thought a dev board and data service exchange could make an excellent bit of safety gear?