# Arduino 101 CurieIMU Orientation Visualiser

This tutorial demonstrates how to make use the 6-axis IMU to read the X, Y, and Z values of both the accelerometer and the gyroscope.

This tutorial demonstrates how to make use the Arduino 101's onboard 6-axis accelerometer/gyro to read the X, Y, and Z values of both the accelerometer and the gyroscope. While the gyroscope is able to determine the orientation of the board, the accelerometer measures the angular velocity of the board. Together, the accelerometer and the gyroscope form an Inertial Monitoring Unit (IMU) which can be used to precisely identify the orientation of the board. Madgwick's filter algorithm is used in this example to calculate quarternions from the 6 axes' values. The quarternions are then used to calculate Euler angles Pitch, Yaw, and Roll, which are received by Processing and used to control the rotation of an object around the X, Y and Z axes.

## Hardware Required

The CurieIMU library uses the IMU (accelerometer + gyroscope) built into the Arduino 101.

## Instructions

1. Set up the Arduino software (IDE) as described in Getting Started with Arduino 101.

2. Connect the 101 to your computer.

3. Launch the Arduino software (IDE) and select Arduino 101 from the Tools > Board menu.

4. Install the Madgwick library from library manager. To do this, open the Arduino Software (IDE), go to Sketch > Include Library > Manage Libraries. There you can search 'Madgwick' and install the library directly from there.

5. Download and Launch the Processing software and create a file with the Processing code shown below.

6. Change the Serial port to the one that your 101 is using (see "Processing Sketch" section).

7. Upload the example contained in the Madgwik library called Visualizer101 to your 101, making sure that the board is flat and stationery so it can perform the calibration accurately.

8. After a few seconds, run the Processing sketch, adjust the orientation of your board, and watch as the Processing sketch gives a visualization of your board. The Processing is contained in the "extras" folder of the Madgwick library.

## The Circuit

image developed using Fritzing.

## How it works

The Madgwick filter algorithm is open-source and is well documented in Madgwick's information and reports. The Madgwick filter algorithm was developed by Sebastian Madgwick during his Ph.D. in 2010 and is designed to be computationally inexpensive and efficient even at low sampling rates. The algorithm takes raw values from a gyroscope and accelerometer, and uses them to return four quaternions, which are 4-dimensional numbers which contain x, y, and z values to represent the axis around which rotation occurs, as well as a Ļ value which represents the value of rotation which occurs around the same axis. These quaternions can be used to calculate the Euler angles pitch, yaw, and roll; three angles used to describe the orientation of a rigid body in terms of x,y, and z as presented by Leonhard Euler in the 1700s. The equations (7) (8) (9) in Madgwick's Report are used to calculate the values for pitch, roll, and yaw, and their functions are included within the library.

We can create a 3D representation of the Arduino 101's onboard IMU in Processing, which will move as the board does. This is achieved with the values for Euler angles pitch, roll and yaw obtained by the Madgwick filter algorithm. These values can then be sent via Serial to Processing and used as angle arguments for Processing's to compute the position of the Arduino 3D model using the methods: applyMatrix(), pushMatrix(), and popMatrix() functions.

### Arduino Sketch

The sketch uses functions inside the CurieIMU library to get the data from the accelerometer/gyro.

In order to see a 3D representation in Processing, the Arduino sketch must incorporate two main functionalities; using the IMU data and algorithm to calculate yaw, pitch, and roll values, and enabling serial communication in a handshake fashion in order to send those values to Processing.

First, we must create a Madgwick object to access the functions from the Madgwick class in the library. Here, we call it filter:

``1Madgwick filter;``

In the

``setup()``
function we perform a preliminary configuration of the CurieIMU, by setting the sample rate of the accelerometer and the gyro and the filter to 25Hz:

``````1CurieIMU.begin();2
3CurieIMU.setGyroRate(25);4
5CurieIMU.setAccelerometerRate(25);6
7filter.begin(25);``````

Then we set the accelerometer range to 2g and the gyro range to 250 Ā°/s:

``````1CurieIMU.setAccelerometerRange(2);2
3CurieIMU.setGyroRange(250);``````

In the

``loop()``
function we will need to send a sample according to the sample rate we set for the CurieIMU reading:

``1microsPerReading = 1000000 / 25;``

We can then 'get' accelerometer and gyroscope data using the following functions from CurieIMU library and convert the raw data to acceleration (g) and angular velocity (Ā°/s):

