IMU to You! © MIT

Send Arduino101 IMU data over BLE to your mobile device with Evothings and smoothie charts!

  • 8,468 views
  • 39 comments
  • 29 respects

Components and supplies

Apps and online services

About this project

Introduction and Motivation:

There are many excellent tutorials on how to utilize the onboard Inertial Measurement Unit(IMU) of the arduino/genuino 101 in your projects. For example: (https://www.arduino.cc/en/Tutorial/Genuino101CurieIMUOrientationVisualiser), but information on how to transmit this data using the Arduino/Genuino 101’s onboard Bluetooth Low Energy(BLE) is lacking.

This tutorial project will demonstrate one way to transmit raw IMU data to your mobile device over BLE using Evothings Workbench(www.evothings.com). This tutorial will also show how to display IMU data graphically in real time using the smoothie.js library(http://smoothiecharts.org/).

What you will Need

Software

The code for this project is commented and I encourage you to read it. Instead of providing a line-by-line breakdown of the how all the code works, I will highlight the areas that I think are most important.

With a BLE connected Arduino/Genuino 101 project, it is important to understand the overall layout of the software required. In addition to the Arduino sketch residing on the board itself, you will have to write the software to run on the mobile device. To write the sketch, we use the familiar Arduino IDE and the libraries, CurieIMU and CurieBLE, needed for our project.

To develop our software for the mobile device, there are two broad categories of options:

  • Writing a native app directly for the operating system of the mobile device we wish to interact with.
  • Writing a web app utilizing the trio of JavaScript/HTML/CSS, which can run on essentially any platform.

In my initial efforts to work with BLE, I was confronted with the challenge of not only learning BLE but also having to learn about these options. This is where Evothings comes in. The Evothings platform is a complete "ecosystem." With EvothingsStudio we can write the mobile app component of our software using JavaScript/HTML/CSS. With the EvothngsViewer app running on our mobile device, we can access the device’s native features, such as BLE. The Evothings platform includes a library, appropriately named easyble.js, that has all the necessary functions to establish and communicate through a BLE connection to your Arduino/Genuino 101 project.

If you are concerned that not knowing HTML/CSS/JavaScript will hold you back, I can assure that prior to starting these projects and working with Evothings, I didn’t have any experience with web programming. I did have experience in C/Arduino C and Python, but not in  HTML/CSS/JavaScript. The evothings website contains an abundance of outstanding tutorials and articles (https://evothings.com/doc/tutorials/tutorials.html) to help you get started. If you have some basic programming knowledge and a willingness to read through and learn from some interesting code examples, then dive right in and utilize this remarkable tool!

The Arduino Sketch

My first principle of writing software for arduino projects is to build upon the work of others, and this project is no different. I used the accelerometer and gyroscope raw data access example sketches as the basis for my project’s sketch. These are found at the bottom of  the reference page for the CurieIMU library (http://www.arduino.cc/en/Reference/CurieIMU). I started with the accelerometer sketch and cut and pasted in the relevant gyroscope sketch elements into it afterwards.

Bluetooth Low Energy

When I first starting working with BLE, I was overwhelmed by all the new terminology. I  hoped it would be a simple as sending messages over the serial port. If you feel overwhelmed, don’t worry just start experimenting with it. The more you work with it, the more confident you will become in using BLE with your projects! There are many fine tutorials to help you get started. A couple of good places to start learning are the CurieBLE library reference and the Adafruit BLE tutorial :

http://www.arduino.cc/en/Reference/CurieBLE, https://learn.adafruit.com/introduction-to-bluetooth-low-energy/introduction.

The BLE standard specifies services, which are collections or sets of related data values. In the world of BLE, these values are called characteristics. Every service and characteristic is specified by a universally unique identifier(uuid). For officially accepted services these uuids are specified for you. Believe it or not, I was not able to find a standard service for inertial measurements! So for this application, I had to generate a custom uuid for my “imu service.” For custom services, you must create your own 128-bit custom uuid. This is very easy to do and there are many resources to generate these uuids. The one I use was an online uuid generator:

http://www.itu.int/en/ITU-T/asn1/Pages/UUID/uuids.aspx.

Once this is done, you can create your BLE service using the CurieBLE library:

BLEService imuService("917649A0-D98E-11E5-9EEC-0002A5D5C51B"); // Custom UUID

BLECharacteristic imuAccCharacteristic("917649A1-D98E-11E5-9EEC-0002A5D5C51B", BLERead | BLENotify, 12 );

BLECharacteristic imuGyroCharacteristic("917649A2-D98E-11E5-9EEC-0002A5D5C51B", BLERead | BLENotify, 12 );

BLEUnsignedCharCharacteristic appButtonCharacteristic("917649A7-D98E-11E5-9EEC-0002A5D5C51B", BLERead | BLEWrite );

Please read the BLE code examples on the Arduino/Genuino 101 site ( https://www.arduino.cc/en/Tutorial/Genuino101CurieBLEHeartRateMonitor) and the comments in this code to further understand how to set up your BLE service and characteristics.Sending floats over BLE:All BLE data is transmitted as bytes. So no matter whether your data originates as an integer, character, or float, it must be converted to bytes to transmit it over BLE. It is not necessary to transmit IMU data as floating point numbers, the CurieIMU interface lets you access raw IMU data in the form of integers.  However both example applications convert this raw data into physically meaningful numbers which are floating point numbers. These conversion is based on the equations recommended in the CurieIMU reference. For the accelerometer the following equation is recommend to convert raw accelerometer readings into mg:

     float g = (gRaw/32768.0)*getAccelerometerRange()

What are our options for dealing with this?

  • We can send the the raw data integers over BLE and implement the recommended equations on the mobile app side of our software. This shifts the issue to the mobile app.
  • We can convert the floating point numbers into to strings. Strings are simply char arrays, or another way of looking at them is arrays of bytes. Then we can convert or decode those bytes however we want on the mobile app side of things. This is the solution you will find implied by the tutorial/reference for the CurieBLE library  (http://www.arduino.cc/en/Reference/CurieBLE ).
  • We can use some C programming wizardry to convert our floating point numbers into their char array or byte equivalents and then send those over BLE to be “decoded” by the mobile app.

Option 1 is fine, but doesn’t teach us anything new. Also, when we eventually come to a having data only available as floating point numbers, we will be right back where we started. The Intel Curie module is pretty powerful, so why not use it the perform the computation and let the mobile apps take care of other chores anyway? Option 2 is a very commonly used and acceptable, but involves all the overhead of working with strings and string conversions.

Option 3 is the perhaps the most complicated, but also most efficient. We skip all the overhead of dealing with strings. In this option we will also learn how to work with the actual bytes that represent our data. We will gain a greater understanding how our computers represent and store numbers. We will also gain the confidence that comes with taking Arduino C to its core. Most importantly, this option also will gain us some serious “geek cred” and therefore being good makers and hackers, we will of course go for option #3!

Option #3:

All data types have an underlying representation in memory. Knowing how floating point numbers are stored and represented is key to our solution. Arduino C uses 32 bits or 4 bytes to represent and store a floating point number. Both the char and unsigned char data types use 4 bits or 1 byte to represent and store char data.

The unsigned char is the “currency” of data exchange in the CurieBLE library:

/**

* Set the current value of the Characteristic

* @param value New value to set, as a byte array. Data is stored in internal copy.

* @param length Length, in bytes, of valid data in the array to write.

* Must not exceed maxLength set for this characteristic.

* @return bool true set value success, false on error

*/

bool setValue(const unsigned char value[], unsigned short length);

As you can see, in order to change the value of our characteristic using the setValue() function, we need to pass our data as an array of char (an array of unsigned char is the same thing as byte array).

So the question now becomes, how can we pass off a floating number and as an array of unsigned char? We can do this by using a C language union. The union keyword allows us to have variables of different data types share the same space in memory. Since a float in C takes up 4 bytes, we would like to share 4 bytes of memory space with an array of 4 unsigned chars.

In a sense the compiler doesn’t really care how we interpret the bytes, it just reserves the space and allows us to work with this space as if it is of one of the data types in our union. We must, however, be very careful to ensure the we properly account for the size and type of variable when we work with it in our program.

Actually in our case, we will need to account for 3 floats each from the accelerometer and gyroscope. One float for each axis. We could consider a separate variable, union and BLE characteristic for each variable. However, knowing that each floating point number in 4 bytes long and that we can send a maximum of 20 bytes with each BLE transmission we could send the complete set of accelerometer and gyroscope data using 2 unions, 2 arrays of 3 floating point numbers and just 2 characteristics of 12 bytes each. This would not only make our code more readable and manageable but also more efficient.

For this we will use our union to share the space between an array of 3 floats, which requires 12 bytes and an array of 12 unsigned char. This is done as such:

union

{

     float a[3];

    unsigned char bytes[12];

} accData;

When the data is read from the IMU and then converted to a float, we then assign it the float elements of our union:

   CurieIMU.readAccelerometer(axRaw, ayRaw, azRaw);

   accData.a[0] = convertRawAcceleration(axRaw);

   accData.a[1] = convertRawAcceleration(ayRaw);

   accData.a[2] = convertRawAcceleration(azRaw);

Unfortunately we aren’t done just yet. If we try and pass the union to the setValue() function our compiler will complain and not let us proceed. One more step required to make this work. We have to make the compiler believe our union is really an array of unsigned characters by casting it as such. A cast is a bit of programming magic that allows us to transform one data type into another(https://www.arduino.cc/en/Reference/Cast). In some cases this can lead to a loss of data, but in our case, since we are just handling bytes, we won’t lose any information at all. In order to perform this cast however, we do need to use a bit of pointer magic:

   unsigned char *acc = (unsigned char *)&accData;

Let’s translate this:

First, &accData, gets the address of the memory location of our 12 bytes. Then, (unsigned char *)&accData tells the complier with that we consider this memory location to be treated an unsigned an array characters. Actually this is a pointer to the first byte of this memory location. Finally, unsigned char *acc assigns the variable acc to this pointer. That’s a lot to process, but read it over several times and it will make sense. If not, just use the pattern as is to send your data. The exact same pattern of commands is repeated for the gyroscope data. Now we can move on to the mobile app.

The JavaScript/HTML/CSS Mobile App:

It may seem like a chore to have to learn JavaScript/HTML/CSS in order to develop our mobile app. However the time and effort will be well worth it! It will open many opportunities for your projects and allow you to utilize many exciting libraries to program with. One such library, smoothie.js will be discussed here.In order to help you learn how to work with JavaScript/HTML/CSS, the Evothings website and Evothings workbench have an abundance of examples and tutorials to help you. For the purposes of this project I found that the easiest route was to modify the TI SensorTag CC2650 example. This example code follows a strongly functional structure, is easy to read and already has much of the functionality we need for this project. Each example has a directory tree of many files that may seem complicated at first, but for our purposes we need only modify 2 files, index.html and app.js, in the root directory of the example and add the smoothie.js library to our project directory. The project recipe below goes over these steps

Highlights of the Mobile App:

Nothing can substitute reading the code, but I will cover some of the highlights here. Remember that our IMU data will be sent over BLE as an array of unsigned chars or bytes. Luckily JavaScript supplies easy to use functions to translate these bytes back into any form of data. Here’s a code fragment that translates our accelerometer bytes back to floating point numbers in JavaScript:

   var ax = new DataView(data).getFloat32(0, true);

   var ay = new DataView(data).getFloat32(4, true);

   var az = new DataView(data).getFloat32(8, true);

The bytes are passed into our function with the data variable. This data is used to instantiate a DataView. Once we have this, we can call on one of the many methods of the DataView object to convert the data to our desired representation. In this case we want to convert it back into a float. Remember our floats created from the Arduino/Genuino 101 IMU were 4 bytes, or 32 bits long? The getFloat32() function is specifically written to deal with the 32 bit float representation. The parameters we send to the getFloat32() function are very important. The first is a number, this number which is 0, 4 or 8 in our case, represents the byte offset or index of the first byte of our 4 byte float. The second parameter, true, indicates that our float is in little endian format. That’s it, pretty simple. We can now assign all of the to a variable, ax, ay, or az to represent the accelerations along each axis.

Now wouldn’t it be slick to have a real-time, scrolling graphical representation of this data? Of course it would! In order to accomplish this, we will use the smoothie.js library. I believe this illustrates the power of using JavaScript/HTML/CSS for developing mobile apps for connected devices. There are free and easy to use libraries and frameworks available for almost any purpose you could think of.

The smoothie.js library is fully described at http://smoothiecharts.org/  website. In order to learn how to use the library, the authors have created a 10 minute tutorial( http://smoothiecharts.org/tutorial.html). After looking at the examples and reading their tutorial you should be able to easily use their code to create your own plot. The authors have also created a builder page(http://smoothiecharts.org/builder/), that allows you to graphically customize your plot. When you have the plot formatted to your wishes, you can cut and paste the code from text box at the bottom of the builder page directly into your app.

We will download the smoothie.js library from the smoothiecharts.org homepage into our mobile app directory to use it. Follow the instructions in the project recipe below in order to do this. The following line on our index.html allows us to use the the library in our app:

<script type="text/javascript" src="smoothie/smoothie.js"></script>

And that's about it for the teaching. Let's make the project!

Hardware:

Arduino/Genuino 101.

Because the Arduino/Genuino 101 comes with on board BLE and IMU capabilities, there is no circuit to make! Simply connect your to your computer via USB to upload the sketch and to access the serial monitor window of the Arduino Software (IDE) for debugging purposes.

Project Recipe:

I will assume that you have installed the Arduino IDE, board support for the intel curie and all the necessary libraries for it. If not, instructions are available here: https://www.arduino.cc/en/Guide/Arduino101.

1) Download and install the EvothingsStudio and EvothingsViewer ( http://evothings.com/download/). Follow the instructions here specific to your operating system and mobile platform.

2) Start up the Evothings Workbench and click on the Examples tab.


3) Make a copy of the TI SensorTag CC2650 Demo app and give it a unique name.

4) With file browser, go to the EvothingsStudio/MyApps directory and find the home directory for the project you renamed in step 3.

5) Select and rename the index.html file to index_old.html.

6) Select and rename the app.js file to app_old.js.

7) Now copy the index.html and app.js files from this project tutorial into the home directory of your new project.

8) Make a directory called called smoothie.

