Having Trouble using IMU sensor (ICM-20689) with Arduino I2C.

Hello, I am a graduate student working on a project,
and trying to make & test the IMU sensor (EV ICM-20689).

#include <Wire.h>

#define ADDRESS      0x68 // AD0 = 0 : 0x68, AD0 = 1 : 0x69

#define GYRO_CONFIG             0x1B
#define ACCEL_CONFIG            0x1C

#define ACCEL_XOUT_H            0x3B
#define ACCEL_YOUT_H            0x3D
#define ACCEL_ZOUT_H            0x3F
#define GYRO_XOUT_H             0x43
#define GYRO_YOUT_H             0x45
#define GYRO_ZOUT_H             0x47

#define PWR_MGMT_1              0x6B
#define PWR_MGMT_2              0x6C

byte rawAX[2], rawAY[2], rawAZ[2];
byte rawGX[2], rawGY[2], rawGZ[2];
float rawACCX, rawACCY, rawACCZ;
float rawGYROX, rawGYROY, rawGYROZ;

void setup() {
  Serial.begin(9600);
  Wire.begin();

  Serial.println("Connecting to ICM-20689...");
  
  // Power Management 1 setup
  Wire.beginTransmission(ADDRESS);
  Wire.write(PWR_MGMT_1);
  Wire.write(0x01);
  Wire.endTransmission();

  // Power Management 2 setup
  Wire.beginTransmission(ADDRESS);
  Wire.write(PWR_MGMT_2);
  Wire.write(0x00);         
  Wire.endTransmission();

  // Accelerometer Configuration
  Wire.beginTransmission(ADDRESS);
  Wire.write(ACCEL_CONFIG);
  Wire.write(0x00);         
  Wire.endTransmission();

  // Gyroscope Configuration
  Wire.beginTransmission(ADDRESS);
  Wire.write(GYRO_CONFIG);
  Wire.write(0x00);         
  Wire.endTransmission();
  
  Serial.println("  Acc_X           Acc_Y             Acc_Z                  Gyro_X           Gyro_Y           Gyro_Z");
}
void loop() {
  getACC();
  Serial.print(rawACCX/16384, DEC); Serial.print('\t');
  Serial.print(rawACCY/16384, DEC); Serial.print('\t');
  Serial.print(rawACCZ/16384, DEC); Serial.print("\t\t");

  getGYRO();
  Serial.print(rawGYROX/131, DEC); Serial.print('\t');
  Serial.print(rawGYROY/131, DEC); Serial.print('\t');
  Serial.print(rawGYROZ/131, DEC);

 
  Serial.println();
  delay(100);
}

void getACC(){
  Wire.beginTransmission(ADDRESS);
  Wire.write(ACCEL_XOUT_H);
  Wire.requestFrom(ADDRESS,2,1);

  for(int i = 0; i<2 ; i++)
  {
    rawAX[i] = Wire.read();
  }

  // High + Low Byte 
  rawACCX = rawAX[1] | rawAX[0] <<8;
  Wire.endTransmission();


  Wire.beginTransmission(ADDRESS);
  Wire.write(ACCEL_YOUT_H);
  Wire.requestFrom(ADDRESS,2,1);

  for(int i = 0; i<2 ; i++)
  {
    rawAY[i] = Wire.read();
  }


  rawACCY = rawAY[1] | rawAY[0]<<8;
  Wire.endTransmission();


  Wire.beginTransmission(ADDRESS);
  Wire.write(ACCEL_ZOUT_H);
  Wire.requestFrom(ADDRESS,2,1);

  for(int i = 0; i<2 ; i++)
  {
    rawAZ[i] = Wire.read();
  }

  rawACCZ = rawAZ[1] | rawAZ[0]<<8;
  Wire.endTransmission();
}


