Have you ever wondered how your smartphone knows which way is up? It’s one of the most innovative features of today’s smartphones. They all have a tiny device called an accelerometer built into the circuitry that detects when you tilt the device from side to side. That’s how your smartphone knows when to switch from portrait to landscape mode.
Accelerometers are widely used in low-power, low-cost motion and tilt sensing applications such as mobile devices, gaming systems, disk drive protection, image stabilization, and sports and health devices.
Let’s go over what they are, what they do, and how they work.
How Does an Accelerometer Work?
To understand how accelerometers work, imagine a ball inside a 3D cube.
Assuming that the cube is in outer space, where everything is weightless, the ball will simply float in the center of the cube.
Now assume that each wall represents a specific axis.
If we suddenly move the box to the left with acceleration 1g (a single G-force 1g is equivalent to gravitational acceleration 9.8 m/s2), the ball will undoubtedly hit the wall X. If we measure the force the ball exerts on wall X, we can obtain an output value of 1g along the X axis.
Let’s see what happens when we place that cube on Earth. The ball will simply fall on the wall Z, exerting a force of 1g as shown in the diagram below:
In this case, the box isn’t moving, but we still get a 1g reading on the Z axis. This is because gravity (which is actually a form of acceleration) is pulling the ball downward with a force of 1g.
While this model does not exactly represent how a real-world accelerometer sensor is built, it is often useful in understanding why an accelerometer’s output signal is typically specified in ±g, or why an accelerometer reads 1g in the z-axis at rest, or what accelerometer readings you can expect at different orientations.
In the real world, accelerometers are based on Micro-Electro-Mechanical Systems (MEMS fabrication technology). So, let’s find out how a MEMS accelerometer works.
How Does a MEMS Accelerometer Work?
A MEMS (Micro-Electro-Mechanical System) accelerometer is a micro-machined structure built on top of a silicon wafer.
This structure is suspended by polysilicon springs. It allows the structure to deflect when accelerated along the X, Y, and/or Z axes.
As a result of deflection, the capacitance between fixed plates and plates attached to the suspended structure changes. This change in capacitance is proportional to the acceleration along that axis.
The sensor processes this change in capacitance and converts it into an analog output voltage.
ADXL335 Module Hardware Overview
At the core of the module is a small, low-power, low-noise triple axis MEMS accelerometer from Analog Devices – ADXL335. It can measure not only static acceleration caused by gravity, but also dynamic acceleration caused by motion, shock, or vibration.
This breadboard-friendly module breaks out every pin of the ADXL335 to a 6-pin, 0.1′′ pitch header, including 3 analog outputs for X, Y, and Z axis measurements, 2 supply pins, and a self-test pin.
Power
The ADXL335 operates on 1.8V to 3.6VDC (typically 3.3V). However, the on-board 3.3V regulator makes it ideal for interfacing with 5V microcontrollers like the Arduino.
The sensor consumes only 350μA of current during normal operation.
Measurement Range
The ADXL335 has a full sensing range of ±3g. Meaning the maximum amount of acceleration that the ADXL335 can accurately measure and represent as an output is ±3g. If it is accelerated at 4g, for example, the accelerometer will not break, but the output may rail.
The absolute maximum acceleration of the ADXL335 is 10,000g. When subjected to accelerations greater than 10,000g, the ADXL335 may fail.
Ratiometric Output
The ADXL335 output is ratiometric, therefore, the output voltage increases linearly with acceleration over the range. This means that the 0g measurement output is always at half of the 3.3V supply voltage (1.65V), -3g is at 0v, and +3g is at 3.3V, with full scaling in between.
Technical Specifications
Here are the specifications.
Operating Voltage | 1.8V – 3.6V |
Operating Current | 350μA (typical) |
Sensing Range | ±3g (Full Scale) |
Temperature Range | −40 to +85°C |
Sensing axis | 3 axis |
Sensitivity | 270 to 330mV/g (Ratiometric) |
Shock Resistance | Up to 10,000g |
Dimension | 4mm x 4mm x 1.45mm |
For more information, please refer to the datasheet below.
ADXL335 Accelerometer Pinout
Before we get into the hookup and example code, let’s take a look at its Pinout.
VCC supplies power to the module. Connect it to the 5V output of your Arduino.
X-Out outputs an analog voltage proportional to acceleration along the X axis.
Y-Out outputs an analog voltage proportional to acceleration along the Y axis.
Z-Out outputs analog voltage proportional to acceleration along the Z axis.
GND is the ground pin.
ST(Self-Test) pin controls the self-test feature that allows you to test the sensor’s functionality in the final application. This feature is discussed in depth at the end of the tutorial.
Wiring an ADXL335 Accelerometer to an Arduino
Now that we know how the ADXL335 accelerometer works, we can move on to hooking it up to our Arduino.
Connections are pretty simple. Begin by mounting the accelerometer on the breadboard. Connect the VCC pin to the Arduino’s 5V pin and the GND pin to the Arduino’s Ground pin. Connect the X, Y, and Z outputs to Arduino’s analog pins A0, A1, and A2.
To get accurate results, we need to change the analog reference (AREF) voltage on the Arduino. This is accomplished by connecting the Arduino’s 3.3V pin to the AREF pin.
The following image shows the wiring.
Arduino Example Code – Reading the ADXL335 accelerometer
The sketch is quite simple. It simply displays the calibrated sensor output on each axis on the serial interface. Try out the sketch before we get into the details.
const int xInput = A0;
const int yInput = A1;
const int zInput = A2;
// initialize minimum and maximum Raw Ranges for each axis
int RawMin = 0;
int RawMax = 1023;
// Take multiple samples to reduce noise
const int sampleSize = 10;
void setup()
{
analogReference(EXTERNAL);
Serial.begin(9600);
}
void loop()
{
//Read raw values
int xRaw = ReadAxis(xInput);
int yRaw = ReadAxis(yInput);
int zRaw = ReadAxis(zInput);
// Convert raw values to 'milli-Gs"
long xScaled = map(xRaw, RawMin, RawMax, -3000, 3000);
long yScaled = map(yRaw, RawMin, RawMax, -3000, 3000);
long zScaled = map(zRaw, RawMin, RawMax, -3000, 3000);
// re-scale to fractional Gs
float xAccel = xScaled / 1000.0;
float yAccel = yScaled / 1000.0;
float zAccel = zScaled / 1000.0;
Serial.print("X, Y, Z :: ");
Serial.print(xRaw);
Serial.print(", ");
Serial.print(yRaw);
Serial.print(", ");
Serial.print(zRaw);
Serial.print(" :: ");
Serial.print(xAccel,0);
Serial.print("G, ");
Serial.print(yAccel,0);
Serial.print("G, ");
Serial.print(zAccel,0);
Serial.println("G");
delay(200);
}
// Take samples and return the average
int ReadAxis(int axisPin)
{
long reading = 0;
analogRead(axisPin);
delay(1);
for (int i = 0; i < sampleSize; i++)
{
reading += analogRead(axisPin);
}
return reading/sampleSize;
}
The images below show the accelerometer readings at various orientations.
Code Explanation:
The sketch begins by declaring Arduino’s analog input pins to which the sensor’s X, Y, and Z output pins are connected.
const int xInput = A0;
const int yInput = A1;
const int zInput = A2;
Next, two variables, RawMin
and RawMax
, are defined. Because the Arduino has a 10-bit ADC (210 = 1024), it will map the output voltages of the ADXL335, which range from 0 to 3.3 volts, into integer values between 0 and 1023. That is why RawMin
is set to 0 and RawMax
is set to 1023.
// initialize minimum and maximum Raw Ranges for each axis
int RawMin = 0;
int RawMax = 1023;
The sampleSize
variable specifies the number of samples that should be taken by the Arduino during each conversion. In our case, we set sampleSize
to 10 to achieve more accurate results.
// Take multiple samples to reduce noise
const int sampleSize = 10;
In the setup section, we first set the analog reference to EXTERNAL by calling analogReference(EXTERNAL)
. We then initiate serial communications with the PC.
Warning:
This is required since we have connected 3.3V to the AREF pin of the Arduino. If you fail to call analogReference(EXTERNAL)
, you will short together the internally generated active reference voltage and the AREF pin, potentially causing damage to your Arduino.
You can learn more about the analogReference()
function on Arduino’s official website.
analogReference(EXTERNAL);
Serial.begin(9600);
In the loop section, we read analog outputs from the sensor every 200ms. Note that we are calling the ReadAxis()
custom function instead of the analogRead()
function. This function simply takes ten ADC conversion samples and returns the average.
//Read raw values
int xRaw = ReadAxis(xInput);
int yRaw = ReadAxis(yInput);
int zRaw = ReadAxis(zInput);
Converting ADXL335 Output to Acceleration(g)
The following code snippet is the most important part of the program. It maps and converts the analog output voltages from the sensor to gravitational acceleration(G).
The mapping is handled by the IDE’s built-in map()
function. When we call map(xRaw, RawMin, RawMax, -3000, 3000)
, the value of RawMin
is mapped to -3000, the value of RawMax
is mapped to 3000, and values in-between are mapped to values in-between.
The numbers -3000 and 3000 are not random. They actually represent the gravitational acceleration measured by the sensor in milli-g i.e. ±3g (-3000 milli-g to 3000 milli-g).
For example,
- If the sensor outputs 0 volts on x-axis, that is xRaw=0, the
map()
function will return -3000, which corresponds to -3g. - If the sensor outputs 1.65 volts on x-axis, that is xRaw=511, the
map()
function will return 0, which corresponds to 0g. - If the sensor outputs 3.3 volts on x-axis, that is xRaw=1023, the
map()
function will return 3000, which corresponds to +3g.
The term Ratiometric makes more sense now that the output voltage increases linearly with acceleration across the range.
// Convert raw values to 'milli-Gs"
long xScaled = map(xRaw, RawMin, RawMax, -3000, 3000);
long yScaled = map(yRaw, RawMin, RawMax, -3000, 3000);
long zScaled = map(zRaw, RawMin, RawMax, -3000, 3000);
Finally, the sensor’s output is scaled down to fractional Gs by dividing it by 1000 and displayed on the serial monitor.
// re-scale to fractional Gs
float xAccel = xScaled / 1000.0;
float yAccel = yScaled / 1000.0;
float zAccel = zScaled / 1000.0;
Serial.print("X, Y, Z :: ");
Serial.print(xRaw);
Serial.print(", ");
Serial.print(yRaw);
Serial.print(", ");
Serial.print(zRaw);
Serial.print(" :: ");
Serial.print(xAccel,0);
Serial.print("G, ");
Serial.print(yAccel,0);
Serial.print("G, ");
Serial.print(zAccel,0);
Serial.println("G");
ADXL335 Self Test Feature
The ADXL335 Accelerometer has a self-test feature that allows you to test the sensor’s functionality in the final application. ST(self-test) pin on the module controls this feature.
When the ST pin is connected to 3.3V, an electrostatic force is exerted on the accelerometer beam internally. The resulting movement of the beam allows the user to determine whether or not the accelerometer is functional.
The expected change in output is
- −1.08 g (−325 mV) on the X-axis
- +1.08 g (+325 mV) on the Y-axis
- +1.83 g (+550 mV) on the Z-axis
The ST pin may be left open or connected to GND during normal operation.
Warning:
Exposing the ST pin to voltages higher than 3.6V may permanently damage the accelerometer.