Quantcast
Channel: Live Fast - Code Young
Viewing all articles
Browse latest Browse all 12

ADXL345 accelerometer breakout board + Arduino and Processing

$
0
0
Some time ago I've purchased an ADXL345 accelerometer breakout board from SparkFun.com. After some searching and datasheeting (that's an awful term, I know :D), I've finally came up with code, which will allow me to talk to the accelerometer using my Arduino and even pass the data on, to Processing (it's a fun language, covered in my previous post)



In this post I will cover the setup for the communication procedure between the ADXL345 and the Arduino and then, between the Arduino and Processing (ie your PC). All the sources are at the bottom of the post, as usual. (Code is now updated for Arduino 1.0.4)


To tell you the truth, I don't know which you should do first, code the Arduino or hook up the ADXL (probably code first), but my fingers were itchy, so I hooked up the ADXL to the Arduino first:

Connecting ADXL345 breakout board and Arduino for I2C communication

Here's how it actually looks in life (click the pics for larger image)

ADXL345 and Arduino

ADXL345 wiring on breadboard

Arduino wiring for ADXL345

Now, after we have everything plugged in nicely, we may start writing the code for getting the acceleration values from the sensor. You might want to have the ADXL345 Datasheet handy while you code.

There are two ways to talk to the sensor, SPI and I2C protocols. I've chose the I2C (the schematics above are for I2C communication) because it is easier to wire for, and easier to code - the downside, they say, is that I2C is slower.

The Arduino Wire library provides a sort-of-easy way to interact with components that implement the I2C communication protocol. I wish I could say it's usage is straightforward, but it is not quite so. There are few catches! Though, if you're aware of them, the library should be easy to use.

Open page 10 in the datasheet, this page explains how to talk to the device using I2C protocol. When using I2C protocol there are two ways (yes, again two ways) to wire the ADXL345, each way gives it a different device address (so that may prevent address collision). You can see, that the wiring I chose, defines the device address as 0x53, the datasheet also says that this translates to 0xA6 address for write and 0xA7 address for read (because the base device address is 7-bit + 1 read/write bit). Ignore the read and write addresses as the Wire library takes care of this for us (catch).
Now, at the bottom of page 10 in datasheet, you see a diagram of how reads and writes should be performed. We'll start with the writes because:
  1. It's what we do first in the code
  2. Surprisingly, it's easier to do
The diagram shows that in order to perform a write to a register on a device, we need to go through the following steps:
  1. Initiate transmission to device (using write address*)
  2. Write the address of the register we want to write
  3. Write the data we want to write to the register
  4. Finish transmission
* We only use the base address of the device (0x53) because the read/write bit is handled by the Wire library.
** You will also note that there are 'ack' signals coming from the device, those are too, handled by the library

Looks pretty logical, and here's a function that will write a value to a certain register on a specified device (you'll need to add #include <Wire.h> to the top of your program to use the Wire library):

void writeTo(int device, byte address, byte val) {
Wire.beginTransmission(device); //start transmission to device
Wire.write(address); // send register address
Wire.write(val); // send value to write
Wire.endTransmission(); //end transmission
}
So, let's say we want to write the value 8 to register 0x2D on device 0x53 (true story :D), we'll use this function like that:

writeTo(0x53, 0x2D, 8);

Ok, that was easy. The reading function is a bit longer, but uses similar concepts, so it's not going to be hard to follow. This time we want to perform a multiple-byte reading (as opposed to single byte writing we did in the writeTo() function). We need to read 6 bytes of data from the sensor (2 bytes for each axis), because, as the datasheet suggests, we don't want one axis value to change while we're reading another axis' values.

Lets look at that communication diagram on page 10 of the datasheet again. The steps we are to follow, in order to perform a multi-byte reading, are:
  1. Initiate transmission to device (using write address*)
  2. Write the address of the register we want to start** reading from
  3. Initiate transmission to device, again! (using read address*)
  4. Read bytes one after another
  5. Finish transmission

* Again, we only use the base address of the device (0x53) because the read/write bit is handled by the Wire library.
** When we're doing multiple bytes read (or write) we provide the start address - for example 0x15 - and the number of bytes we want to read - for example 4. That way we'll read values of registers 0x15, 0x16, 0x17, 0x18

