Accelerometer serial out precison

Hello all.
An Arduino newbie here.

Below is a simple sketch that reads the x,y,z values from an Analog Accelerometer. It is an ADXL335.

1. #include "ADXL335.h"
2. ADXL335 accelerometer;

3. void setup() {
4.   Serial.begin(9600);
5.   accelerometer.begin();
6. }

7. void loop() {
8.   int x, y, z;
9.   accelerometer.getXYZ(&x, &y, &z);

10.   Serial.print("X: ");
11.   Serial.print(x);
12.   Serial.print("\tY: ");
13.   Serial.print(y);
14.   Serial.print("\tZ: ");
15.   Serial.println(z);

16.   delay(500); // Adjust delay as needed
17. }

Serial Monitor output

X: 285 Y: 358 Z: 348
X: 285 Y: 358 Z: 348
X: 285 Y: 358 Z: 348

I need the x values to be atleast 3 places after the decimal.
So for example I need the X value to say 285.154 instead of just plain old 285.

Here is my .h file for the accelerometer.

 
1. #ifndef __ADXL335_H__
2. #define __ADXL335_H__

3. #include <Arduino.h>
4. /*macro definitions of Analog read pins*/
5. #define X_AXIS_PIN A0
6. #define Y_AXIS_PIN A1
7. #define Z_AXIS_PIN A2

8. #define ADC_AMPLITUDE 1024//amplitude of the 10bit-ADC of Arduino is 1024LSB
9. #define ADC_REF 5   //ADC reference is 5v
10. #define ZERO_X  1.22 //accleration of X-AXIS is 0g, the voltage of X-AXIS is 1.22v
11. #define ZERO_Y  1.22 //
12. #define ZERO_Z  1.25 //
13. #define SENSITIVITY 0.25//sensitivity of X/Y/Z axis is 0.25v/g

14. class ADXL335
15. {
16. private:
17.     void pinsInit();
18.     float scale;
19. public:
20.     void begin();
21.     void getXYZ(int16_t *x,int16_t *y,int16_t *z);
22.     void getAcceleration(float *ax,float *ay,float *az);
23. };

24. #endif

Below is the .cpp file of the accelerometer.

1. #include <Arduino.h>
2. #include "ADXL335.h"

3. void ADXL335::pinsInit()
4. {
5.     pinMode(X_AXIS_PIN, INPUT);
6.     pinMode(Y_AXIS_PIN, INPUT);
7.     pinMode(Z_AXIS_PIN, INPUT);
8. }
9. void ADXL335::begin()
10. {
11.     pinsInit();
12.     scale = (float)SENSITIVITY*ADC_AMPLITUDE/ADC_REF;
13. }
14. void ADXL335::getXYZ(int16_t *x,int16_t *y,int16_t *z)
15. {
16.     *x = analogRead(X_AXIS_PIN);
17.     *y= analogRead(Y_AXIS_PIN);
18.     *z = analogRead(Z_AXIS_PIN);
19. }
20. void ADXL335::getAcceleration(float *ax,float *ay,float *az)
21. {
22.     int x,y,z;
23.     float xvoltage,yvoltage,zvoltage;
24.     getXYZ(&x,&y,&z);
25.     xvoltage = (float)x*ADC_REF/ADC_AMPLITUDE;
26.     yvoltage = (float)y*ADC_REF/ADC_AMPLITUDE;
27.     zvoltage = (float)z*ADC_REF/ADC_AMPLITUDE;
28.     Serial.println("voltage:");
29.     Serial.println(xvoltage);
30.     Serial.println(yvoltage);
31.     Serial.println(zvoltage);
32.     *ax = (xvoltage - ZERO_X)/SENSITIVITY;
33.     *ay = (yvoltage - ZERO_Y)/SENSITIVITY;
34.     *az = (zvoltage - ZERO_Z)/SENSITIVITY;
35.     
36. }


Below is my version of the .cpp file. Yes, I want to go down into the underworld and change stuff such that my x value in the Serial monitor will be displayed to be atleast 3 places after the decimal.

I updated the lines 28 thru 32 from the original .cpp file.
The updates occur from line 12 thru 18 in my .cpp file below.