void getGYRO(){
  Wire.beginTransmission(ADDRESS);
  Wire.write(GYRO_XOUT_H);
  Wire.requestFrom(ADDRESS,2,1);

  for(int i = 0; i<2 ; i++)
  {
    rawGX[i] = Wire.read();
  }

  rawGYROX = rawGX[1] | rawGX[0]<<8;
  Wire.endTransmission();

  Wire.beginTransmission(ADDRESS);
  Wire.write(GYRO_YOUT_H);
  Wire.requestFrom(0x68,2,1);

  for(int i = 0; i<2 ; i++)
  {
    rawGY[i] = Wire.read();
  }
  rawGYROY = rawGY[1] | rawGY[0]<<8;
  Wire.endTransmission();

  Wire.beginTransmission(ADDRESS);
  Wire.write(GYRO_ZOUT_H);
  Wire.requestFrom(0x68,2,1);

  for(int i = 0; i<2 ; i++)
  {
    rawGZ[i] = Wire.read();
  }

  rawGYROZ = rawGZ[1] | rawGZ[0]<<8;
  Wire.endTransmission();
}

I cannot find problem in my code,
but somehow it seems that 'Acc_X' and 'Gyro_X' result is shifted.

Attachment file 'Previous code.png' is the result of the upper code.
If I change the loop part by shifting the Gyro_X and Acc_X like below,

  getACC();
  Serial.print(rawGYROX/16384, DEC); Serial.print('\t');
  Serial.print(rawACCY/16384, DEC); Serial.print('\t');
  Serial.print(rawACCZ/16384, DEC); Serial.print("\t\t");

  getGYRO();
  Serial.print(rawACCX/131, DEC); Serial.print('\t');
  Serial.print(rawGYROY/131, DEC); Serial.print('\t');
  Serial.print(rawGYROZ/131, DEC);

the result changes like 'Changed code.png'

As I know, if I put the sensor vertically or horizontally, the acceleration result must be +/- 1 for each axis.
And if the sensor is stationary, the gyroscope result must be near zero.

So, it appears that two values(Acc_X & Gyro_X) are being shifted somehow.
I would like to get comments on this issue.
Is there a problem with my code?
Or is there a problem with the sensor?

ICM-20689-v2.2-002.pdf (761 KB)

have you tried requesting all the Acceleration register within the same wire transaction, since the addresses are next to each other instead of asking 3 times for 2 bytes, ask your 6 bytes in one go.

or may be don't put true (1) as the third parameter of your Wire.requestFrom(ADDRESS, 2, 1); so that you don't send a stop message after the request which is releasing the bus. do that only when you are done reading everything

The Arduino Mega board is the only board with 10k pullup resistors from SDA to 5V and from SCL to 5V.
That means you are pushing 5V into the sensor via the 10k pullup resistors.

Which module or breakout board do you have ?
There is a NW-MOT-ICM20689 breakout board, but your drawing suggests an other breakout board.

You have your Wire functions all mixed up. That will not work. Have a look at my alternative explanation.

I faced similar kind of issue last time, I am still searching for some proper solution Same issue still no fix to this.
www.tellpizzahut.com

dallaswhite:
I faced similar kind of issue last time, I am still searching for some proper solution Same issue still no fix to this.

Hi, please start a new Topic and show your sketch, tell us what you have and perhaps a photo of the project and a schematic. Instead of a schematic, a photo of a drawing is also okay.

Koepel:
The Arduino Mega board is the only board with 10k pullup resistors from SDA to 5V and from SCL to 5V.
That means you are pushing 5V into the sensor via the 10k pullup resistors.

Which module or breakout board do you have ?
There is a NW-MOT-ICM20689 breakout board, but your drawing suggests an other breakout board.

You have your Wire functions all mixed up. That will not work. Have a look at my alternative explanation.

I found that MEGA board I am using is not genuine.
So I am now using Arduino UNO, and the IMU board is EVB-ICM20689.

dallaswhite:
I faced similar kind of issue last time, I am still searching for some proper solution Same issue still no fix to this.

How is your result similar to mine? I would like to know.

J-M-L:
have you tried requesting all the Acceleration register within the same wire transaction, since the addresses are next to each other instead of asking 3 times for 2 bytes, ask your 6 bytes in one go.

or may be don't put true (1) as the third parameter of your

Wire.requestFrom(ADDRESS, 2, 1);

so that you don't send a stop message after the request which is releasing the bus. do that only when you are done reading everything

Thank you for your reply.
I tried both of your suggestions.
#1

Wire.requestFrom(ADDRESS,2);