9) Go to smoothiecharts.org and click on the button labeled Download smoothie.js. The text of the smoothie.js library should be displayed in your browser.

10) Click on the file tab of your browser and select save page as. Save this page as smoothie.js in the smoothie directory created in step 8.

11) Connect your Arduino/Genuino 101 to your computer with a usb cable.

12) Using your Arduino IDE 1.6.8, upload the imu_ble.ino sketch to the Arduino/Genuino 101.

13) Open up the serial port connection. Data will not be sent from the arduino/genuino101 imu until a BLE connection is established, so you should not expect to see any serial data at this time.

14) Go back to the EvothingsWorkbench and obtain your connection key from the Connect tab.

15) Start up the EvothingsViewer on your mobile device and enter the connection key.

16) Click the start button on the mobile app homepage.

17) Once the connection is made, you should see the following message on your mobile device, Status: Data stream active - accelerometer. Values should be streaming on the Arduino IDE serial window, and the lines should scrolling on the Accelerometer and Gyroscope plots.

18) Shake, twist and turn that Arduino/Genuino101!

Conclusion:

There are many ways you could choose to collect, transmit and display IMU data but I believe the method presented here is one that teaches the most. There is really no substitute closely reading the code and experimenting with it. Your effort will be rewarded. As makers, the more we really understand how our projects work, the more we will be able to accomplish:

