Memsic 2125 Dual Axis Accelerometer

I moved this post from the interface forum because I felt that it wasn’t so much about interfacing as a general application question

ok, I searched the forum several times and could’nt find much about the Memsic MXD2125 Accelerometer. I recently (xmass! woot!) received the Radio Shack Parallax dual axis accelerometer and have been able to interface it pretty well with the Arduino.

I have the arduino correctly sending back X and Y values from -1000 to 1000 (millig’s) using the pulseIn() function. I also did the trick for using the repetitive pulsIn() function to fix the erratic output when sending both x and Y data back.

I am a bit confused on the conversion scale, the code I found for the arduino and this accelerometer is:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1155139805/0

however that gives a multiplication by 18 as the conversion to milliseconds but doing that ends up giving very erroneous data values, on the order of 60,000 when I should be getting around 0 and 1000. I get correct results when I do not multiply by 18.

I am also confused on the arcsin and arccos functions. I have been reading the documentation on the parallax website for this accelerometer (www.parallax.com/rs) but can’t seem to pull out how to do it from there and comparing it with the code at the website link. I am trying to get an actual angle readout sent back to my computer.

Bryce
KB1LQC

The code I got to work… well kind of is here. It is more or less thrown together and very messy due to debugging (lots of commented out commands). When I used the atan2(); function with the xGForce and yGForce values I get correct readings if I hold the accelerometer so the z-axis is perpendicular to gravity. There is no z axis on the chip but that should give a good picture of how I have to hold it.

I then tried to use the asin(); function which should work better with the 1000 mG’s as the divisor but it ONLY outputs 0.00 or +/- 90.00 degrees… I know it should work but something is obviously being missed!

#include <math.h>

#define M_PI 3.141592653589793238462643



int xPin = 2;

int yPin = 3;

int xraw, xGForce, yraw, yGForce, xtime;

double angle;



void setup(){

  Serial.begin(9600);

  pinMode(xPin, INPUT);

  pinMode(yPin, INPUT);

}



void loop(){

xraw = pulseIn (xPin, HIGH);

xraw = pulseIn (xPin, HIGH);

yraw = pulseIn (yPin, HIGH);

yraw = pulseIn (yPin, HIGH);

xGForce = ((xraw/10) - 500) *8;

yGForce = ((yraw/10) - 500) *8;

Serial.print(xGForce);

Serial.print(" ");

Serial.print(yGForce);

Serial.print(" ");

angle = atan2 (xGForce , yGForce);

//angle = asin(xGForce/1000);

//printDouble(angle,100);

Serial.print(" ");

angle = angle * (360/(2*M_PI));

printDouble(angle, 100);



delay(100);

}

// printDouble found on arduino forums through Google search.

// code written by user mems.

void printDouble( double val, unsigned int precision){

  

  Serial.print (int(val));

  Serial.print (".");

  unsigned int frac;

  if(val>=0){

    frac = (val - int(val)) * precision;

  }

  else

  frac = (int(val) - val ) * precision;

int frac1 = frac;

while( frac1 /= 10 ){

  precision /= 10;

}

precision /= 10;

while( precision /= 10){

  Serial.print("0");

}

Serial.println(frac, DEC);

}

Bryce
KB1LQC

Make sure you’re forcing the division operation to use at least one floating-point argument, or the compiler will compute the INTEGER results of the division (usually ends up 0 or 1), then pass that to asin() as a floating-point number (0.0 or 1.0).

angle = asin(xGForce/1000[glow].0[/glow]);

or

angle = asin([glow]((float)[/glow]xGForce[glow])[/glow]/1000);

I love Google Calculator. http://www.google.com/search?q=arcsin+1+in+degrees

Thanks! I got it working for the most part by forcing the decimal place in the 1000.0! I totally forgot about doing that, did it a few months ago when playing around with the analog input and voltages. So here is the nice cleaned-up code:

#include <math.h>

int xPin = 2;
int yPin = 3;
int Xraw, Yraw;
double xGForce, yGForce, Xangle, Yangle;

void setup(){
   Serial.begin(9600);
   pinMode(yPin, INPUT);
   pinMode(xPin, INPUT);
   }

