ADXL345 sensor issues with Photon

ADXL345 issues with Photon

I’m having problems taking readings from an ADXL345 sensor connected to my Photon board over I2C. I move the device around and it seems to be taking correct readings, but then one or more of the axis will spike up to 65000+ as shown from my serial analyser below.

I’ve tested the same sensor and code on my Arduino UNO for sanity and it the readings are fine, no huge jumps in numbers.
I’ve had a look around the forums here and here, I noticed that I should be using a pull-up resistor on SCL and SDA line, so I added a 4.7k on each and tied the CS line to 3.3v, but I still get the large numbers when I move the sensor. I believe the wiring is correct, although my hardware experience is limited but here’s a picture:

Here’s the code I’m using, it’s taken from here :

> // Cabling for i2c using Sparkfun breakout with a Spar Core
> // Spark Core <-> Breakout board
> // Gnd         -  GND
> // 3.3v        -  VCC
> // 3.3v        -  CS
> // Digital 0   -  SDA
> // Digital 1   -  SCL


> #define DEVICE (0x53) // Device address as specified in data sheet 

> byte _buff[6];

> char POWER_CTL = 0x2D;	//Power Control Register
> char DATA_FORMAT = 0x31;
> char DATAX0 = 0x32;	//X-Axis Data 0
> char DATAX1 = 0x33;	//X-Axis Data 1
> char DATAY0 = 0x34;	//Y-Axis Data 0
> char DATAY1 = 0x35;	//Y-Axis Data 1
> char DATAZ0 = 0x36;	//Z-Axis Data 0
> char DATAZ1 = 0x37;	//Z-Axis Data 1

> void setup()
> {
>   Wire.begin();        // join i2c bus (address optional for master)
>   Serial.begin(57000);  // start serial for output. Make sure you set your Serial Monitor to the same!
>   Serial.print("init");

>   //Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
>   writeTo(DATA_FORMAT, 0x01);
>   //Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
>   writeTo(POWER_CTL, 0x08);
> }

> void loop()
> {
>   readAccel(); // read the x/y/z tilt
>   delay(500); // only read every 0,5 seconds
> }

> void readAccel() {
>   uint8_t howManyBytesToRead = 6;
>   readFrom( DATAX0, howManyBytesToRead, _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
>   long x = (((int)_buff[1]) << 8) | _buff[0];   
>   long y = (((int)_buff[3]) << 8) | _buff[2];
>   long z = (((int)_buff[5]) << 8) | _buff[4];
>   Serial.print("x: ");
>   Serial.print( x );
>   Serial.print(" y: ");
>   Serial.print( y );
>   Serial.print(" z: ");
>   Serial.println( z );
> }

> void writeTo(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
> }

> // Reads num bytes starting from address register on device in to _buff array
> void readFrom(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
> }

Thanks in advance for any help :smile:

These huge jumps in numbers aren’t that big when you see the returned numbers as uint16_t (and this is what you do when feeding two byte into the low word of a long).
Then this 65517 isn’t anything else than -19

If you use int16_t instead of long for your target variables, you’ll see positive and negative numbers.

2 Likes

Fantastic, that resolved it. Huge thanks @ScruffR.

2 Likes

Friend, how do I solve the problem? I have the same problem

Friend, this was asked and answered in the OP. The problem was using a negative signed integer value with an unsigned integer variable. Convert your variables to signed integers and all should be well.

2 Likes

Hello! I using almost the same code to get roll & pitch. The code works fine with Arduino, but when i using it with Boron then things go wrong. For some reason “Roll” value cannot be negative and “Pitch” value is only negative or zero.

I have tried the tips of this chain, taking the variables x, y, z as int16_t, but with poor success.
I don’t understand what’s broken here. Maybe @ScruffR or someone else could help?

#include <Wire.h>

int ADXL345 = 0x53; // Device address
char DATAX0 = 0x32;  //X-Axis Data 0
char POWER_CTL = 0x2D; //Power Control Register
byte _buff[6];

void setup() {
  Serial.begin(9600);
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.print("init");

  Wire.beginTransmission(ADXL345); // start transmission to device 
  Wire.write(0x2D);             // send register address
  Wire.write(0x08);             // send value to write
  Wire.endTransmission();       // end transmission
}

void loop() {
  Wire.beginTransmission(ADXL345);  // start transmission to device 
  Wire.write(DATAX0);               // sends address to read from
  Wire.endTransmission();           // end transmission
  Wire.beginTransmission(ADXL345);  // start transmission to device
  Wire.requestFrom(ADXL345, 6);     // 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

  float x = ((_buff[1]) << 8) | _buff[0];
  x = x / 256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
  float y = ((_buff[3]) << 8) | _buff[2];
  y = y / 256;
  float z = ((_buff[5]) << 8) | _buff[4];
  z = z / 256;

  float roll = (atan2(y, z) * 57.3);
  float pitch = (atan2((- x), sqrt(y * y + z * z)) * 57.3);

  Serial.print(roll);
  Serial.print("/");
  Serial.println(pitch);
}

Have you checked the final state of i - it could be that you don’t receive all six bytes.

@ScruffR Yes, i have all six bytes, and it seems that every other byte is 0 or 255 and the rest is anything between 0 and 255.

That seems rather reasonable. When you have values in the range of -256..+255 the high byte of an int16_t would either be zero (for positive values) or 255 (for negative numbers) while the low byte holds the value.

When you said you tried int16_t it would have been useful to post that code to eliminate further distraction by using float - particularly for negative numbers your approach wouldn't work with float.

Here is an excerpt of the datasheet regarding the ADXL345 register 0x32..0x37


twos-complement is what's stored in a signed integer and since each value is made up by two bytes int16_t is the type to use.

BTW, instead of reading the data in a loop you could also use this

  int16_t x,y,z;

  int i = Wire.readBytes((char*)_buff, 6);  // <-- inherited from Stream class

  if (6 == i) {
    x = _buff[1];
    x <<= 8;
    x |= _buff[0];  

    y = _buff[3];
    y <<= 8;
    y |= _buff[2];  

    z = _buff[5];
    z <<= 8;
    z |= _buff[4];  
  }
  else
    Serial.printlnf("not all data received (%d)", i);
1 Like

Great, Now it works. :smile: Now I using int16_t instead of a float. What I did differently this time, I deleted the lines where I divided the xyz values by 256.
Tomorrow after work, I will read your message again and with care and I will try the script you give.
Thank you!