So, let's see the read function:
void readFrom(int device, byte address, int num, byte buff[]) {
Wire.beginTransmission(device); //start transmission to device
Wire.write(address); //sends address to read from
Wire.endTransmission(); //end transmission

Wire.beginTransmission(device); //start transmission to device (initiate again)
Wire.requestFrom(device, num); // request 6 bytes from device

int i = 0;
while(Wire.available()) //device may send less than requested (abnormal)
{
buff[i] = Wire.read(); // receive a byte
i++;
}
Wire.endTransmission(); //end transmission
}
The new elements in this function are

  • Wire.requestFrom(device, num) - requests num number of bytes from device. The reading will start from the register passed in the Wire.write() call.
  • Wire.available() - returns true if there's something to read from the device
  • Wire.read() - reads one byte from the device
Essentially, this function performs the reading steps listed above. One thing to note, the result is saved in the buff array, and not returned by the function. So you must pass an array to the function and it has to be long enough to hold the data.

Whew! Ok, we have the read and write functions set. We're ready to write a program that will talk to the sensor (finally).

#include <Wire.h>
#define DEVICE (0x53) //ADXL345 device address
#define TO_READ (6) //num of bytes we are going to read each time (two bytes for each axis)

byte buff[TO_READ] ; //6 bytes buffer for saving data read from the device
char str[512]; //string buffer to transform data before sending it to the serial port

void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output

//Turning on the ADXL345
writeTo(DEVICE, 0x2D, 0);
writeTo(DEVICE, 0x2D, 16);
writeTo(DEVICE, 0x2D, 8);
}



Most of the code is pretty self-explanatory, so I'll explain only the code after the //Turning on the ADXL345 comment.
As you can see we are writing three different values to register 0x2D. 0x2D is the Power Control register of the ADXL345 (see datasheet). We first reset the power control register, then put the sensor in standby mode, and last we are putting it in to measure mode. We're doing the writes one after another because that's what the datasheet recommends. We could simply do the last write also. If you don't turn on the fourth bit on (writeTo(DEVICE, 0x2D, 8)) the sensor will be in sleep mode, and will give zero readings!!

And now, finally, the reading loop:

void loop()
{
int regAddress = 0x32; //first axis-acceleration-data register on the ADXL345
int x, y, z;

readFrom(DEVICE, regAddress, TO_READ, buff); //read the acceleration data from the ADXL345

//each axis reading comes in 10 bit resolution, ie 2 bytes. Least Significat Byte first!!
//thus we are converting both bytes in to one int
x = (((int)buff[1]) << 8) | buff[0];
y = (((int)buff[3])<< 8) | buff[2];
z = (((int)buff[5]) << 8) | buff[4];

//we send the x y z values as a string to the serial port
sprintf(str, "%d %d %d", x, y, z);
Serial.print(str);
Serial.write(10);

//It appears that delay is needed in order not to clog the port
delay(15);
}

Let's start from the beginning. The regAddress variable represents the register we want to start reading from (registers 0x32 - 0x37 are acceleration data registers, page 18 in the datasheet). We're defining the ints (x,y,z) which we'll use later, to send the data through serial port to Processing.
Next we're performing a readFrom function call, we pass our device's address, the register address to start reading from , the number of bytes to read and the buffer in which to save the data.
Now comes the tricky part - convert the data received from 2 bytes in to one int. Each axis' pair of bytes are manipulated, in order to create an int from two bytes using C bitwise operators (see here and here for more info on bitwise operators in C).
Afterward we're using the sprintf() function to turn the integers we have in to one string. And then we send that string to the serial port, terminating it with end-of-line character. The delay in the end is not strictly necessary, but it gave me better performance that way.

That's it! Below is a link to the sources - it also contains a matching Processing application to read the data and manipulate a model on the screen accordingly.

The next step is writing an actual 3D game for this thing, I'll probably do it using Microsoft's XNA studio, which appears to be pretty awesome. Stay tuned. Questions and comments are of course welcomed.

Source code files
Old sources (for before 1.0.4)

Viewing all articles
Browse latest Browse all 12

Trending Articles