Skip to content

wire.available returns 0 when reading mpu6050 fifo #594

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ifrew opened this issue Aug 26, 2017 · 18 comments
Closed

wire.available returns 0 when reading mpu6050 fifo #594

ifrew opened this issue Aug 26, 2017 · 18 comments

Comments

@ifrew
Copy link

ifrew commented Aug 26, 2017

Please fill the info fields, it helps to get you faster support ;)

If you have a Guru Meditation Error, please decode it:
https://github.com/me-no-dev/EspExceptionDecoder

----------------------------- Remove above -----------------------------

Hardware:

Board: nodemcu 32s
Core Installation/update date: 20/aug/2017
IDE name: vmicro visual studio
Flash Frequency: 80mhz
Upload Speed: 115200

Description:

First time posting to this forum. I am porting code that runs fine on nodemcu esp8266 to esp32 that communicates with a mpu6050 imu that is using i2cdevlib for the communication. This library uses the esp32 core two wire library. I am using the DMP processor on this chip rather than reading raw values. When data is available to be read from the FIFO on the DMP an interrupt is raised. I then read the number of bytes to be read from the FIFO and then read the FIFO.

The problem I am seeing on the esp32 is that the wire.available call is returning 0 bytes. I have read comments by Me-no-dev that the mpu6050 i2c bus interface on esp32 had some problems before and I am wondering now if this is a new issue? I had enabled debug trace statements in the calls and the relevant trace is attached. I have also attached the code from the i2cdevlib that is calling the two wire library. Ardunio version is greater than 100.
getfifobytesdmp.txt

int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout) {

#ifdef I2CDEV_SERIAL_DEBUG

    Serial.print("I2C (0x");

    Serial.print(devAddr, HEX);

    Serial.print(") reading ");

    Serial.print(length, DEC);

    Serial.print(" bytes from 0x");

    Serial.print(regAddr, HEX);

    Serial.print("...");

#endif



int8_t count = 0;

uint32_t t1 = millis();



#if (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE)



    #if (ARDUINO < 100)

        // Arduino v00xx (before v1.0), Wire library



        // I2C/TWI subsystem uses internal buffer that breaks with large data requests

        // so if user requests more than BUFFER_LENGTH bytes, we have to do it in

        // smaller chunks instead of all at once

        for (uint8_t k = 0; k < length; k += min(length, BUFFER_LENGTH)) {

            Wire.beginTransmission(devAddr);

            Wire.send(regAddr);

            Wire.endTransmission();

            Wire.beginTransmission(devAddr);

            Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH));



            for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {

                data[count] = Wire.receive();

                #ifdef I2CDEV_SERIAL_DEBUG

                    Serial.print(data[count], HEX);

                    if (count + 1 < length) Serial.print(" ");

                #endif

            }



            Wire.endTransmission();

        }

    #elif (ARDUINO == 100)

        // Arduino v1.0.0, Wire library

        // Adds standardized write() and read() stream methods instead of send() and receive()



        // I2C/TWI subsystem uses internal buffer that breaks with large data requests

        // so if user requests more than BUFFER_LENGTH bytes, we have to do it in

        // smaller chunks instead of all at once

        for (uint8_t k = 0; k < length; k += min(length, BUFFER_LENGTH)) {

            Wire.beginTransmission(devAddr);

            Wire.write(regAddr);

            Wire.endTransmission();

            Wire.beginTransmission(devAddr);

            Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH));

    

            for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {

                data[count] = Wire.read();

                #ifdef I2CDEV_SERIAL_DEBUG

                    Serial.print(data[count], HEX);

                    if (count + 1 < length) Serial.print(" ");

                #endif

            }

    

            Wire.endTransmission();

        }

    #elif (ARDUINO > 100)

        // Arduino v1.0.1+, Wire library

        // Adds official support for repeated start condition, yay!



        // I2C/TWI subsystem uses internal buffer that breaks with large data requests

        // so if user requests more than BUFFER_LENGTH bytes, we have to do it in

        // smaller chunks instead of all at once

        for (uint8_t k = 0; k < length; k += min(length, BUFFER_LENGTH)) {

            Wire.beginTransmission(devAddr);

            Wire.write(regAddr);

            Wire.endTransmission();

            Wire.beginTransmission(devAddr);

            Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH));

    

            for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {

                data[count] = Wire.read();

                #ifdef I2CDEV_SERIAL_DEBUG

                    Serial.print(data[count], HEX);

                    if (count + 1 < length) Serial.print(" ");

                #endif

            }

        }

    #endif



#elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE)



    // Fastwire library

    // no loop required for fastwire

    uint8_t status = Fastwire::readBuf(devAddr << 1, regAddr, data, length);

    if (status == 0) {

        count = length; // success

    } else {

        count = -1; // error

    }



#endif



// check for timeout

if (timeout > 0 && millis() - t1 >= timeout && count < length) count = -1; // timeout



#ifdef I2CDEV_SERIAL_DEBUG

    Serial.print(". Done (");

    Serial.print(count, DEC);

    Serial.println(" read).");

#endif



return count;

}

@ifrew
Copy link
Author

ifrew commented Aug 26, 2017

Forgot to add my test sketch. This runs fine on esp8266 but not on esp32.
testmpu6050esp32.txt

@tobozo
Copy link
Contributor

tobozo commented Aug 26, 2017

see #53

@ifrew
Copy link
Author

ifrew commented Aug 26, 2017

Yeah I saw that thanks. However it's reading and writing to all the registers and initialized the dmp fine. I'm guessing some kind of timing issue possibly. Is there a way to set the clock speed slower on the esp32?

@tobozo
Copy link
Contributor

tobozo commented Aug 26, 2017

Have you tried to set a lower value (i.e. 100000) for Wire.setClock() ?

@tobozo
Copy link
Contributor

tobozo commented Aug 26, 2017

I couldn't get your sketch to work (requires modifications to MPU6050 lib) but I got this sketch working perfectly.

[edit] pasting the example code here:

#include <Wire.h>

#define MPU6050_ADDR         0x68 // MPU-6050 device address
#define MPU6050_SMPLRT_DIV   0x19 // MPU-6050 register address
#define MPU6050_CONFIG       0x1a
#define MPU6050_GYRO_CONFIG  0x1b
#define MPU6050_ACCEL_CONFIG 0x1c
#define MPU6050_WHO_AM_I     0x75
#define MPU6050_PWR_MGMT_1   0x6b

double offsetX = 0, offsetY = 0, offsetZ = 0;
double gyro_angle_x = 0, gyro_angle_y = 0, gyro_angle_z = 0;
float angleX, angleY, angleZ;
float interval, preInterval;
float acc_x, acc_y, acc_z, acc_angle_x, acc_angle_y;
float gx, gy, gz, dpsX, dpsY, dpsZ;

void culcRotation();

//I2c書き込み
void writeMPU6050(byte reg, byte data) {
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(reg);
  Wire.write(data);
  Wire.endTransmission();
}

//i2C読み込み
byte readMPU6050(byte reg) {
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(reg);
  Wire.endTransmission(true);
  Wire.requestFrom(MPU6050_ADDR, 1/*length*/); 
  byte data =  Wire.read();
  return data;
}

void setup() {

  Wire.begin(21, 22);
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0); // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  
  Serial.begin(115200);
  delay(100);
  
  //正常に接続されているかの確認
  if (readMPU6050(MPU6050_WHO_AM_I) != 0x68) {
    Serial.println("\nWHO_AM_I error.");
    while (true) ;
  }

  //設定を書き込む
  writeMPU6050(MPU6050_SMPLRT_DIV, 0x00);   // sample rate: 8kHz/(7+1) = 1kHz
  writeMPU6050(MPU6050_CONFIG, 0x00);       // disable DLPF, gyro output rate = 8kHz
  writeMPU6050(MPU6050_GYRO_CONFIG, 0x08);  // gyro range: ±500dps
  writeMPU6050(MPU6050_ACCEL_CONFIG, 0x00); // accel range: ±2g
  writeMPU6050(MPU6050_PWR_MGMT_1, 0x01);   // disable sleep mode, PLL with X gyro

  //キャリブレーション
  Serial.print("Calculate Calibration");
  for(int i = 0; i < 3000; i++){
    
    int16_t raw_acc_x, raw_acc_y, raw_acc_z, raw_t, raw_gyro_x, raw_gyro_y, raw_gyro_z ;
    
    Wire.beginTransmission(MPU6050_ADDR);
    Wire.write(0x3B);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU6050_ADDR, 14, true);
  
    raw_acc_x = Wire.read() << 8 | Wire.read();
    raw_acc_y = Wire.read() << 8 | Wire.read();
    raw_acc_z = Wire.read() << 8 | Wire.read();
    raw_t = Wire.read() << 8 | Wire.read();
    raw_gyro_x = Wire.read() << 8 | Wire.read();
    raw_gyro_y = Wire.read() << 8 | Wire.read();
    raw_gyro_z = Wire.read() << 8 | Wire.read();
    dpsX = ((float)raw_gyro_x) / 65.5;
    dpsY = ((float)raw_gyro_y) / 65.5;
    dpsZ = ((float)raw_gyro_z) / 65.5;
    offsetX += dpsX;
    offsetY += dpsY;
    offsetZ += dpsZ;
    if(i % 1000 == 0){
      Serial.print(".");
    }
  }
  Serial.println();

  offsetX /= 3000;
  offsetY /= 3000;
  offsetZ /= 3000;

  Serial.print("offsetX : ");
  Serial.println(offsetX);
  Serial.print("offsetY : ");
  Serial.println(offsetY);
  Serial.print("offsetZ : ");
  Serial.println(offsetZ);
  
}