For getACC and getGYRO code, as suggested,
I did not put third parameter for first two 'Wire.requestFrom' lines as above.
However, the result was the same.
#2
I asked the 6 bytes in one go, as you mentioned.

#define ACCEL_XOUT_H            0x3B[color=#222222][/color]
#define ACCEL_YOUT_H            0x3D[color=#222222][/color]
#define ACCEL_ZOUT_H            0x3F[color=#222222][/color]
#define GYRO_XOUT_H             0x43[color=#222222][/color]
#define GYRO_YOUT_H             0x45[color=#222222][/color]
#define GYRO_ZOUT_H             0x47

byte rawACC[6];
byte rawGYRO[6];
float rawACCX, rawACCY, rawACCZ;
float rawGYROX, rawGYROY, rawGYROZ;

void getACC(){
  Wire.beginTransmission(ADDRESS);
  Wire.write(ACCEL_XOUT_H);
  Wire.requestFrom(ADDRESS,6,1);

  for(int i = 0; i < 6, i++)
  {
     rawACC[i] =  Wire.read();
  }
  rawACCX = rawACC[1] | rawACC[0] <<8;
  rawACCY = rawACC[3] | rawACC[2] <<8;
  rawACCZ = rawACC[5] | rawACC[4] <<8;
  
  Wire.endTransmission();
}

void getGYRO(){
  Wire.beginTransmission(ADDRESS);
  Wire.write(GYRO_XOUT_H);
  Wire.requestFrom(ADDRESS,6,1);
  for(int i = 0; i<6 ; i++)
  {
    rawGYRO[i] = Wire.read();
  }

  rawGYROX = rawGYRO[1] | rawGYRO[0]<<8;
  rawGYROY = rawGYRO[3] | rawGYRO[2]<<8;
  rawGYROZ = rawGYRO[5] | rawGYRO[4]<<8;
  Wire.endTransmission();
}

Acc_X Acc_Y Acc_Z Gyro_X Gyro_Y Gyro_Z
[g] [g] [g] [deg/s] [deg/s] [deg/s]
0.0000000000 0.0000000000 0.0000000000 1.0076335668 -56.5190849304 109.0687026977
0.0031127929 0.0079345703 0.0029296875 0.3053435087 -56.9465637207 108.5496215820
0.0034179687 0.0073242187 0.0031738281 0.4885496139 -57.0381660461 108.7022933959
0.0036621093 0.0077514648 0.0031738281 0.0000000000 -56.6717567443 108.7633590698
0.0040283203 0.0073852539 0.0031127929 -0.2748091459 -56.4274826049 108.9160308837
0.0026245117 0.0087890625 0.0031127929 0.3053435087 -56.3969459533 108.4885482788
0.0017089843 0.0075073242 0.0033569335 -0.2442748069 -57.0687026977 108.9770965576
0.0034179687 0.0084838867 0.0017089843 0.3358778715 -56.3358764648 108.9465637207
0.0026245117 0.0076904296 0.0016479492 0.4885496139 -56.9771003723 109.2213745117
0.0020751953 0.0074462890 0.0028686523 1.0381679534 -57.0992355346 108.8549652099
0.0034790039 0.0079956054 0.0014648437 0.2137404441 -56.7938919067 108.6717529296
0.0036621093 0.0093994140 0.0029907226 0.2748091459 -56.7328262329 108.3053436279
0.0029296875 0.0081787109 0.0020751953 1.2824426841 -57.1603050231 108.9465637207
0.0022583007 0.0073242187 0.0023803710 0.3358778715 -57.5572509765 109.1908416748
0.0024414062 0.0079345703 0.0020141601 0.4274808883 -57.2824440002 109.3740463256

But the result dramatically changed. The code seems to have an error

Manufacturer's page of the ICM-20689: ICM-20689 | TDK.
The link to its Evaluation Board does not open.

I think that you did not read my alternative explanation of the Wire functions.
You have the I2C-reading and I2C-writing all mixed up. At this moment you are calling randomly functions of the Wire library and hoping that you get the right data. Of course you don't get the right data.

Please read it and tell me that you have read it.

