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 0x1D. 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

31 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
  6. Sergey llunal@mail.ruApril 22, 2015 at 3:45 PM

    Hi! in this article you have connected 2 ADXL345, and how to connect 3 pieces?

    ReplyDelete
    Replies
    1. Hi Sergey,

      as there are only two possible addresses for the ADXLs, you can either reimplement the Wire library (probably a bad idea!) so it uses other in/out ports than Analog4/5. Or you could use another arduino to the readings of the 3rd/4th ADXL. There might be another way, but I can't really think of one.

      Delete
  7. Hey Buddy!

    The thing is i want the accelerometers to read different readings based on their relative motions in whatever directions. However this does not seem to be the case.

    What is happening is the values of xb,yb,zb are shown as values of xa,ya,za in the very next instance and it keeps on repeating. i want the vales of xa,ya,za to be completely different than xb,yb,zb and different in every instance.

    could you please help me out?

    Appreciate it. Thanks for the code and connections bud! Really helped

    ReplyDelete
    Replies
    1. Yo. Are yo usure you're using the right code? You can download the complete source code from here:
      http://www.cookietroll.com/blogfiles/adxl/two_adxls.ino

      Delete
    2. Well, same problem for me. I also do not read anything coherent from Z axis, just a 511 number.
      Thank in advance!

      Delete
  8. This comment has been removed by the author.

    ReplyDelete
  9. hello friend,can you help me to replace ADXL335 with ADXL345
    I need to replace this in my project
    what changes should i make to program

    contact me:karthik18.rk@gmail.com

    ReplyDelete
  10. Hello,
    Thanks for your post. It is really helpfull and works perfectly. However, I would like to understand what are the resistances used for?
    I can't find and understand why you are using them for.
    Thanks in advance

    ReplyDelete
    Replies
    1. Hi,

      the resistors are used because that's how the right way to connect the ADXL345 is described in the datasheet. So, wasn't my decision :)

      Delete
    2. Hi Euristic,

      Thanks for you quick answer! :-)

      Delete
  11. Hi Euristic,

    Can I request for a clearer picture of your wiring ?
    I desperately needs this for my final year project. I tried following your photo but it doesnt work.
    Here is my email: thisisyuketin@gmail.com

    ReplyDelete
  12. You wrote .."These addresses are 0x53 and 0xD1."

    It is 0x53 and 1D. Just a small typo - Please fix it.

    ReplyDelete
  13. Can you please provide the code for ADXL335? what is the device address or I2C address for ADXL335?.Please forward it to my mail.
    uttiugesh@gmail.com

    ReplyDelete
  14. Can we calculate the difference between the two angles received??

    ReplyDelete
    Replies
    1. Yes definitely, just subtract xa from xb, ya from yb etc and you'll get your difference.

      Delete
  15. Thank you for your response.
    I'm Under Construction a device
    To read the angles of the body joints
    Below is attached the address
    if possible, help me in doing
    https://deskgram.net/explore/tags/bikefit
    https://business.facebook.com/pg/STTSystems/posts/

    ReplyDelete
  16. Hi, How can I apply this to my 4 servos? I tried to copy this and made some additional codes for the 4 servos to function but the result was not good. Only one command works for both adxl345 and not the different command. Can you help?

    ReplyDelete
    Replies
    1. Depends on the servos. Not so sure you can use my code as is, probably needs tweaking and heavily depends on what's written in the servo's datasheet.

      Delete