“The best dividends on the labor invested have invariably come from seeking more knowledge rather than more power.” Signed Wilbur and Orville Wright, March 12, 1906.”

     ― David McCullough, The Wright Brothers

Schematics

Arduino/Genuino101
This project utilizes the onboard IMU and BLE, so there is no circuit other than the board itself.
Genuino101fzz

Code

imuble01.inoArduino
This is the arduino sketch which will run on the arduino/genuino101.
/*
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

/*
   This sketch example demonstrates how to collect Inertial
   Measurement Unit(IMU) accelerometer and gyroscope data and then
   transmit that data using the on board Bluetooth Low Energ(BLE)
   capability of the Arduino/Genuino 101.

   This sketch is based on the IMU acceleromter and gyroscope examples
   sketches found at: https://www.arduino.cc/en/Reference/CurieIMU

   
*/

#include "CurieIMU.h"
#include <CurieBLE.h>


/**
* BLE Initialization Code
*
*/
BLEPeripheral blePeripheral;       // BLE Peripheral Device (the board you're programming)

/** 
 *  Inertial Measurement Unit(IMU) Service - somewhat surprisely I could not find a standard IMU
 *  service on the Bluetooth SIG website(https://www.bluetooth.com/develop-with-bluetooth). So I 
 *  used an online uuid generator(http://www.itu.int/en/ITU-T/asn1/Pages/UUID/uuids.aspx) to 
 *  create this uuid. You are welcome to use it or make your own. Keep in mind that custom service 
 *  uuid must always be specified in 128 bit format as seen below.
*/ 
BLEService imuService("917649A0-D98E-11E5-9EEC-0002A5D5C51B"); // Custom UUID

