Thursday, June 20, 2013

Connecting two ADXL345s to one Arduino

I'm getting this question more frequently than others on my previous post about ADXL345 and Arduino, so I've decided to answer it with a post of itself. So, how do you connect and read from two ADXL345s with a single Arduino board? It's pretty straight forward if you already have one ADXL345 running. If you don't, head here, set everything up and get back here because I'll be assuming that you have read it and familiar with the concepts from the previous post.

A tiny bit of explanations first, but you can jump to the drawings if you're bored, and also grab the sources from the bottom of the post. So, what's special about connecting two ADXLs? The fact that the communication between Arduino and the ADXLs is carried out by the means of the I2C protocol and the Arduino Wire library which basically implements this protocol. The Wire library uses analog pins 4 and 5 (20 and 21 on Arduino Mega) as the data and clock pins respectively. Each I2C device - in our case both ADXLs - needs to be connected to both pins. Which means both devices have to be connected to Arduino's data and clock pins. How does the Arduino tell them apart though? Well, the I2C protocol has a property called device address, which lets the master device talk to a specific device that's connected to the data and clock pins by pointing out it's address.

The ADXL345 datasheet says on page 10 tells us that the ADXL may have two addresses, depending on how you wire it. These addresses are 0x53 and 0xD1. The addresses altering is made by changing the HIGH/LOW state of the ADXL SDO pin. In my previous ADXL post I've wired it so that it responds to address 0x53, by grounding the SDO pin (setting it to LOW):


But here we need to connect another ADXL to the same Arduino, so we need another address, that's why we're applying voltage to the SDO pin, setting it to HIGH:


So, now to make everything clear here's a scheme of both ADXL's connected to the same Arduino (click the image to enlarge):


Ok, with that done let's take a look at the code that makes this stuff work. I won't go in detail through basic ADXL345/Arduino interaction because I've done that here, I'll just point out the differences between connecting one and two ADXLs.

This is the write function (it will be used for both devices):

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
}

This is the read function (it will be used for both devices):

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
  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
}


Now the interesting parts.

#include <Wire.h>

#define DEVICE_A (0x1D)    //first ADXL345 device address
#define DEVICE_B (0x53)    //second 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 both ADXL345s
  writeTo(DEVICE_A, 0x2D, 24);   
  writeTo(DEVICE_B, 0x2D, 24);
}

What's interesting here? First of all we define two addresses, one for each device, and we send the ON command to each device separately (the writeTo() lines).

Now let's see the actual readings from the devices:


int regAddress = 0x32;      //first axis-acceleration-data register on the ADXL345
int xa = 0, ya = 0, za = 0;  
int xb = 0, yb = 0, zb = 0;
  
void loop()
{  
  readFrom(DEVICE_A, 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
  xa = (((int)buff[1]) << 8) | buff[0];   
  ya = (((int)buff[3])<< 8) | buff[2];
  za = (((int)buff[5]) << 8) | buff[4];
  
  readFrom(DEVICE_B, regAddress, TO_READ, buff); //read the acceleration data from the second ADXL345
  xb = (((int)buff[1]) << 8) | buff[0];   
  yb = (((int)buff[3])<< 8) | buff[2];
  zb = (((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 %d %d %d", xa, ya, za, xb, yb, zb);  
  Serial.print(str);
  Serial.write(10);
  
  //It appears that delay is needed in order not to clog the port
  delay(15);
}
Ok, first we have defined the variables needed for reading - the address that we want to read from (it's the same for both devices, because they're two instances of the same device). And then we define the vars we want to store that data in. Now, in the loop we perform a reading from DEVICE_A, convert the data that we got in the buff variables to the respective axes vars, and then do the same reading only from DEVICE_B, and again, store the data and voilà, we pack the data in to a nice string and send it away through the serial port!

Well, me thinks that that's it :) give me a shout if you have questions or find errors.

Thy sources

12 comments:

  1. With great pleasure I found your post, as I (am too old to become a coder I) was looking for such an solution!.

    Now I've run into some ?wiring? issue: when connecting the SDO to 3V3 (the right ADXL), all lEDs go out and the monitor doesn't give any new lines.

    Switching the ADXL's shows that both sensors are not broken.

    Do you have any suggestion?

    thx Cor

    ReplyDelete
    Replies
    1. Hi Cor :)

      If all LEDs go out it might mean that you are shorting the circuit when connecting the second SDO to 3v3 - maybe putting it in GND by mistake?

      Delete
  2. Hello Euristic,

    I also think that somewhere I must have misconnected a wire. As a test I made:
    the left ADXL SDO connect to 3V3
    the right ADXL SDO connect to GND.

    When doing so: All LED's go of and no readings at the serial monitor
    When disconnecting the left ADXL SDO from 3V3, then the lights go on again.

    As the "problem" shifted from right to left, I think that my wiring of both ADXL's is conform drawing, and are working individually. But not yet together...

    ReplyDelete
    Replies
    1. When you plug them individually they work? Are you using different breadboards for that, or simply cutting the power off for each one?

      Delete
  3. individually: ADXL left SDO GND : works
    individually: ADXL right SDO GND : works
    individually: ADXL left or right SDO 3V3 : LEDs out = doesn't work

    I use one breadboard, I have both ADXL's connected with ribbon cables

    If you like I can send you a few pictures of how I wired.
    you can send your email adres to cor@vanmarion.net

    thx

    Cor

    ReplyDelete
    Replies
    1. Cool, I'll contact you via email

      Delete
    2. did you figure it out? Ive got two working individualy, but not both at the same time :(

      Delete
  4. Any luck getting both to work? I'm having basically the same problem, when I connect both, nothing works, but individually they work.

    I have the 0x53 device's SDO floating. I plan to ground it to test if that's the issue. I also am not using the resisitors for sda and scl.

    ReplyDelete
    Replies
    1. @Jeff hi there :)
      Maybe you should wire the elements just as described on the scheme? I just did to re-test the whole thing and it works like a charm.

      Delete
    2. You sir are correct!

      adding the ground on 0x53 didn't matter, but the resistors are needed. Individually they aren't, but when you run both, you need the resistors.

      Thanks for posting this, the wiring diagram is helpful.

      I'm using the code from: https://github.com/jrowberg/i2cdevlib

      it's the only library I found that doesn't hard code the device address, and also has nice functions to handle all the config and interupts

      Delete
  5. please give me a solution to programm ADXL345 with arduino

    ReplyDelete
    Replies
    1. @Khawla do you need help with anything specific?

      You can also email me: livefast.codeyoung at gmail dot com

      Delete