void loop(){
   
   // Due to erroneous output of pulseIn();
   // when x and y are calculated together
   // pulseIn(); must be used twice.

   Xraw = pulseIn (xPin, HIGH);
   Xraw = pulseIn (xPin, HIGH);
   Yraw = pulseIn (yPin, HIGH);
   Yraw = pulseIn (yPin, HIGH);

   // Calculate G-force in Milli-g's.

   xGForce = (( Xraw / 10 ) - 500) * 8;
   yGForce = (( Yraw / 10 ) - 500) * 8;

   // uncomment next four lines for milli-g output.
   
   //Serial.print(xGForce);
   //Serial.print(" ");
   //Serial.print(yGForce);
   //Serial.print(" ");

   // Calculate angle (radians) for both -x and -y axis.
   
   Xangle = asin ( xGForce / 1000.0 );
   Yangle = asin ( yGForce / 1000.0 );
   
   // Convert radians to degrees.

   Xangle = Xangle * (360 / (2*M_PI));
   Yangle = Yangle * (360 / (2*M_PI));

   // Print the angle with printDouble function

   printDouble (Xangle, 10);
   printDouble (Yangle, 10);

   // Delay for number of measurments per second needed.

   delay(100);

   }

void printDouble( double val, unsigned int precision){
   //code based off of arduino code found at:
   //

   Serial.print(int(val));
   Serial.print(".");
   unsigned int frac;
   if(val >= 0){
      frac = (val - int(val)) * precision;
   }
   else
      frac = (int(val) - val ) * precision;

   int frac1 = frac;
   while(frac1 /= 10){
      precision /= 10;
   }
   precision /= 10;
   while( precision /= 10){
      Serial.print("0");
   }
   Serial.println(frac, DEC);
}

It works very well until you get near 90 degrees. Signs of trouble start showing up around 75 degrees. When the axis is close to vertical then the output jumps from >75 degrees roughly to 90.0 degrees and to 0.0 degrees. I will post a picture of the Hyperterminal output soon. Any ideas on how to correct for that or is it inherently from the inaccuracy of the device about 50 degrees (MXD2125 data sheet).

Bryce
KB1LQC

It’s inherent in the nature of the trigonometry, coupled with any slight inaccuracy in the calibration.

Proper calibration requires you know exactly what values it would give under zero gees of acceleration-- hard to manage on Earth’s surface where the sum force of all three axes will be around 1.0 gees. A calibration routine could collect data at +1.0 gees and -1.0 gees, and assume a linear relationship. I have a routine in the works for this, but I doubt any game player would bother-- that’s why the Wii never bothers.

A workaround for the trigonometry would be to figure out that the pitch was more than 45 degrees, and then start computing the pitch using the inverse formula. I haven’t started that technique but it should work much smoother, say to 85 degrees. There is a limit, though. Even measuring three axes, you will run into “gimbaling,” where Euler angles cannot express a coherent position near the north and south poles of the rotation space.

Its not going to be used for gaming or control like that so im very interested in making the algorithm better! I take it that the Euler angles you are talking about can be compensated for by the Kalman filters with a gyroscope?

The idea of describing an object’s orientation in terms of “roll” and “pitch” (and “yaw”) are Euler angles (pronounced oil-er). They’re the angles of rotation away from an agreed zero, zero, zero orientation. However, when you get near pitch of 90, it gets harder and harder to distinguish what the actual roll is. The best analogy I can think of is to point out that it takes 1000 miles at the equator to go to the next time zone, but it only takes a footstep or two, when you’re near Santa’s house.

nice analogy haha that makes sense. I did know it was pronounced “oiler” my calc III professor last quarter made sure we knew how to pronounce it! I would still mess it up every now and then ::slight_smile: . So I take it that the method here can be refined a little but without other methods involved, it will not get too accurate above about 45 degrees.

Is there any chance any of you have interfaced with the t_out pin yet? Non-linear, calibrated pin is throwing me for a loop as to how to read the temperature with this chip. Any help?

OK so I found the application notes for the Memsic device! The application note for using thermal accelerometers can be found at:

http://www.memsic.com/data/pdfs/an-00mx-007.pdf

It shows the implementation used in the code I wrote and confirms that it should not be used for >60 degrees. The other way around that as explained in the PDF is to use both axis measuring ONE axis. Its a good read!

It also shows the linear method used by the Parallax BASIC Stamp when used with this accelerometer. Luckily the Arduino can use the math.h library ;D .

As for the TOUT, I think you may be able to find it in the other PDF’s located:

http://www.memsic.com/products/app_notes.htm

Bryce
KB1LQC

xGForce = (( Xraw / 10 ) - 500) * 8 A(g) = ((T1 / T2) - 0.5) / 12.5%
Can someone tell me why are these not the same?i got them from the app note, and im really having a hard time figuring out which one to use in my program… Any suggestions is highly appreciated :slight_smile: Thanks

oh btw, here is what i understand. Acceleration (in g) = (Duty cycle-Duty cycle@ 0g)/(Duty cycle per g)

=((T1/T)-50%)/12.5%
=(T1*F - 0.5 )*8

Where: T1=Duration, F=Frequency.