/** 
 *  Accelerometer & Gyroscope Characteristics - these are simple variations on the service uuid from 
 *  above. 
 *  
 *  A float in C takes up 4 bytes of space. We will send the x, y and z components of both the
 *  accelerometer and gyroscope values. The byte length of 12 is specified as the last value
 *  passed in the function here. Float data is stored in memory as little endian. There are many
 *  good tutorials on little and big endian, here is a link to one of them:
 *  (https://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Data/endian.html).
 *  BLE limits us to 20 bytes of data to transmit. Therefore we cannot send all 6 floats using one 
 *  characteristics. So we will create one characteristic for each main element of the IMU.
 *  
 *  The appButtonCharacteristic will allow us to recieve button inputs from the mobile app. Since
 *  a single byte can represent 256 values, we could in principle differentiate among a minimum
 *  of 256 buttons!
*/
BLECharacteristic imuAccCharacteristic("917649A1-D98E-11E5-9EEC-0002A5D5C51B", BLERead | BLENotify, 12 );
BLECharacteristic imuGyroCharacteristic("917649A2-D98E-11E5-9EEC-0002A5D5C51B", BLERead | BLENotify, 12 );
BLEUnsignedCharCharacteristic appButtonCharacteristic("917649A7-D98E-11E5-9EEC-0002A5D5C51B", BLERead | BLEWrite );

BLEDescriptor imuAccDescriptor("2902", "block");
BLEDescriptor imuGyroDescriptor("2902", "block");

/** 
 *  Define pins, connect your LEDs to the appropriate pins as defined below. These pins are 
 *  not required for the demo to function and therefore the circuit to connect them
 *  are not necessary.
*/
#define BLE_CONNECT 3 // This pin will service as a hardware confirmation of the BLE connection
#define INDICATOR_LEDA 4 // This pin will be used to debug input buttons from mobile app


/**
* The union directive allows 3 variables to share the same memory location. Please see the 
* tutorial covering this project for further discussion of the use of the union
* directive in C.
*
*/
 union 
 {
  float a[3];
  unsigned char bytes[12];      
 } accData;

 union 
 {
  float g[3];
  unsigned char bytes[12];         
 } gyroData;
 

