Ali Basmaci
Ali Basmaci

Reputation: 1

Arduino code not adding numbers properly

I am trying to create a function to find the average of sensor values on the Arduino in order to calibrate it, however the summation is not working properly and therefore the average is not correct. The table below shows a sample of the output. The left column should be the rolling sum of the output which is displayed in the right column (How are negatives getting in there?)

-10782      17112        
  6334      17116       
 23642      17308        
-24802      17092        
 -7706      17096        
  9326      17032        
 26422      17096        
-21986      17128  

The calibrateSensors() function, which is supposed to execute this is shown below

void calibrateSensors(int16_t * accelOffsets){

  int16_t rawAccel[3];
  int sampleSize = 2000;


  Serial.println("Accelerometer calibration in progress...");

  for (int i=0; i<sampleSize; i ++){

    readAccelData(rawAccel);        // get raw accelerometer data
    accelOffsets[0] += rawAccel[0]; // add x accelerometer values
    accelOffsets[1] += rawAccel[1]; // add y accelerometer values
    accelOffsets[2] += rawAccel[2]; // add z accelerometer values
    Serial.print(accelOffsets[2]);
    Serial.print("\t\t");
    Serial.print(rawAccel[2]);
    Serial.print("   \t FIRST I:");
    Serial.println(i);
  }

  for (int i=0; i<3; i++)
  {
    accelOffsets[i] = accelOffsets[i] / sampleSize;
    Serial.print("Second I:");
    Serial.println(i);
  }

  Serial.println("Accelerometer calibration complete");
  Serial.println("Accelerometer Offsets:");
  Serial.print("Offsets (x,y,z): ");
  Serial.print(accelOffsets[0]);
  Serial.print(", ");
  Serial.print(accelOffsets[1]);
  Serial.print(", ");
  Serial.println(accelOffsets[2]);
}

and the readAccelData() function is as follows

void readAccelData(int16_t * destination){
  // x/y/z accel register data stored here
  uint8_t rawData[6];
  // Read the six raw data registers into data array
  I2Cdev::readBytes(MPU6050_ADDRESS, ACCEL_XOUT_H, 6, &rawData[0]);  
  // Turn the MSB and LSB into a signed 16-bit value
  destination[0] = (int16_t)((rawData[0] << 8) | rawData[1]) ;  
  destination[1] = (int16_t)((rawData[2] << 8) | rawData[3]) ;  
  destination[2] = (int16_t)((rawData[4] << 8) | rawData[5]) ; 

Any idea where I am going wrong?

Upvotes: 0

Views: 1331

Answers (2)

Baldrickk
Baldrickk

Reputation: 4409

You have two problems:

  1. You do not initialise your arrays. They start with garbage data in them (space is allocated, but not cleared). You can initialise an array to be all zeros by doing:

int array[5] = {};

This will result in a array that initially looks like [0,0,0,0,0]

Your second problem is that you are creating an array of 16-bit signed integers. A 16-bit integer can store 65536 different values. Problem is that because you are using a signed type, there are only 32767 positive integer values that you can use. You are overflowing and getting negative numbers when you try and store a value larger than that.

I believe the arduino supports 32-bit ints. If you only want positive numbers, then use an unsigned type.

To use an explicit 32-bit integer:

#include <stdint.h>
int32_t my_int = 0;

Some info on standard variable sizes (note that they can be different sizes based on the arduino model the code is built for):

https://www.arduino.cc/en/Reference/Int

On the Arduino Uno (and other ATMega based boards) an int stores a 16-bit (2-byte) value. This yields a range of -32,768 to 32,767 (minimum value of -2^15 and a maximum value of (2^15) - 1). On the Arduino Due and SAMD based boards (like MKR1000 and Zero), an int stores a 32-bit (4-byte) value. This yields a range of -2,147,483,648 to 2,147,483,647 (minimum value of -2^31 and a maximum value of (2^31) - 1).

https://www.arduino.cc/en/Reference/UnsignedInt

On the Uno and other ATMEGA based boards, unsigned ints (unsigned integers) are the same as ints in that they store a 2 byte value. Instead of storing negative numbers however they only store positive values, yielding a useful range of 0 to 65,535 (2^16) - 1).

The Due stores a 4 byte (32-bit) value, ranging from 0 to 4,294,967,295 (2^32 - 1).

https://www.arduino.cc/en/Reference/UnsignedLong

Unsigned long variables are extended size variables for number storage, and store 32 bits (4 bytes). Unlike standard longs unsigned longs won't store negative numbers, making their range from 0 to 4,294,967,295 (2^32 - 1).

Upvotes: 1

user2371524
user2371524

Reputation:

With this code:

void calibrateSensors(int16_t * accelOffsets){
  int16_t rawAccel[3];
  //  [...]
  accelOffsets[0] += rawAccel[0];

There's an obvious problem: You are adding two 16bit signed integers here. A typical maximum value for a 16bit signed integer is 0x7fff (the first bit would be used as the sign bit), in decimal 32767.

Given your first two sample numbers, 17112 + 17116 is already 34228, so you're overflowing your integer type.

Overflowing a signed integer is undefined behavior in , because different implementations could use different representations for negative numbers. For a program with undefined behavior, you can't expect any particular result. A very likely result is that the value will "wrap around" into the negative range.

As you already use types from stdint.h, the solution is simple: Use uint32_t for your sums, this type has enough bits for values up to 4294967295.

As a general rule: If you never need a negative value, just stick to the unsigned type. I don't see a reason why you use int16_t here, just use uint16_t.

Upvotes: 0

Related Questions