void loop() {

  calcRotation();

  Serial.print("angleX : ");
  Serial.print(angleX);
  Serial.print("    angleY : ");
  Serial.print(angleY);
  Serial.print("    angleZ : ");
  Serial.println(angleZ);
 
}

//加速度、ジャイロから角度を計算
void calcRotation(){

  int16_t raw_acc_x, raw_acc_y, raw_acc_z, raw_t, raw_gyro_x, raw_gyro_y, raw_gyro_z ;
  
  //レジスタアドレス0x3Bから、計14バイト分のデータを出力するようMPU6050へ指示
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(0x3B);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU6050_ADDR, 14, true);
  
  //出力されたデータを読み込み、ビットシフト演算
  raw_acc_x = Wire.read() << 8 | Wire.read();
  raw_acc_y = Wire.read() << 8 | Wire.read();
  raw_acc_z = Wire.read() << 8 | Wire.read();
  raw_t = Wire.read() << 8 | Wire.read();
  raw_gyro_x = Wire.read() << 8 | Wire.read();
  raw_gyro_y = Wire.read() << 8 | Wire.read();
  raw_gyro_z = Wire.read() << 8 | Wire.read();
  
  //単位Gへ変換
  acc_x = ((float)raw_acc_x) / 16384.0;
  acc_y = ((float)raw_acc_y) / 16384.0;
  acc_z = ((float)raw_acc_z) / 16384.0;
  
  //加速度センサーから角度を算出
  acc_angle_y = atan2(acc_x, acc_z + abs(acc_y)) * 360 / -2.0 / PI;
  acc_angle_x = atan2(acc_y, acc_z + abs(acc_x)) * 360 / 2.0 / PI;

  dpsX = ((float)raw_gyro_x) / 65.5; // LSB sensitivity: 65.5 LSB/dps @ ±500dps
  dpsY = ((float)raw_gyro_y) / 65.5;
  dpsZ = ((float)raw_gyro_z) / 65.5;
  
  //前回計算した時から今までの経過時間を算出
  interval = millis() - preInterval;
  preInterval = millis();
  
  //数値積分
  gyro_angle_x += (dpsX - offsetX) * (interval * 0.001);
  gyro_angle_y += (dpsY - offsetY) * (interval * 0.001);
  gyro_angle_z += (dpsZ - offsetZ) * (interval * 0.001);
  
  //相補フィルター
  angleX = (0.996 * gyro_angle_x) + (0.004 * acc_angle_x);
  angleY = (0.996 * gyro_angle_y) + (0.004 * acc_angle_y);
  angleZ = gyro_angle_z;
  gyro_angle_x = angleX;
  gyro_angle_y = angleY;
  gyro_angle_z = angleZ;
}

@ifrew
Copy link
Author

ifrew commented Aug 26, 2017 via email

@ifrew
Copy link
Author

ifrew commented Aug 27, 2017

Ok. Tried a lower clock value no luck. Attached are the source files I am using in the test sketch for the mpu6050 library. Hopefully you may be able to compile now. I can of course go back to using raw values and doing the calculation but using the DMP makes better sense.
MPU6050_6Axis_MotionApps20.zip

@tobozo
Copy link
Contributor

tobozo commented Aug 28, 2017