void setup() {

  // initialze serial port for debugging communications
  Serial.begin(9600); // initialize Serial communication
  while (!Serial);    // wait for the serial port to open
 
  Serial.println("Arduino101/IntelCurie/Accelerometer/Evothings Example Started");
  Serial.println("Serial rate set to 9600");
  
  // initialize IMU
  // Serial.println("Initializing IMU device...");
  CurieIMU.begin();

  // Set the accelerometer range to 2G
  CurieIMU.setAccelerometerRange(2);

  // Set the accelerometer range to 250 degrees/second
  CurieIMU.setGyroRange(250);

  // prepare & initiazlie BLE
  // enable LED pins for output.
  pinMode(BLE_CONNECT, OUTPUT);
  pinMode(INDICATOR_LEDA, OUTPUT);

  blePeripheral.setLocalName("imu");
  blePeripheral.setAdvertisedServiceUuid(imuService.uuid());  // add the service UUID
  blePeripheral.addAttribute(imuService);   
  blePeripheral.addAttribute(imuAccCharacteristic);
  blePeripheral.addAttribute(imuAccDescriptor);
  blePeripheral.addAttribute(imuGyroCharacteristic);
  blePeripheral.addAttribute(imuGyroDescriptor);
  blePeripheral.addAttribute(appButtonCharacteristic);


  // All characteristics should be initialized to a starting value prior
  // using them.
  const unsigned char initializerAcc[12] = { 0,0,0,0,0,0,0,0,0,0,0,0 };
  const unsigned char initializerGyro[12] = { 0,0,0,0,0,0,0,0,0,0,0,0 };
 
  imuAccCharacteristic.setValue( initializerAcc, 12);
  imuGyroCharacteristic.setValue( initializerGyro, 12 );
  appButtonCharacteristic.setValue(0);
  
  blePeripheral.begin();
  
}

void loop() {
  
  int axRaw, ayRaw, azRaw;         // raw accelerometer values
  int gxRaw, gyRaw, gzRaw;         // raw gyro values
  

  // ?: Would there be an eficiency gained by declaring these outside the loop?

  // Here we connect oth central, your mobile device!
  BLECentral central = blePeripheral.central();
  if (central) {
    
    Serial.print("Connected to central: "); Serial.println(central.address());
    // It does not matter f you connect this LED or not, it is up to you.
    digitalWrite(BLE_CONNECT, HIGH);


    /**
     * All processing occurs in the context of an active BLE connection. 
    */
    while (central.connected()) {
      
    
      // read raw accelerometer measurements from device
      CurieIMU.readAccelerometer(axRaw, ayRaw, azRaw);

     /** 
      *  convert the raw accelerometer data to G's and assign them to the elements of
      *  the float array in the union representing the accelerometer data.
     */
     

      accData.a[0] = convertRawAcceleration(axRaw);
      accData.a[1] = convertRawAcceleration(ayRaw);
      accData.a[2] = convertRawAcceleration(azRaw);

      
      // read raw gyro measurements from device
     CurieIMU.readGyro(gxRaw, gyRaw, gzRaw);

     
    /** 
      *  convert the raw gyro data to degrees/second and assign them to the elements of
      *  the float array in the union representing the gyroscope data.
     */
     
     gyroData.g[0] = convertRawGyro(gxRaw);
     gyroData.g[1] = convertRawGyro(gyRaw);
     gyroData.g[2] = convertRawGyro(gzRaw);
     
     
     // These statements are for debugging puposes only and can be commented out to increae the efficiency of the sketch.
     Serial.print( "(ax,ay,az): " ); 
     Serial.print("("); Serial.print(accData.a[0]); Serial.print(","); Serial.print(accData.a[1]); Serial.print(","); Serial.print(accData.a[2]); Serial.print(")");Serial.println();
     Serial.print( "(gx,gy,gz): " ); 
     Serial.print("("); Serial.print(gyroData.g[0]); Serial.print(","); Serial.print(gyroData.g[1]); Serial.print(","); Serial.print(gyroData.g[2]); Serial.print(")");Serial.println();
     
     
      
     /**
      * The following two statements have the potential to cuase the most confusion. Please see the tutorial for
      * more on this.
      * What we are doing here is casting our union variables into a pointer of unsigned characters in
      * order to allow us to pass the array of bytes to the setValue() function.
     */
     unsigned char *acc = (unsigned char *)&accData;
     unsigned char *gyro = (unsigned char *)&gyroData;


      /**
       * Setting the values here will cause the notification mechanism on the moible app 
       * side to be enacted.
      */
     imuAccCharacteristic.setValue( acc, 12 );
     imuGyroCharacteristic.setValue( gyro, 12 );
      
  
     /**
      * When a button is pressed on the mobile app, the value of the characteristic is changed and
      * sent over BLE to the arduino/genuino101. A change in the characteristic is indicated
      * by a true value from the written() function and the value transmitted from the button 
      * on the mobile app is read here with the value() function.
      * The values change here essentially do nothing but print out a line to the serial port.
      * You can make them do anything here.
      * 
     */    
     if ( appButtonCharacteristic.written() ) {

        int appButtonValue = appButtonCharacteristic.value();

        switch(appButtonValue) {

          case 0:
            Serial.println( "App Input Value(0): " + appButtonValue );
            break;
          case 1:
            Serial.println( "App Input Value(1): " + appButtonValue );
            break;
          case 2:  
             Serial.println( "App Input Value(2): " + appButtonValue );
            break;  
        }
        
      }
      
      
    } // while central.connected  
  } // if central
} // end loop(){}


/**
 * The follwing functions are taken directly from the accelerometer
 * and gyroscope demo apps.
*/