``````1CurieIMU.readMotionSensor(aix, aiy, aiz, gix, giy, giz);2
3// convert from raw data to gravity and degrees/second units4
5ax = convertRawAcceleration(aix);6
7ay = convertRawAcceleration(aiy);8
9az = convertRawAcceleration(aiz);10
11gx = convertRawGyro(gix);12
13gy = convertRawGyro(giy);14
15gz = convertRawGyro(giz);``````

We can then use the function updateIMU() from the Madgwick library.

``1filter.updateIMU(gx, gy, gz, ax, ay, az);``

After that, we are ready to obtain from the filter the roll, pith and yaw values:

``````1roll = filter.getRoll();2
3pitch = filter.getPitch();4

These values are sent 25 times per second over the serial port to the Processing application.

As seen in the code, the gyroscope values have been scaled down by a variable factor so that they fit into a range which works well with the algorithm. Without this scaling, the values which are inputted to the function are too high and the visualization of the movement of the board becomes very sensitive to small changes of the 101's position, interpreting a slight change as a great change and causing the 'virtual' board to spin.

The full code can be found at the bottom of the page.

Note that the serial prints for gx,gy,gz,ax,az,ay are left in loop in comments for debugging and must be commented whilst communicating with Processing.

### Processing Sketch

If you haven't already, the first thing to do is to download the latest version of Processing from processing.org. Processing is a language similar to Arduino which allows the user to draw dynamic imagery in the familiar

``void setup()``
and
``void loop()``

The processing code receives incoming data from the serial port which is parsed and assigned to floats

``yaw``
,
``pitch``
, and
``roll``
, which are then used to compute the transformation matrix which moves the 3D model of the Arduino board.

To enable Processing to read from the same port that Arduino is sending to, myPort needs to be changed to your serial port's name. In setup(), this is the Second parameter of Serial.

``1myPort = new Serial(this, Serial.list()[0], 9600);``

The correct port can be found by using the list() function from the Serial class. The number inside the square brackets refers to the number of the serial port and will be 0, 1, 2, etc. The sketch works if you have just one COM port active on your machine. As an alternative, you could specify directly the "COMx" port of the 101 board - the one used to program from Arduino Software (IDE) the 101 board - commenting this line

``1myPort = new Serial(this, Serial.list()[0], 9600);``

and uncomment of the lines corresponding to your OS:

``````1myPort = new Serial(this, "COM5:", 9600);                    // Windows2
3myPort = new Serial(this, "/dev/ttyACM0", 9600);             // Linux4
5myPort = new Serial(this, "/dev/cu.usbmodem1217321", 9600);  // Mac``````

You must replace the port string with the correct name for your COM port.

If in doubt, you can print a list of your available serial ports in a separate sketch to determine this name.

The function serialEvent() is then used to receive and parse data.

``````1void serialEvent()2{3
4  int newLine = 13; // new line character in ASCII5
6  String message;7
8  do {9
12    if (message != null) {13
14      String[] list = split(trim(message), " ");15
16      if (list.length >= 4 && list[0].equals("Orientation:")) {17
18        yaw = float(list[1]); // convert to float yaw19
20        pitch = float(list[2]); // convert to float pitch21
22        roll = float(list[3]); // convert to float roll23
24      }25
26    }27
28  } while (message != null);29}``````

This reads from the serial port until ASCII character 13 (new line) and then uses the split() function to separate the values using the comma character. Since we know that we sent from Arduino in the order yaw, pitch, roll, we can then convert each string to a float and assign them to the first three values in String array list[]. The strings are then converted into floats and stored in float variables. The full Arduino and Processing sketches can be seen below.

## Code

### Arduino Code