The ICM-20689 is a 3.3V sensor. You might have damaged it by connecting it to the Mega board. The Arduino Uno is still a 5V board with a 5V I2C bus. I doubt if the Evaluation Board does have level shifters, so you can damage the sensor when you connect it to a Arduino Uno.

Maybe you should read this whole page as well.

Koepel:
Manufacturer's page of the ICM-20689: ICM-20689 | TDK InvenSense.
The link to its Evaluation Board does not open.

I think that you did not read my alternative explanation of the Wire functions.
You have the I2C-reading and I2C-writing all mixed up. At this moment you are calling randomly functions of the Wire library and hoping that you get the right data. Of course you don't get the right data.

Please read it and tell me that you have read it.

The ICM-20689 is a 3.3V sensor. You might have damaged it by connecting it to the Mega board. The Arduino Uno is still a 5V board with a 5V I2C bus. I doubt if the Evaluation Board does have level shifters, so you can damage the sensor when you connect it to a Arduino Uno.

Maybe you should read this whole page as well.

Here is the link to the EV_ICM_20689 datasheet
#1
I have read the datasheet, and I know that the proper voltage to be used is 3.3V not 5V.
So I have connected to the 3.3V output pin of both arduino UNO, and MEGA.
#2
I have read the your links, and realized the problem.

void getACC(){
  Wire.beginTransmission(ADDRESS);
  Wire.write(ACCEL_XOUT_H);
  Wire.endTransmission();
  
  Wire.requestFrom(ADDRESS,2,1);  
  for(int i = 0; i<2 ; i++)
  {
    rawAX[i] = Wire.read();
  }
  rawACCX = rawAX[1] | rawAX[0]<<8;

  //==================================================
  Wire.beginTransmission(ADDRESS);
  Wire.write(ACCEL_YOUT_H);
  Wire.endTransmission();
  
  Wire.requestFrom(ADDRESS,2,1);
  for(int i = 0; i<2 ; i++)
  {
    rawAY[i] = Wire.read();
  }
  rawACCY = rawAY[1] | rawAY[0]<<8;
  

  //==================================================
  Wire.beginTransmission(ADDRESS);
  Wire.write(ACCEL_ZOUT_H);
  Wire.endTransmission();
  
  Wire.requestFrom(ADDRESS,2,1);
  for(int i = 0; i<2 ; i++)
  {
    rawAZ[i] = Wire.read();
  }
  rawACCZ = rawAZ[1] | rawAZ[0]<<8;
}


void getGYRO(){
  Wire.beginTransmission(ADDRESS);
  Wire.write(GYRO_XOUT_H);
  Wire.endTransmission();
  
  Wire.requestFrom(ADDRESS,2,1);
  for(int i = 0; i<2 ; i++)
  {
    rawGX[i] = Wire.read();
  }
  rawGYROX = rawGX[1] | rawGX[0]<<8;

  //==================================================
  Wire.beginTransmission(ADDRESS);
  Wire.write(GYRO_YOUT_H);
  Wire.endTransmission();
  
  Wire.requestFrom(ADDRESS,2,1);
  for(int i = 0; i<2 ; i++)
  {
    rawGY[i] = Wire.read();
  }
  rawGYROY = rawGY[1] | rawGY[0]<<8;


  //==================================================
  Wire.beginTransmission(ADDRESS);
  Wire.write(GYRO_ZOUT_H);
  Wire.endTransmission();
  
  Wire.requestFrom(ADDRESS,2,1);
  for(int i = 0; i<2 ; i++)
  {
    rawGZ[i] = Wire.read();
  }
  rawGYROZ = rawGZ[1] | rawGZ[0]<<8;
}