float convertRawAcceleration(int aRaw) {
  // since we are using 2G range
  // -2g maps to a raw value of -32768
  // +2g maps to a raw value of 32767
  
  float a = (aRaw * 2.0) / 32768.0;

  return a;
}

float convertRawGyro(int gRaw) {
  // since we are using 250 degrees/seconds range
  // -250 maps to a raw value of -32768
  // +250 maps to a raw value of 32767
  
  float g = (gRaw * 250.0) / 32768.0;

  return g;
}
index.htmlHTML
This is the html code to be used with the mobile app running on Evothings
<!DOCTYPE html>
<!--
	Description:

	BLE example application that demonstrates how to read values
	IMU values from the Arduino/Genuino101.

	Requirements:

	See text in the app user interface.
 
    See http://smoothiecharts.org/ for more information about real time data streaming with
    the smoothie.js library.
 
-->
<html>

<head>

	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, user-scalable=no,
		shrink-to-fit=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0" />

	<title>Arduino/Genuino101 IMU</title>

	<style>
		@import 'ui/css/evothings-app.css';
	</style>

	<script>
	// Redirect console.log to Evothings Workbench.
	if (window.hyper && window.hyper.log) { console.log = hyper.log }
	</script>

	<script src="cordova.js"></script>
	<script src="libs/jquery/jquery.js"></script>
    <script type="text/javascript" src="smoothie/smoothie.js"></script>
	<script src="libs/evothings/evothings.js"></script>
	<script src="libs/evothings/ui/ui.js"></script>
	<script src="libs/evothings/easyble/easyble.js"></script>
	<script src="app.js"></script>

</head>

<body ontouchstart=""><!-- ontouchstart="" enables low-delay CSS transitions. -->

	<header>
		<button class="back" onclick="history.back()">
			<img src="ui/images/arrow-left.svg" />
		</button>

		<img class="logotype" src="ui/images/logo.svg" alt="Evothings" />

		<button id="menu-button" tabindex="0" onclick="this.focus()">
			<img src="ui/images/menu.svg" />
		</button>

		<menu>
			<menuitem>
				<a href="#">Demo</a>
			</menuitem>

			<menuitem>
				<a href="#information">Info</a>
			</menuitem>
		</menu>
	</header>

	<h1>Arduino/Genuino101 IMU Demo</h1>

	<p id="info">Not Connected</p>
    
    <table>
        <tr>
        <td><button onclick="app.onStartButton()" class="green wide big">START</button></td>
        <td><button onclick="app.onStopButton()" class="red wide big">STOP</button></td>
        </tr>
    </table>

    <table>
        <tr><td>Accelerometer</td></tr>
        <tr><td><canvas id="canvasAcc" width="500" height="150"></canvas></td></tr>
        <tr><td>Gyroscope</tr></td>
        <tr><td><canvas id="canvasGyro" width="500" height="150"</canvas></td></tr>
    </table>

	<section id='information'>
        <!-- keep in case I would like to use this section later -->
	</section>

</body>

</html>
app.jsJavaScript
This is the JavaScript that will provide the functionality of the moible app running on the Evothings Platform
// JavaScript code for the TI SensorTag Demo app.

/**
 * Object that holds application data and functions.
 */
var app = {};

/**
 * for input buttons/write characteristics
 */
app.device = null;

/**
 * Data that is plotted on the canvas.
 */
app.dataPoints = [];

/**
 * smoothie.js data
 *
 */

var lineAX = new TimeSeries();
var lineAY = new TimeSeries();
var lineAZ = new TimeSeries();

var lineGX = new TimeSeries();
var lineGY = new TimeSeries();
var lineGZ = new TimeSeries();

app.AX = -1.00;
app.AY = -1.00;
app.AZ = -1.00;

app.GX = 0.00;
app.GY = 0.00;
app.GZ = 0.00;

var smoothieAcc;
var smoothieGyro;

app.coin = 'monk';


/**
 * Timeout (ms) after which a message is shown if the SensorTag wasn't found.
 */
app.CONNECT_TIMEOUT = 3000;

/**
 * Object that holds SensorTag UUIDs.
 */
app.curie = {};

/** 
 * UUIDs for movement services and characteristics. These must match the
 * UUIDs specified in the arduino sketch.
 */
app.curie.IMU_SERVICE = '917649a0-d98e-11e5-9eec-0002a5d5c51b';

app.curie.IMU_ACC = '917649a1-d98e-11e5-9eec-0002a5d5c51b';
app.curie.IMU_AXDESCRIPTOR = '00002902-0000-1000-8000-00805f9b34fb';

app.curie.IMU_GYRO = '917649a2-d98e-11e5-9eec-0002a5d5c51b';
app.curie.IMU_GXDESCRIPTOR = '00002902-0000-1000-8000-00805f9b34fb';


app.curie.APP_INPUTCHARACTERISTIC = '917649a7-d98e-11e5-9eec-0002a5d5c51b';



/**
 * Initialise the application.
 */
app.initialize = function()
{
	document.addEventListener(
		'deviceready',
		function() { evothings.scriptsLoaded(app.onDeviceReady) },
		false);

	// Called when HTML page has been loaded.
	$(document).ready( function()
	{
		// Adjust canvas size when browser resizes
		$(window).resize(app.respondCanvas);

		// Adjust the canvas size when the document has loaded.
		app.respondCanvas();
                      
        // set up smoothie canvas
        app.setUpSmoothie();
                      
	});
};