``````1#include <CurieIMU.h>2#include <MadgwickAHRS.h>3
8void setup() {9
10  Serial.begin(9600);11
12  // start the IMU and filter13
14  CurieIMU.begin();15
16  CurieIMU.setGyroRate(25);17
18  CurieIMU.setAccelerometerRate(25);19
20  filter.begin(25);21
22  // Set the accelerometer range to 2G23
24  CurieIMU.setAccelerometerRange(2);25
26  // Set the gyroscope range to 250 degrees/second27
28  CurieIMU.setGyroRange(250);29
30  // initialize variables to pace updates to correct rate31
32  microsPerReading = 1000000 / 25;33
34  microsPrevious = micros();35}36
37void loop() {38
39  int aix, aiy, aiz;40
41  int gix, giy, giz;42
43  float ax, ay, az;44
45  float gx, gy, gz;46
49  unsigned long microsNow;50
51  // check if it's time to read data and update the filter52
53  microsNow = micros();54
55  if (microsNow - microsPrevious >= microsPerReading) {56
57    // read raw data from CurieIMU58
59    CurieIMU.readMotionSensor(aix, aiy, aiz, gix, giy, giz);60
61    // convert from raw data to gravity and degrees/second units62
63    ax = convertRawAcceleration(aix);64
65    ay = convertRawAcceleration(aiy);66
67    az = convertRawAcceleration(aiz);68
69    gx = convertRawGyro(gix);70
71    gy = convertRawGyro(giy);72
73    gz = convertRawGyro(giz);74
75    // update the filter, which computes orientation76
77    filter.updateIMU(gx, gy, gz, ax, ay, az);78
79    // print the heading, pitch and roll80
81    roll = filter.getRoll();82
83    pitch = filter.getPitch();84
87    Serial.print("Orientation: ");88
91    Serial.print(" ");92
93    Serial.print(pitch);94
95    Serial.print(" ");96
97    Serial.println(roll);98
99    // increment previous time, so we keep proper pace100
101    microsPrevious = microsPrevious + microsPerReading;102
103  }104}105
106float convertRawAcceleration(int aRaw) {107
108  // since we are using 2G range109
110  // -2g maps to a raw value of -32768111
112  // +2g maps to a raw value of 32767113
114
115
116  float a = (aRaw * 2.0) / 32768.0;117
118  return a;119}120
121float convertRawGyro(int gRaw) {122
123  // since we are using 250 degrees/seconds range124
125  // -250 maps to a raw value of -32768126
127  // +250 maps to a raw value of 32767128
129
130
131  float g = (gRaw * 250.0) / 32768.0;132
133  return g;134}``````

### Processing Code

``````1import processing.serial.*;2Serial myPort;3
4float yaw = 0.0;5float pitch = 0.0;6float roll = 0.0;7
8void setup()9{10
11  size(600, 500, P3D);12
13  // if you have only ONE serial port active14
15  myPort = new Serial(this, Serial.list()[0], 9600); // if you have only ONE serial port active16
17  // if you know the serial port name18
19  //myPort = new Serial(this, "COM5:", 9600);                    // Windows20
21  //myPort = new Serial(this, "/dev/ttyACM0", 9600);             // Linux22
23  //myPort = new Serial(this, "/dev/cu.usbmodem1217321", 9600);  // Mac24
25  textSize(16); // set text size26
27  textMode(SHAPE); // set text mode to shape28}29
30void draw()31{32
33  serialEvent();  // read and parse incoming serial message34
35  background(255); // set background to white36
37  lights();38
39  translate(width/2, height/2); // set position to centre40
41  pushMatrix(); // begin object42
55  applyMatrix( c2*c3, s1*s3+c1*c3*s2, c3*s1*s2-c1*s3, 0,56
57               -s2, c1*c2, c2*s1, 0,58
59               c2*s3, c1*s2*s3-c3*s1, c1*c3+s1*s2*s3, 0,60
61               0, 0, 0, 1);62
63  drawArduino();64
65  popMatrix(); // end of object66
67  // Print values to console68
69  print(roll);70
71  print("\t");72
73  print(pitch);74
75  print("\t");76
77  print(yaw);78
79  println();80}81
82void serialEvent()83{84
85  int newLine = 13; // new line character in ASCII86
87  String message;88
89  do {90
93    if (message != null) {94
95      String[] list = split(trim(message), " ");96
97      if (list.length >= 4 && list[0].equals("Orientation:")) {98
99        yaw = float(list[1]); // convert to float yaw100
101        pitch = float(list[2]); // convert to float pitch102
103        roll = float(list[3]); // convert to float roll104
105      }106
107    }108
109  } while (message != null);110}111
112void drawArduino()113{114
115  /* function contains shape(s) that are rotated with the IMU */116
117  stroke(0, 90, 90); // set outline colour to darker teal118
119  fill(0, 130, 130); // set fill colour to lighter teal120
121  box(300, 10, 200); // draw Arduino board base shape122
123  stroke(0); // set outline colour to black124
125  fill(80); // set fill colour to dark grey126
127  translate(60, -10, 90); // set position to edge of Arduino box128
129  box(170, 20, 10); // draw pin header as box130
131  translate(-20, 0, -180); // set position to other edge of Arduino box132
133  box(210, 20, 10); // draw other pin header as box134}``````