Tuesday, 21 June 2016

Messing about with a MMA8452 accelerometer (and Arduino)

As much as we groan and complain about Arduino, a few of the nerds have been doing quite a bit of "real work" with Arduino and AVR chips; mainly because when you hand over a project (in return for money) the recipient likes to understand what's going on - and there are plenty more (non-industrial) developers who understand Arduino than PIC based languages. So that's the platform of choice.

For work.

We've been playing with the idea of using physical objects as controllers. So, for example, rotating a mug on a coaster as a crude volume control. Or selecting images from a TV listings guide to change channels.

All pretty pointless but - let's be honest - pretty fun, ideas to play about with. One device that gets called up a lot when playing with the objects-as-controllers-idea is the accelerometer. Having an object that you can rotate in 3d space is a pretty powerful controller concept, with loads of possibilities. But first, we had to understand what all the funny numbers meant.


Setting things up was pretty simple. The accelerometer uses I2C so we connected A4 to the SDA line and A5 to the SCL, stuck on a bit of power and read back the values:


#include <Wire.h> // Must include Wire library for I2C
#include <SparkFun_MMA8452Q.h>

MMA8452Q accel;

void setup() {
   // put your setup code here, to run once:

   Serial.begin(9600);
   Serial.println(F("MMA8452 Test"));
   accel.init();
   
}

void loop() {
   // put your main code here, to run repeatedly:
   if(accel.available()){

      accel.read();
      printAccels();
      printOrientation();
      delay(1000);
   }
}


void printAccels(){
   Serial.print(accel.cx,3);
   Serial.print("\t");
   Serial.print(accel.cy,3);
   Serial.print("\t");
   Serial.print(accel.cz,3);
   Serial.println("");
}

void printOrientation(){
   byte pl = accel.readPL();
   switch(pl){
       case PORTRAIT_U:
       Serial.println(F("portrait up")); break;

       case PORTRAIT_D:
       Serial.println(F("portrait down")); break;

       case LANDSCAPE_R:
       Serial.println(F("landscape right")); break;

       case LANDSCAPE_L:
       Serial.println(F("landscape left")); break;

       case LOCKOUT:
       Serial.println(F("flat")); break;
   }
}


Then we captured the raw data from the serial window.
Starting with the breadboard face up on the table, I picked it up and slowly rotated it, longways, until all the gubbins was on the underside. Then continued rotating, until it was the right way up again. The output from the serial window was dumped into a Google Docs spreadsheet




And converted into a graph/chart



Even before we started, we were expecting to see some kind of regular pattern between the values. It's only really once we saw the offset sine-waves that it became immediately obvious and apparent what was happening.

The axes on the accelerometer are all off-set by 90 degrees.
And in our graph, we can see two values consistently offset by 90 degrees.
So as the x-axis value increases, the z-axis value decreases. When x is at it's maximum value, z is zero. And all the while, the y value consistently remains at zero.

All we had to do was work out which "plane" in 3d space each axis related to.
Luckily, we've been doing some 3d layout work in Google Ketchup recently, and throwing objects around in Unity, so 90-degree-offset 3d axes are things we're quite comfortable with at the minute (maybe not so much in a few months time, when it's all be forgotten!)

If we imagine our y-axis as a "horizon" and consider that we were rotating our breadboard around this axis, this is pretty much what was going on:




And when you look at that diagram, you can immediately see that the x and z axis should, indeed
always be exactly 90 degrees apart. Which is exactly what we observed.

Now complex 3d maths, matrix transformations and sine/cosine calculations are quite a bit to ask of a lowly 8-bit microcontroller. They only just cope with floating point maths! But with this in mind, there's no reason why we couldn't create a series of look-up tables, to estimate the sine-wave curves of the axis and use these to estimate the rotation of our objects, in world space.

And if we can do that, we'd have some pretty nifty objects-as-real-world-controllers....