Acc_X Acc_Y Acc_Z Gyro_X Gyro_Y Gyro_Z
-0.0219726562 -0.1416015625 0.9704589843 0.0000000000 1.0381679534 0.3587786197
-0.0207519531 -0.1367187500 0.9653320312 0.4580152511 0.8167939186 0.4045801639
-0.0217285156 -0.1384277343 0.9680175781 0.5954198360 0.9160305023 0.3358778715
-0.0229492187 -0.1352539062 0.9658203125 0.8778625488 0.7862595558 0.4351144790
-0.0187988281 -0.1340332031 0.9672851562 0.5954198360 0.9618320465 0.3358778715
-0.0205078125 -0.1364746093 0.9680175781 0.8854962348 0.8702289581 0.3358778715
-0.0192871093 -0.1389160156 0.9682617187 0.7099236965 1.0000000000 0.4198473453
-0.0212402343 -0.1381835937 0.9680175781 0.5190839767 0.8473282814 0.3129770994
-0.0205078125 -0.1352539062 0.9685058593 0.9389312744 0.8396946907 0.4351144790
-0.0168457031 -0.1420898437 0.9682617187 0.4885496139 0.9312976837 0.2900763511
-0.0158691406 -0.1367187500 0.9672851562 0.5496182918 1.1603053808 0.4427481174
-0.0200195312 -0.1435546875 0.9714355468 0.2290076255 1.0152671337 0.4122137546
-0.0178222656 -0.1401367187 0.9692382812 0.2748091459 0.9923664093 0.2671755790
-0.0207519531 -0.1440429687 0.9711914062 0.3435114622 1.0534350872 0.2442748069
-0.0175781250 -0.1396484375 0.9711914062 0.1603053331 1.1221374273 0.2595419883
-0.0231933593 -0.1425781250 0.9689941406 0.0763358783 1.0305343866 0.3587786197
-0.0249023437 -0.1413574218 0.9709472656 0.1145038127 1.1984733390 0.4427481174
-0.0222167968 -0.1394042968 0.9714355468 -0.1526717567 1.3435114622 0.1755725145
-0.0197753906 -0.1374511718 0.9665527343 -0.3435114622 1.2137404680 0.4351144790
-0.0231933593 -0.1435546875 0.9680175781 0.0610687017 0.9847328186 0.5038167953
-0.0195312500 -0.1374511718 0.9670410156 -0.1068702220 1.0916030406 0.2977099227
-0.0219726562 -0.1433105468 0.9663085937 0.1297709941 1.0687023401 0.2671755790
-0.0192871093 -0.1398925781 0.9687500000 0.0381679391 0.9236640930 0.4274808883

The result of this code is above, and I think the problem is almost solved.
I have tested all four axis rotation of the device, and found everything matches.

The device is almost horizontally placed, without any rotation.
In this case, Acc_Z should be near 1g, Acc_X and Acc_Y should be near 0g.
All gyro result should be near 0 deg/s.
The result matches the desired values.

I would like to ask if there are any more problem in the revised code I made.

No, no problems :smiley:

The acceleration measures the earth gravity and is very sensitive for small vibrations.
The gyro measures the increase and decrease in rotation (not the rotation).
The ±250 °/s for the gyro is the most sensitive, then that gyro noise is normal I think.

It is possible to read 14 bytes with one Wire.requestFrom() call. Then you get also the temperature, but it still will be faster.

You may omit the third parameter of the Wire.requestFrom(). The default is always a STOP condition at the end.

Wire.requestFrom(ADDRESS,2);  // request 2 bytes from ADDRESS

Can you handle it if I start nagging about voltage levels on the I2C bus once more ?
The voltage regulator on that module will make 3.0V, regardless if you power the board with 3.3V or 5V. That is not what this is about. This is about the I2C bus.

The Arduino Uno runs at 5V. It has internal pullup resistors from SDA to 5V and from SCL to 5V. They are 30k to 50k. It expects that the I2C bus signals are either 0V or 5V.

According to that document, the sensor seems to be running at 3.0V.
The schematic is very confusing. It seems that everything that is "open" is not on the board.
I think that the SDA is the AUX_DA and is directly connected to the sensor.
So the sensor is using a 3.0V I2C bus.
According to the datasheet the VIH for the I2C bus is maximum VDDIO + 0. 5 V, that is 3.5V.

When you connect the 5V I2C bus to the 3.0V I2C bus then the internal pullup resistors of the Arduino Uno leaks current into the SDA pin of the sensor. That can damage the sensor.
You better use a I2C level shifter.

All this trouble is gone in an instant of you use a 3.3V Arduino board. Since 3.3V is within the specifications of SDA and SCL (max 3.5V), then the SDA and SCL pins can be connected directly to the sensor.