You code seems to work just fine, until I shake the MPU then it crashes (the MPU, not the ESP), then I have to power-cycle it to get it back.
Looking at this and this it looks like it's back to the normal behaviour considering the buggy quality of the library.
What's the advantage of using the FIFO over reading raw data?

@ifrew
Copy link
Author

ifrew commented Aug 28, 2017

Darn, thanks for posting the links, especially the first one as that is exactly the issue and code line that s failing. I had actually saw this a week ago when looking to resolve another issue and forgot about it. I can close this issue as a duplicate of that thread.

Using the dmp does a lot of the maths for fusing the gyro and accel,data and so, off loads that from the host processor. According to the product sheet "The FIFO buffers the complete data set, reducing timing requirements on the system processor by allowing the processor burst read the FIFO data. After burst reading the FIFO data, the system processor can save power by entering a low-power sleep mode while the MPU collects more data "

Many thanks again. I'll reduce the sampling rate to see if it makes a difference but I'm sure I have already tried that and it didn't work. I expect it is the mpu6050 i2c HW interface with esp32 that is the issue as discussed in the thread.

Cheers
Iain,

@ifrew
Copy link
Author

ifrew commented Aug 28, 2017

Don't know how to close this issue. First time using this system.

@ifrew
Copy link
Author

ifrew commented Aug 28, 2017

Update: Found a workaround.! Just so folks know after reading all the threads on this issue, it did seem to be a timing related issue when requesting a large amount of data to be read from the twowire library. So I thought about what happens if I just read a smaller amount of data and chunk up the requests. Well it turns out that the i2cdevlib library already does that so all I needed to do was change the buffer size to some value smaller. Lo and behold it works. Changing the buffer_length in the i2cdevlib library to 30 works with my test sample code here on my device. Anything greater it fails. In my actual application, I needed to reduce the buffer size to 16 to get it to work as during the DMP initialization the Fifo is also read but there is normally a large amount of data in it. So for this device, you may need to play with the buffer length to get it working on your application if you decide to use the DMP. Thanks to all those that work on this stuff and provide the support. It's a pretty amazing resource.!

Cheers
Iain

@LEMAOR
Copy link

LEMAOR commented Sep 4, 2017

can i ask how difficult would it be to implement this on the C side of the esp32, i understand that here we are relaying on the "wire" library to perform the reads writes and buffer the read bytes, which i dont believe we have over on the non arduino-esp32.

I'm wondering if its just a matter of waking the mpu6050, loading settings, loading the "firmware" (i call firmware the big array of bytes you have to write to the mpu6050), and reading the fifo periodically.

am i over simplifying how to read the dmp? would it be way more complex than it is worth? i understand a bit of coding but i kind of feel trying to implement this is more than i can chew.

Maybe its not, it would be nice some input from someone else.

thanks in advance.

p.d. i have a very mature implementation already working on the esp-idf, moving to esp32-arduino wouldnt be my best option atm. but as everyone else i see the advantage to reading the dmp

@me-no-dev
Copy link
Member

@LEMAOR are you asking for this? Wire is just a wrapper around the C API

@LEMAOR
Copy link

LEMAOR commented Sep 4, 2017

my question is more about the implementation... as i don't think there is an i2cdevlib written for c that I can use on the esp32idf environment, so what i am wondering if reading the dmp, is just a matter of just replicating the register writes and read that the i2cdevlib does.

would it be just a matter of using: i2cWrite(),i2cRead() , in such a way that i load the firmware in and just start reading the fifo registers?

or would it be way more complex that its not something i can accomplish on a timely manner?

I'm sorry for asking about esp-idf on the esp32-arduino git, just looking for some guidance
thanks in advance

@me-no-dev
Copy link
Member

idf also has I2C driver of it's own here. If you do not find that lib working on ESP32 then yes, you will need to replace the calls with the ones for the driver of your choice. Make sure you are using the same driver for all your I2C devices on that bus.

@carbonadam
Copy link

@ifrew I would love to see your solution to get out the FIFO data. I am in the process of porting over code from other robot projects and currently stuck getting decent data from my MPU6050. ..just the raw stuff which is not much help

@aopaw
Copy link

aopaw commented Dec 13, 2019

what does 0.004 mean

@tobozo
Copy link
Contributor

tobozo commented Dec 13, 2019

angleX = (0.996 * gyro_angle_x) + (0.004 * acc_angle_x);

0.004 + 0.996 = 1

it's used for inertia, acceleration data is diluted into to gyro data 4 per thousands at a time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants