Advanced Accellerometer Guide: Converting Analog readings to G-Forces +

This guide was written for this deviceBy popular request our next project is to create a library that handles the MMA7361 3 Axis Accelerometers operation and translate the analog reading into something more “Physics Friendly”. There are several areas of concern when trying to create a library of this kind:

  • First there is the problem of variances from one device to the next that are a natural result of the manufacturing, and quality control processes used by the companies that design, manufacture, and distribute Accelerometer chips. The fact is that there will always be some variation from part to part.
  • Second there is the fact that to eliminate a very painfull and repetitive calibration process each time your program is run you either need to use EEPROM to store calibration data, manually calibrate and hardcode (only useful for small projects), or use a simple calibration routine that relies on close similarities between a single chip type. We will be going over the latter of the three, and possibly dabbling in the 2nd option.
  • The third problem is that Accelerometers usually have several pin configurable options that need to be kept in mind when we are collecting the data we need to create our first functional Analog to G converter.

This guide was written for the MMA7361, which can be purchased below:

The first thing you need to do before you can create a program is collect some data on the raw output of your Accelerometer. We ran this code to get our data:

//#################BEGIN CODE####################
int _sleepPin= 4;
int _GS= 5;
int _xpin= A0;
int _ypin = A1;
int _zpin = A2;

void setup()
{
Serial.begin(9600);

pinMode(_sleepPin,OUTPUT);//output mode
pinMode(_GS,OUTPUT);//output mode
pinMode(_xpin, INPUT);//input mode
pinMode(_ypin, INPUT);//input mode
pinMode(_zpin, INPUT);//input mode

digitalWrite(_GS,LOW);//sets GS mode
digitalWrite(_sleepPin, HIGH);//turns off sleep mode and activates device
digitalWrite(_xpin, HIGH);//turn on pull up resistor
digitalWrite(_ypin, HIGH);//turn on pull up resistor
digitalWrite(_zpin, HIGH);//turn on pull up resistor
}

void loop()
{
delay(2000); //Delay for readability
Serial.print(“X Reading: “);
Serial.println(analogRead(_xpin), DEC);
Serial.print(“Y Reading: “);
Serial.println(analogRead(_ypin), DEC);
Serial.print(“Z Reading: “);
Serial.println(analogRead(_zpin), DEC);
}

//######################END CODE########################

 

What you have to do is collect 9 values, what those values will correspond to are 1 G, -1G and 0G points. The way you can get these points is pretty easy for a -1G you simply hold the device with the axis facing upward against gravity, to collect the 1G point you simply turn the device upside-down, the 0G point is collected when the Axis is perpendicular to gravity (horizontal).

[stextbox id="warning"]NOTE: This calibration method only works on planet earth. If enough astronauts express an interest we may be able to write a moon library as well.[/stextbox]

The values we collected are as follows:

Z Axis:
-1G = 750
0G = 660
1G = 565
X Axis:
-1G = 754
0G = 660
1G = 567
Y Axis:
-1G = 770
0G = 677
1G = 588

From this data we can surmise the following:

  • 0G points can vary from part to part, so we need to be able to collect zero G points as part of our calibration process.
  • For the MMA7361 the 1G consistently relates to ~90 to 95 on a 5V 10 bit Analog to Digital Port.

Because we have to pick a 1G value we will need to dig a little deeper with the data we have. First lets look at the range ocuppied by the Z axis by taking the -1G value – the 1G value => 750-565= 185. We then divide that quantity by 2 to get the 1G equivalent, 185/2=92.5 points/G.

Doing the same process with the X Axis results in the following: 754-567=187 => 187/2 = 93.5 points/G

Doing the same process with the Y Axis results in the following: 770-588 = 182 => 182/2 = 91 points/G

As you can see though the 0G point does drift the actual Gravity Divisor remains fairly consistent from device to device. We can take advantage of this fact to greatly simplify our calibration process later. The average of all three gravity divisors ended up 92.333 we will go ahead and round that down to 92 in our program.

So before we begin the process of writing code to do this translation for us lets go ahead and test the way that our program should run by hand.

  • First we should determine our 0G points, we will do this by calling a calibrate function that requests the user hold the accelerometer right-side up on a flat surface those points will be collected and the G Divisor subtracted from the Z axis to get us our 0G points for all Axis.
  • Second we take our individual Axis readings, subtract them from our 0G points and divide by our Gravity Divisor.
    (0G – Reading)/(Gc) = Gravities

 

To accomplish these things we will have to write two functions, one to calibrate, and another to translate readings (You may notice a couple other changes to the code below that are meant improve functionality and help later when we turn this into a library). Once you have gravities it is as simple as multiplying that number by 9.8 m/s^2 to get your acceleration in m/s^2.

//#################BEGIN CODE###################

const static boolean GFORCE = true;
const static boolean METERS = false;

byte _sleepPin= 4;
byte _GS= 5;
byte _xpin= A0;
byte _ypin = A1;
byte _zpin = A2;

int _gc = 92;//gravity constant (inverted for proper sinage)

int _0z = 660; //defaults to read values (this is how you would hardcode a calibration)
int _0x = 660; //defaults to read values
int _0y = 677; //defaults to reasonable values
boolean _calibrated=false;

void setup()
{
Serial.begin(9600);
begin();//runs setup
calibrate();
}

void loop()
{
delay(2000); //Delay for readability
Serial.println(“In G-FORCES:”);
readGx();
readGy();
readGz();
Serial.println();
Serial.println(“In Meters Per Second-Squared:”);
readMx();
readMy();
readMz();
}

void begin()
{
pinMode(_sleepPin,OUTPUT);//output mode
pinMode(_GS,OUTPUT);//output mode
pinMode(_xpin, INPUT);//input mode
pinMode(_ypin, INPUT);//input mode
pinMode(_zpin, INPUT);//input mode

digitalWrite(_GS,LOW);//sets GS mode
digitalWrite(_sleepPin, HIGH);//turns off sleep mode and activates device
digitalWrite(_xpin, HIGH);//turn on pull up resistor
digitalWrite(_ypin, HIGH);//turn on pull up resistor
digitalWrite(_zpin, HIGH);//turn on pull up resistor
}

void calibrate()
{
Serial.println(“Please place device right-side up on a flat surface.”);
delay(2000);//give user two seconds to comply.
Serial.println(“Calibrating”);

_0z = readAverage(_zpin, 10) – _gc;//remove 1 G from the reading to get an “accurate” 0G
Serial.print(“Z Axis Calibrated:”); Serial.println(_0z);
_0x = readAverage(_xpin, 10);
Serial.print(“X Axis Calibrated:”); Serial.println(_0x);
_0y = readAverage(_ypin, 10);
Serial.print(“Y Axis Calibrated:”); Serial.println(_0y);

_calibrated = true;
}
int readAverage(int pin, int samples)
{
long readings = 0;

for(int i = 0; i < samples; i++)
{
readings += analogRead(pin);
}

return (readings/samples);
}

float convert(float myReading, float my0G)
{
return convert(myReading, my0G, GFORCE);//defaults to G-Forces
}

float convert(float myReading, float my0G, bool myUnits)
{
float myResult=((my0G-myReading)/(_gc));
if(myUnits)
return myResult; //G-Forces
else
return (myResult * 9.80665);
//converts to m/s^2 using conventional value for gravity.
//if you are on a different planet you may need to use a
//value for your conversions.
}

void readGx()
{
Serial.print(“X Reading: “);
Serial.println(convert(analogRead(_xpin), _0x), DEC);
}

void readGy()
{
Serial.print(“Y Reading: “);
Serial.println(convert(analogRead(_ypin), _0y), DEC);
}

void readGz()
{
Serial.print(“Z Reading: “);
Serial.println(convert(analogRead(_zpin), _0z), DEC);
}

void readMx()
{
Serial.print(“X Reading: “);
Serial.println(convert(analogRead(_xpin), _0x, METERS), DEC);
}

void readMy()
{
Serial.print(“Y Reading: “);
Serial.println(convert(analogRead(_ypin), _0y, METERS), DEC);
}

void readMz()
{
Serial.print(“Z Reading: “);
Serial.println(convert(analogRead(_zpin), _0z, METERS), DEC);
}

//######################END CODE########################

 

This code should accomplish everything an advanced user needs with the Accelerometer. I hope that this has helped to clear things up about the Accelerometer, and I look forward to releasing the first Auto-calibrating Accelerometer library shortly!

No comments yet.

Leave a Reply