Next step: filters: Kalman, AHRS.

Koepel:
No, no problems :smiley:

The acceleration measures the earth gravity and is very sensitive for small vibrations.
The gyro measures the increase and decrease in rotation (not the rotation).
The ±250 °/s for the gyro is the most sensitive, then that gyro noise is normal I think.

It is possible to read 14 bytes with one Wire.requestFrom() call. Then you get also the temperature, but it still will be faster.

You may omit the third parameter of the Wire.requestFrom(). The default is always a STOP condition at the end.

Wire.requestFrom(ADDRESS,2);  // request 2 bytes from ADDRESS

Can you handle it if I start nagging about voltage levels on the I2C bus once more ?
The voltage regulator on that module will make 3.0V, regardless if you power the board with 3.3V or 5V. That is not what this is about. This is about the I2C bus.

The Arduino Uno runs at 5V. It has internal pullup resistors from SDA to 5V and from SCL to 5V. They are 30k to 50k. It expects that the I2C bus signals are either 0V or 5V.

According to that document, the sensor seems to be running at 3.0V.
The schematic is very confusing. It seems that everything that is "open" is not on the board.
I think that the SDA is the AUX_DA and is directly connected to the sensor.
So the sensor is using a 3.0V I2C bus.
According to the datasheet the VIH for the I2C bus is maximum VDDIO + 0. 5 V, that is 3.5V.

When you connect the 5V I2C bus to the 3.0V I2C bus then the internal pullup resistors of the Arduino Uno leaks current into the SDA pin of the sensor. That can damage the sensor.
You better use a I2C level shifter.

All this trouble is gone in an instant of you use a 3.3V Arduino board. Since 3.3V is within the specifications of SDA and SCL (max 3.5V), then the SDA and SCL pins can be connected directly to the sensor.

Next step: filters: Kalman, AHRS.

Thank you so much for helping me out.
It would be better to use 3.3V Arduino board, as you mentioned.
I found arduino pro mini can operate in 3.3V. Is there any other boards I can use?

You don't have to quote my reply. What I wrote is already there :wink:

The newer generation boards are 3.3V. The best processor is the SAMD21G. It is on most MKR boards.
A ATmega328P (as used in the Arduino Uno) can run with 3.3V when the clock is 8 MHz instead of the normal 16 MHz.
In the Arduino IDE, after selecting Pro Mini, there is an extra selection in the menu for 5V/16MHz or 3.3V/8MHz.

When you are going to use Wifi or Bluetooth, then the ESP32 is a good board. It also runs at 3.3V.

A week ago, there was a new processor the RP2040. That is also a 3.3V processor. Let's hope they make a simple Arduino board with it, and not a over the top expensive board.

Koepel, I don't know how to reply for your replies... (hope this works)

I don't need Wifi or Bluetooth options, I will need just 3.3V arduino board as you mentioned.

I found Arduino Nano 33 IoT, which is equipped with IMU sensor ESP32.
I am using Arduino to experiment Magnetometer(MMC5883MA) and Acc&Gyro sensor(ICM-20689),
and need a sensor to compare with.
It has built-in sensor to use and it is cheap, so I am bought Nano 33 IoT.
I already ordered this before reading this, and it's so great that the best processor SAMD21G is the one that I bought.

Due to the time difference, I couldn't help but contact you at late hours.
Thank you so much for your help :smiley: :smiley:

The Arduino Nano 33 IoT has the SAMD21G, that is very nice.
But it has a NINA-W10 module, which has a ESP32 inside.
I call that: double the trouble and half the fun. Because the communication between the SAMD21G and ESP32 is extra trouble. The firmware of the NINA-W10 / ESP32 could be old.

If you buy a ESP32 board, and use it in Arduino compatible mode, then a sketch runs in the ESP32 itself. The firmware is always up-to-date, it is updated every time a sketch is uploaded.
The disadvantage of the ESP32 is the analog inputs, those are not accurate at all.
The fun thing about the ESP32 is that it has two cores and runs FreeRTOS. It is possible to upload a sketch without using that, but it is also possible to make use of FreeRTOS.

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