/**
 * Adjust the canvas dimensions based on its container's dimensions.
 */
app.respondCanvas = function()
{
	var canvas = $('#canvas')
	var container = $(canvas).parent()
	canvas.attr('width', $(container).width() ) // Max width
	// Not used: canvas.attr('height', $(container).height() ) // Max height
};

/**
 * This function allows us to customize our smoothie charts. If you use the
 * builder at http://smoothiecharts.org/builder/ to customize your chart,
 * you can paste the JavaScript from the bottom of that page into the code
 * below.
 */


app.setUpSmoothie = function()
{

    smoothieAcc = new SmoothieChart(
    {
        maxValue:2.05,minValue:-2.05,
        grid: { strokeStyle:'rgb(125, 0, 0)', fillStyle:'rgb(60, 0, 0)',
        lineWidth: 1, millisPerLine: 250, verticalSections: 6, },
        labels: { fillStyle:'rgb(60, 0, 0)' }
    });
    
    smoothieGyro = new SmoothieChart(
    
    {
        maxValue:300.00,minValue:-300.00,
        grid: { strokeStyle:'rgb(125, 0, 0)', fillStyle:'rgb(60, 0, 0)',
        lineWidth: 1, millisPerLine: 250, verticalSections: 6, },
        labels: { fillStyle:'rgb(60, 0, 0)' }
    });
    
    smoothieAcc.streamTo(document.getElementById("canvasAcc"),1000);
    smoothieGyro.streamTo(document.getElementById("canvasGyro"),1000);
    
    setInterval( function()
    {
                
      lineAX.append( new Date().getTime(), app.getAx() );
      lineAY.append( new Date().getTime(), app.getAy() );
      lineAZ.append( new Date().getTime(), app.getAz() );
       
      lineGX.append( new Date().getTime(), app.getGx() );
      lineGY.append( new Date().getTime(), app.getGy() );
      lineGZ.append( new Date().getTime(), app.getGz() );
                
                
    }, 1000);
    
    smoothieAcc.addTimeSeries(lineAX,  { strokeStyle:'rgb(0, 255, 0)', lineWidth:3 });
    smoothieAcc.addTimeSeries(lineAY,  { strokeStyle:'rgb(255, 0, 0)', lineWidth:3 });
    smoothieAcc.addTimeSeries(lineAZ,  { strokeStyle:'rgb(0, 0, 255)', lineWidth:3 });
    smoothieGyro.addTimeSeries(lineGX,  { strokeStyle:'rgb(0, 255, 0)', lineWidth:3 });
    smoothieGyro.addTimeSeries(lineGY,  { strokeStyle:'rgb(255, 0, 0)', lineWidth:3 });
    smoothieGyro.addTimeSeries(lineGZ,  { strokeStyle:'rgb(0, 0, 255)', lineWidth:3 });
    
    
    
};

app.onDeviceReady = function()
{
	app.showInfo('Activate the SensorTag and tap Start.');
};

app.showInfo = function(info)
{
	document.getElementById('info').innerHTML = info;
};

app.onStartButton = function()
{
	app.onStopButton();
	app.startScan();
	app.showInfo('Status: Scanning...');
	app.startConnectTimer();
};

app.onStopButton = function()
{
	// Stop any ongoing scan and close devices.
	app.stopConnectTimer();
	evothings.easyble.stopScan();
	evothings.easyble.closeConnectedDevices();
	app.showInfo('Status: Stopped.');
};

app.startConnectTimer = function()
{
	// If connection is not made within the timeout
	// period, an error message is shown.
	app.connectTimer = setTimeout(
		function()
		{
			app.showInfo('Status: Scanning... ' +
				'Please press the activate button on the tag.');
		},
		app.CONNECT_TIMEOUT)
}

app.stopConnectTimer = function()
{
	clearTimeout(app.connectTimer);
}

app.startScan = function()
{
	evothings.easyble.startScan(
		function(device)
		{
			// Connect if we have found a sensor tag.
			if (app.deviceIsArduino101(device))
			{
				app.showInfo('Status: Device found: ' + device.name + '.');
                // set up the app.device variable to access device for writing input button results to Arduino/Genuino101
                app.device = device;
				evothings.easyble.stopScan();
				app.connectToDevice(device);
				app.stopConnectTimer();
			}
		},
		function(errorCode)
		{
			app.showInfo('Error: startScan: ' + errorCode + '.');
		});
};

app.deviceIsArduino101 = function(device)
{
	console.log('device name: ' + device.name);
	return (device != null) &&
		(device.name != null) &&
		(device.name.indexOf('imu') > -1 ||
			device.name.indexOf('imu') > -1);
};

/**
 * Read services for a device.
 */
app.connectToDevice = function(device)
{
	app.showInfo('Connecting...');
	device.connect(
		function(device)
		{
			app.showInfo('Status: Connected - reading services...');
			app.readServices(device);
		},
		function(errorCode)
		{
			app.showInfo('Error: Connection failed: ' + errorCode + '.');
			evothings.ble.reset();
			// This can cause an infinite loop...
			//app.connectToDevice(device);
		});
};

