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:
> // 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
> }
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.
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.
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);
}
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 triedint16_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);
Great, Now it works. 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!