Gyroscope L3GD20 erratic values reading

I am completely new on electronics. I bought an Arduino with a Gyroscope + accelerometer module. The gyroscope I am trying to use is the L3GD20 connected via I2C connection to my arduino.

The setup is as follows:

The code I have managed to build is the following:

#include <Wire.h>

int gyroscopeAddress = 0x69; // Device address

#define X_Axis_Register_L 0x28 // Register associated to the X_L axis (as stated on the datasheet)
#define controlRegister  0x20 // Register for determining the working mode
int16_t w_x_raw; // Stores the raw 16 bit signed data after joining both registers
int X_H; // High value from the register
int X_L; // Low value from the register
float w_x; // Final result after the transformation to degrees per second

void setup() {
  Wire.begin();
  Serial.begin(9600);
  // Enable measurement
  Wire.beginTransmission(gyroscopeAddress);
  Wire.write(controlRegister);
  Wire.write(15); // Sets normal mode on the chip (00001111) -> the 4th bit is for normal mode, the first 3 are for the X,Y and Z data measurement.
  Wire.endTransmission();
}

void loop() {
  Wire.beginTransmission(gyroscopeAddress);
  Wire.write(X_Axis_Register_L);
  Wire.endTransmission(false);

  Wire.requestFrom(gyroscopeAddress, 2, true); // Read two registers starting at X_L --> Reads X_L, X_H
  X_L = Wire.read();
  X_H = Wire.read();

  w_x_raw = (X_H | (X_L << 8)); // x angular speed value in raw

  w_x = w_x_raw * 0.00875; // Transformation to dps


  Serial.println(w_x);

  delay(250);
}

When I run this code, I get values that are all negative and far beyond the digital zero-rate level for the setting I am using (the default ones):

digital zero-rate level = +-10 dps
FS = +-250 dps
8.75 millidegrees per second / digit.

I attach now the values I get for the X axis rotation when the sensor is static (units degrees per second) :

   TIME             dps_x

13:29:33.883 -> -76.47
13:29:34.146 -> -110.20
13:29:34.366 -> -134.93
13:29:34.645 -> -47.23
13:29:34.879 -> -101.20
13:29:35.147 -> -116.94
13:29:35.412 -> -125.94
13:29:35.616 -> -87.71
13:29:35.885 -> -130.44
13:29:36.140 -> -110.20
13:29:36.393 -> -71.97
13:29:36.659 -> -44.98
13:29:36.893 -> -76.47
13:29:37.158 -> -159.67
13:29:37.410 -> -139.43
13:29:37.658 -> -33.74

That looks like normal sensor noise to me. If you read all three axes you can compare the noise levels, as each axis has an independent sensor.

For accurate readings you must calculate and subtract the offsets. Most people sum a couple of hundred readings with the sensor still, calculate the averages for each axis, and subtract those from subsequent readings.

This line looks backwards to me:

 w_x_raw = (X_H | (X_L << 8)); // x angular speed value in raw

Based on the name you chose for the X_H and X_L variables, I would have expected:

 w_x_raw = (X_L | (X_H << 8)); // x angular speed value in raw

I wouldn't expect noise in the range of 100.0 degrees per second when it is sitting still.

Normally I have had to send some register write requests for setting up my inertial sensors but maybe all of the defaults are just what you need for this one.

1 Like

Good catch. It is incorrect.

Hi, @joaquinglc
Welcome to the forum.

Thanks for using code tags.. :+1: :+1:

Can you please post some images of your project?
Can you please post a copy of your circuit, a CAD jpg EXPORT, or a picture of a hand drawn circuit in jpg, png?
Hand drawn and photographed is perfectly acceptable.
Please include ALL hardware, component names and pin labels.

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:

@jremington @ibuildrobots Thanks for your answer. I have changed the line that you pointed out and I still get quite erratic readings:

11:04:00.219 -> -87.71
11:04:00.475 -> -85.46
11:04:00.727 -> -103.45
11:04:00.981 -> -96.70
11:04:01.232 -> -87.71
11:04:01.482 -> -130.44
11:04:01.721 -> -112.45
11:04:01.962 -> -74.22
11:04:02.228 -> -107.95
11:04:02.481 -> -62.97
11:04:02.716 -> -24.75
11:04:02.954 -> -76.47
11:04:03.239 -> -128.19
11:04:03.493 -> -29.24
11:04:03.714 -> -76.47
11:04:04.000 -> -103.45
11:04:04.236 -> -63.01
11:04:04.501 -> -114.69
11:04:04.751 -> -49.48
11:04:04.970 -> -47.23
11:04:05.235 -> -94.46
11:04:05.505 -> -89.9

What register writes do you usually use? I just wanted to test that I am able to turn the sensor on and obtain usable values so I left everything on default. Once I try to do something specific with the data I would consider changing the settings.

Also, I have noticed that if I wiggle a little bit the cables while its reading, the values turn positive more often. Could it be a problem with the connections? (I am using a breadboard with some male-male jump cables).

UPDATE: I have implemented the computation of the mean value for the first 500 readings and the results I get are the following:

11:41:53.752 -> 22.13
11:41:54.017 -> -2.60
11:41:54.255 -> -56.57
11:41:54.519 -> -36.33
11:41:54.752 -> 33.38
11:41:54.990 -> 13.14
11:41:55.230 -> 31.13
11:41:55.513 -> -13.85
11:41:55.762 -> 44.62
11:41:55.998 -> -36.33
11:41:56.262 -> -4.85
11:41:56.517 -> -7.10

ok! I have edited the main post.

Please post the revised code, using code tags.

Please post the revised code, using code tags.

Also, I have noticed that if I wiggle a little bit the cables while its reading, the values turn positive more often. Could it be a problem with the connections? (I am using a breadboard with some male-male jump cables).

Yes, redo all the connections and test again, without wiggling while testing. Breadboards are very unreliable.

You should try the Adafruit library (example program) to see if the scaling is correct. GitHub - adafruit/Adafruit_L3GD20: Driver for Adafruit's L3GD20 I2C Gyroscope Breakout

Sorry, I misunderstood. Here is the code:

#include <Wire.h>

int gyroscopeAddress = 0x69; // Device address

#define X_Axis_Register_L 0x28 // Register associated to the X_L axis (as stated on the datasheet)
#define controlRegister  0x20 // Register for determining the working mode
int16_t w_x_raw; // Stores the raw 16 bit signed data after joining both registers
int X_H; // High value from the register
int X_L; // Low value from the register
float w_x; // Final result after the transformation to degrees per second
float calibration[250];
int calibration_flag = 0; // flag for deciding if calibration is necessary
int calibration_counter = 0; // counter for the calibration procedure
float offset = 0; // mean of the offsets vector


void setup() {
  Wire.begin();
  Serial.begin(9600);
  // Enable measurement
  Wire.beginTransmission(gyroscopeAddress);
  Wire.write(controlRegister);
  Wire.write(15); // Sets normal mode on the chip (00001111) -> the 4th bit is for normal mode, the first 3 are for the X,Y and Z data measurement.
  Wire.endTransmission();
}

void loop() {
  Wire.beginTransmission(gyroscopeAddress);
  Wire.write(X_Axis_Register_L);
  Wire.endTransmission(false);

  Wire.requestFrom(gyroscopeAddress, 2, true); // Read two registers starting at X_L --> Reads X_L, X_H
  X_L = Wire.read();
  X_H = Wire.read();

  w_x_raw = (X_L | (X_H << 8)); // x angular speed value in raw
  w_x = w_x_raw * 0.00875;

  if (calibration_flag == 1){    // If the calibration have been already done, plot the data.
    Serial.println(w_x - offset);  // Apply the average offset to the data
    delay(100);
  } else{			// If not: compute the average
    calibration[calibration_counter] = w_x;
    calibration_counter++;	 // Calibrate using the average of 500 values
    if(calibration_counter == 500){
      Serial.print("Done calibrating!");
      calibration_flag = 1;
      for(int n = 0; n<500; n++){         // computing the average offset
        offset = offset + calibration[n];
      }
      offset = offset / 500;   // Average offset = sum(w_x)/500
      Serial.print(" The offset is [dps]: ");
      Serial.println(offset);
      }
    delay(5);
  }

}