// Some getters and setters to properly work with imu data

app.setAx = function( value )
{
  app.Ax = value;
};

app.getAx = function()
{
   return app.Ax;
};

app.setAy = function( value )
{
    app.Ay = value;
};

app.getAy = function()
{
    return app.Ay;
};

app.setAz = function( value )
{
    app.Az = value;
};

app.getAz = function()
{
    return app.Az;
};

app.setGx = function( value )
{
    app.Gx = value;
};

app.getGx = function()
{
    return app.Gx;
};

app.setGy = function( value )
{
    app.Gy = value;
};

app.getGy = function()
{
    return app.Gy;
};

app.setGz = function( value )
{
    app.Gz = value;
};

app.getGz = function()
{
    return app.Gz;
};




app.readServices = function(device)
{
	device.readServices(
		[
		app.curie.IMU_SERVICE // Movement service UUID.
		],
		// Function that monitors accelerometer data.
		app.startIMUNotification,
		function(errorCode)
		{
			console.log('Error: Failed to read services: ' + errorCode + '.');
		});
};



/**
 * Read accelerometer data.
 */
app.startIMUNotification = function(device)
{
	app.showInfo('Status: Starting IMU notification...');

	

	// Set accelerometer notifications to ON.
	device.writeDescriptor(
		app.curie.IMU_ACC,
		app.curie.IMU_AXDESCRIPTOR, // Notification descriptor.
		new Uint8Array([1,0]),
		function()
		{
			console.log('Status: writeDescriptor ok.');
		},
		function(errorCode)
		{
			// This error will happen on iOS, since this descriptor is not
			// listed when requesting descriptors. On iOS you are not allowed
			// to use the configuration descriptor explicitly. It should be
			// safe to ignore this error.
			console.log('Error: writeDescriptor: ' + errorCode + '.');
		});
    
    
    

    // Set gyroscope notifications to ON.
    device.writeDescriptor(
        app.curie.IMU_GYRO,
        app.curie.IMU_GXDESCRIPTOR, // Notification descriptor.
        new Uint8Array([1,0]),
        function()
        {
            console.log('Status: writeDescriptor ok.');
        },
        function(errorCode)
        {
            // This error will happen on iOS, since this descriptor is not
            // listed when requesting descriptors. On iOS you are not allowed
            // to use the configuration descriptor explicitly. It should be
            // safe to ignore this error.
            console.log('Error: writeDescriptor: ' + errorCode + '.');
        });
    
    
	// Start accelerometer notifications.
	device.enableNotification(
		app.curie.IMU_ACC,
		function(data)
		{
			app.showInfo('Status: Data stream active - IMU');
			
            /**
             * The stream of bytes sent over BLE comes in here as the variable data
             * We create a DataView object and use the getFloat32() method to get
             * the floating point representation of our data here.
             */
            var ax = new DataView(data).getFloat32(0, true);
            var ay = new DataView(data).getFloat32(4, true);
            var az = new DataView(data).getFloat32(8, true);
            
            // debugging, can comment out
            console.log( '(ax,ay,az):' + '(' + ax + ',' + ay + ',' + az + ')' );
                              
            app.setAx(ax);
            app.setAy(ay);
	        app.setAz(az);

        },
		function(errorCode)
		{
			console.log('Error: enableNotification: ' + errorCode + '.');
		});
    
    
    // Start gyroscope notifications.
    device.enableNotification(
        app.curie.IMU_GYRO,
        function(data)
        {
            // See comments above.
            var gx = new DataView(data).getFloat32(0, true);
	        var gy = new DataView(data).getFloat32(4, true);
            var gz = new DataView(data).getFloat32(8, true);
           
 	   
            console.log( '(gx,gy,gz):' + '(' + gx + ',' + gy + ',' + gz + ')' );
                              
            app.setGx(gx);
            app.setGy(gy);
            app.setGz(gz);
        },
        function(errorCode)
        {
                console.log('Error: enableNotification: ' + errorCode + '.');
        });
};


// Initialize the app.
app.initialize();

Comments

Similar projects you might like

Pac-Man LED Pixel Panel Costume

Project tutorial by Ben Muller

  • 4,728 views
  • 3 comments
  • 84 respects

LoRa Gateway for DeviceHive

Project tutorial by DeviceHive IoT team

  • 1,281 views
  • 2 comments
  • 17 respects

IoT Bird Feeder with Sigfox and Tweeter

Project showcase by Gaël Porté

  • 370 views
  • 0 comments
  • 7 respects

SmartWay

Project tutorial by Universum

  • 248 views
  • 0 comments
  • 5 respects

Raspberry Pi and Arduino Laptop

Project tutorial by Dante Roumega

  • 17,750 views
  • 6 comments
  • 45 respects

Arduino-Based Automatic Guitar Tuner

Project tutorial by Ben Overzat

  • 3,486 views
  • 0 comments
  • 12 respects
Add projectSign up / Login