So for example instead of just plain old 285 being read from the Accelerometer, I THINK I will see something like 285.154 with 3 places after the decimal. ( Right now, the values change by 1 whole number increments or decrements.

Can someone please tell me if the following will give me x values in the Serial monitor that will be atleast 3 places after the decimal?


1. void ADXL335::getAcceleration(float *ax, float *ay, float *az)
2. {
3.     int x, y, z;
4.     float xvoltage, yvoltage, zvoltage;
5.     getXYZ(&x, &y, &z);
6.     
7.     // Calculate voltage for each axis
8.     xvoltage = (float)x * ADC_REF / ADC_AMPLITUDE;
9.     yvoltage = (float)y * ADC_REF / ADC_AMPLITUDE;
10.     zvoltage = (float)z * ADC_REF / ADC_AMPLITUDE;
11.     
12.     Serial.println("Voltage:");
13.     Serial.print("X: ");
14.     Serial.println(xvoltage, 4); // Print with 4 digits after the decimal point
15.     Serial.print("Y: ");
16.     Serial.println(yvoltage, 4); // Print with 4 digits after the decimal point
17.     Serial.print("Z: ");
18.     Serial.println(zvoltage, 4); // Print with 4 digits after the decimal point
19.     
20.     // Calculate acceleration for each axis
21.     *ax = (xvoltage - ZERO_X) / SENSITIVITY;
22.     *ay = (yvoltage - ZERO_Y) / SENSITIVITY;
23.     *az = (zvoltage - ZERO_Z) / SENSITIVITY;
24. }

Thank you all for your replies!!

How do you expect to get precision value when your getXY function returns 16-bit integer values? You are throwing the fractional part of the values away. Return floats, not integers.

Ok, here is my great idea. I will change the original .cpp file from line 14 till the end with the following piece of code.

void ADXL335::getXYZ(float *x, float *y, float *z)
{
    *x = analogRead(X_AXIS_PIN) * ADC_REF / ADC_AMPLITUDE;
    *y = analogRead(Y_AXIS_PIN) * ADC_REF / ADC_AMPLITUDE;
    *z = analogRead(Z_AXIS_PIN) * ADC_REF / ADC_AMPLITUDE;
}

void ADXL335::getAcceleration(float *ax, float *ay, float *az)
{
    float x, y, z;
    float xvoltage, yvoltage, zvoltage;
    getXYZ(&x, &y, &z);
    
    // Calculate voltage for each axis
    xvoltage = x;
    yvoltage = y;
    zvoltage = z;
    
    Serial.println("Voltage:");
    Serial.print("X: ");
    Serial.println(xvoltage, 4); // Print with 4 digits after the decimal point
    Serial.print("Y: ");
    Serial.println(yvoltage, 4); // Print with 4 digits after the decimal point
    Serial.print("Z: ");
    Serial.println(zvoltage, 4); // Print with 4 digits after the decimal point
    
    // Calculate acceleration for each axis
    *ax = (xvoltage - ZERO_X) / SENSITIVITY;
    *ay = (yvoltage - ZERO_Y) / SENSITIVITY;
    *az = (zvoltage - ZERO_Z) / SENSITIVITY;
}

You also need to change your loop so you are passing floats.

And why are you using pointers, instead of references? Either will work, but references are a much "cleaner" way to go, and FAR less prone to potentially disastrous errors.

Ty. I am using points cause that what the dude who wrote the original .cpp decided on using. I didnt wanna go ahead and change the .cpp file too much.

Since ADC_REF and ADC_AMPLITUDE are both constant integers, the preprocessor might optimize that math away before it gets to be multiplied by the (float)x Maybe add some parentheses or define ADC_REF as 5.0.

Nevermind. In testing it seems to work, and if it didn't it would have given you all zeros.

Ty all.

I made the changes as directed by others here. The output is satisfactory and the initial problem is solved.

The new problem is that the x,y, and z values are constant no matter how much I move the ADXL335 accelerometer around.

All I did was change the type of data in the .h and .cpp files which solved the initial problem but also created this new problem. ( The hardware is wired properly and works perfect for other sample ADXL335 sketches)

My output

X: 1.0000 Y: 1.0000 Z: 2.0000
X: 1.0000 Y: 1.0000 Z: 2.0000
X: 1.0000 Y: 1.0000 Z: 2.0000
X: 1.0000 Y: 1.0000 Z: 2.0000
X: 1.0000 Y: 1.0000 Z: 2.0000

Here are my updated sketch .h and .cpp files below; respectively.

The Arduino sketch below.

#include "ADXL335.h"
ADXL335 accelerometer;

void setup() {
  Serial.begin(9600);
  accelerometer.begin();
}

void loop() {
  float x, y, z;
  accelerometer.getXYZ(&x, &y, &z);

  Serial.print("X: ");
  Serial.print(x, 4);
  Serial.print("\tY: ");
  Serial.print(y, 4);
  Serial.print("\tZ: ");
  Serial.println(z, 4);

  delay(500);  // Adjust delay as needed
}

.h file below.

#ifndef __ADXL335_H__
#define __ADXL335_H__

#include <Arduino.h>
/*macro definitions of Analog read pins*/
#define X_AXIS_PIN A0
#define Y_AXIS_PIN A1
#define Z_AXIS_PIN A2

#define ADC_AMPLITUDE 1024//amplitude of the 10bit-ADC of Arduino is 1024LSB
#define ADC_REF 5   //ADC reference is 5v
#define ZERO_X  1.22 //accleration of X-AXIS is 0g, the voltage of X-AXIS is 1.22v
#define ZERO_Y  1.22 //
#define ZERO_Z  1.25 //
#define SENSITIVITY 0.25//sensitivity of X/Y/Z axis is 0.25v/g

class ADXL335
{
private:
    void pinsInit();
    float scale;
public:
    void begin();
    // void getXYZ(int16_t *x,int16_t *y,int16_t *z);
    void getXYZ(float *x,float *y,float *z);
    void getAcceleration(float *ax,float *ay,float *az);
};

#endif

The .cpp file below.

#include <Arduino.h>
#include "ADXL335.h"

void ADXL335::pinsInit()
{
    pinMode(X_AXIS_PIN, INPUT);
    pinMode(Y_AXIS_PIN, INPUT);
    pinMode(Z_AXIS_PIN, INPUT);
}

void ADXL335::begin()
{
    pinsInit();
    scale = (float)SENSITIVITY * ADC_AMPLITUDE / ADC_REF;
}

void ADXL335::getXYZ(float *x, float *y, float *z)
{
    *x = analogRead(X_AXIS_PIN) * ADC_REF / ADC_AMPLITUDE;
    *y = analogRead(Y_AXIS_PIN) * ADC_REF / ADC_AMPLITUDE;
    *z = analogRead(Z_AXIS_PIN) * ADC_REF / ADC_AMPLITUDE;
}

void ADXL335::getAcceleration(float *ax, float *ay, float *az)
{
    float x, y, z;
    float xvoltage, yvoltage, zvoltage;
    getXYZ(&x, &y, &z);
    
    xvoltage = x;
    yvoltage = y;
    zvoltage = z;
    
    Serial.println("Voltage:");
    Serial.print("X: ");
    Serial.println(xvoltage, 4); // Print with 4 digits after the decimal point
    Serial.print("Y: ");
    Serial.println(yvoltage, 4); // Print with 4 digits after the decimal point
    Serial.print("Z: ");
    Serial.println(zvoltage, 4); // Print with 4 digits after the decimal point
    
    *ax = (xvoltage - ZERO_X) / SENSITIVITY;
    *ay = (yvoltage - ZERO_Y) / SENSITIVITY;
    *az = (zvoltage - ZERO_Z) / SENSITIVITY;
}

Now it's all integer math, so it all truncates to integers & doesn't change much. Make the calculation happen in real numbers by changing to

 #define ADC_REF 5.0

Bingo. Your advice about #define ADC_REF 5.0 worked. Even the new problem is solved. Ty!

LOL.....This is not an issue but there is another newest issue.....the 'newest issue' is that the new output values are in the single digit range. Whereas the old values were in the hundreds range.

Must be some sort of scaling issue.

Lol. But I will figure it out. I don't want to bother you guys too much. Ty all for the suggestions/advice!

Old output

X: 285 Y: 358 Z: 348
X: 285 Y: 358 Z: 348
X: 285 Y: 358 Z: 348

New output

X: 1.3867 Y: 1.7432 Z: 1.6943
X: 1.3965 Y: 1.7432 Z: 1.6992
X: 1.3916 Y: 1.7432 Z: 1.6943
X: 1.3965 Y: 1.7432 Z: 1.6943

Edit:
More on the scaling factor that I pointed out to.
Let's take a look at the relevant part of the getAcceleration() function in the .cpp file:

void ADXL335::getAcceleration(float *ax, float *ay, float *az)
{
    int x, y, z;
    float xvoltage, yvoltage, zvoltage;
    getXYZ(&x, &y, &z);
    xvoltage = (float)x * ADC_REF / ADC_AMPLITUDE;
    yvoltage = (float)y * ADC_REF / ADC_AMPLITUDE;
    zvoltage = (float)z * ADC_REF / ADC_AMPLITUDE;

    *ax = (xvoltage - ZERO_X) / SENSITIVITY;
    *ay = (yvoltage - ZERO_Y) / SENSITIVITY;
    *az = (zvoltage - ZERO_Z) / SENSITIVITY;
}

From this piece of code above, we can infer the scaling factor used for converting voltage readings to acceleration values.

The scaling factor is the value of SENSITIVITY, which is defined in the .h file as #define SENSITIVITY 0.25.

Closing thoughts for other beginners....

Changing #define ADC_REF 5 to #define ADC_REF 5.0 has resolved the issue.

The reason for this change is related to the data types used in the calculations. When we use 5 without the decimal point, it's treated as an integer, and any division or multiplication involving it will also produce integer results, potentially leading to loss of precision.

By changing #define ADC_REF 5 to #define ADC_REF 5.0, we are explicitly specifying that ADC_REF should be treated as a floating-point number.

This ensures that all calculations involving ADC_REF will be performed using floating-point arithmetic, preserving the required precision in the calculations.