I will check the adafruit library and see if get anything useful. Regarding the connections, I only have jumping wires which have some room to wiggle when connected to the breadboard. I do not know how I would be able to solve that problem without soldering the cables directly to the gyroscope-accelerometer module.

This can't possibly work correctly, because you have allocated only 250 cells for the calibration array.

    calibration[calibration_counter] = w_x;
    calibration_counter++;	 // Calibrate using the average of 500 values
    if(calibration_counter == 500){

Soldering is the very best and most reliable way to make connections.

You must solder header pins to the sensor module, and use male-female jumpers to the breadboard (or if using Arduino Uno, to the female headers).

Here is a great tutorial and another one (of many on Adafruit) on how to do that.

My apologies. I copied the wrong code. This is the one computing the average. The results are listed below.

#include <Wire.h>

int gyroscopeAddress = 0x69; // Device address

#define X_Axis_Register_L 0x28 // Register associated to the X_L axis (as stated on the datasheet)
#define controlRegister  0x20 // Register for determining the working mode
int16_t w_x_raw; // Stores the raw 16 bit signed data after joining both registers
int X_H; // High value from the register
int X_L; // Low value from the register
float w_x; // Final result after the transformation to degrees per second
float calibration[300];
int calibration_flag = 0; // flag for deciding if calibration is necessary
int calibration_counter = 0; // counter for the calibration procedure
float offset = 0; // mean of the offsets vector


void setup() {
  Wire.begin();
  Serial.begin(9600);
  // Enable measurement
  Wire.beginTransmission(gyroscopeAddress);
  Wire.write(controlRegister);
  Wire.write(15); // Sets normal mode on the chip (00001111) -> the 4th bit is for normal mode, the first 3 are for the X,Y and Z data measurement.
  Wire.endTransmission();
}

void loop() {
  Wire.beginTransmission(gyroscopeAddress);
  Wire.write(X_Axis_Register_L);
  Wire.endTransmission(false);

  Wire.requestFrom(gyroscopeAddress, 2, true); // Read two registers starting at X_L --> Reads X_L, X_H
  X_L = Wire.read();
  X_H = Wire.read();

  w_x_raw = (X_L | (X_H << 8)); // x angular speed value in raw
  w_x = w_x_raw * 0.00875;

  if (calibration_flag == 1){    // If the calibration have been already done, plot the data.
    Serial.println(w_x - offset);  // Apply the average offset to the data
    delay(100);
  } else{			// If not: compute the average
    calibration[calibration_counter] = w_x;
    calibration_counter++;	 // Calibrate using the average of 500 values
    if(calibration_counter == 300){
      Serial.print("Done calibrating!");
      calibration_flag = 1;
      for(int n = 0; n<300; n++){         // computing the average offset
        offset = offset + calibration[n];
      }
      offset = offset / 300;   // Average offset = sum(w_x)/500
      Serial.print(" The offset is [dps]: ");
      Serial.println(offset);
      }
    delay(5);
  }

}

I think the serial plot is the best way to see how erratic the values are. Anyways

Additionally, the Adafruit library says "No L3GD20 device was found". When I check into the library code itself, the I2C address that the library (0x6B) has for my chip differs from that given back when I run an I2C scanner on my arduino (0x69). I am really confused at this point :frowning:

The Adafruit library allows you to specify the correct I2C address. It is really important that you get it to work and compare results.

It is also really important that you make good connections to the sensor module, which means soldering headers to the module.

Please post a link to the gyro module product page.

I have been investigating. The Adafruit library won't identify my L3GD20 because I do not know why the WHO_AM_I register gives back a -1 value (11111111), which does not corresponds to the one from the data sheet or the one in the library.

Here is the webpage: https://es.aliexpress.com/item/32860106876.html?spm=a2g0o.order_list.0.0.6f58194d2NjrWI&gatewayAdapt=glo2esp

That is almost undoubtedly a counterfeit, reject or recycled part, which is a common experience with cheap resellers on sites like eBay or Aliexpress. I suspect you are just wasting your time.

The seller identifies the module as "L3G4200D", which has long been obsolete, so why do you think it is an L3GD20? The WHO_AM_I value you get is wrong for both possibilities.

If you want a genuine L3GD20 sensor, buy from a reputable supplier with a return guarantee.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.