dotnet / iot

This repo includes .NET Core implementations for various IoT boards, chips, displays and PCBs.
MIT License
2.18k stars 586 forks source link

Iot.Device.Imu.Mpu6050 - System.IO.IOException on .CalibrateGyroscopeAccelerometer() #2356

Open mfitconsultants opened 1 month ago

mfitconsultants commented 1 month ago

I am struggling to calibrate an Mpu 6050 specifcially it is a;

Adafruit MPU-6050 6-DoF Accel and Gyro Sensor - STEMMA QT Qwiic

which is connected to a Raspberry Pi 2.

When I try to call the .CalibrateGyroscopeAccelerometer(); method I get the error;

System.IO.IOException: Can set GyroscopeBandwidth, desired value Bandwidth0184Hz, stored value Bandwidth0250Hz

 public HardwareAccelerometerController(
     ILogger<HardwareAccelerometerController> logger,
     int i2cAddress,
     int recalibrationIntervalMinutes,
     int i2cBusId = 1)
 {
     _logger = logger;

     try
     {
         var i2cSettings = new I2cConnectionSettings(i2cBusId, i2cAddress);
         var i2cDevice = I2cDevice.Create(i2cSettings);

         _mpu6050 = new Mpu6050(i2cDevice);  // Initialize the MPU6050
         _logger.LogInformation($"MPU6050 initialized with I2C address {i2cAddress} on bus {i2cBusId}.");
     }
     catch (Exception ex)
     {
         _logger.LogError(ex, $"Failed to initialize MPU6050 with I2C address {i2cAddress} on bus {i2cBusId}. Check connections and configuration.");
         throw new InvalidOperationException("MPU6050 initialization failed. Ensure the device is connected and configured correctly.", ex);
     }

     _recalibrationIntervalMinutes = recalibrationIntervalMinutes;
     _lastCalibrationTime = DateTime.Now;
 }

 /// <summary>
 /// Calibrate the accelerometer and gyroscope.
 /// </summary>
 public async Task CalibrateAsync()
 {
 if (_mpu6050 == null)
 {
     _logger.LogError("Cannot calibrate MPU6050 because it is not initialized.");
     throw new InvalidOperationException("MPU6050 is not initialized.");
 }

 await Task.Run(() =>
 {
     _logger.LogInformation("Calibrating MPU6050...");
     try
     {
         _mpu6050.CalibrateGyroscopeAccelerometer();
         _logger.LogInformation("Calibration complete.");
     }
     catch (IOException ex)
     {
         _logger.LogError(ex, "Failed to set the GyroscopeBandwidth. Proceeding with default bandwidth.");
     }
 });
}

From the logger the device is initilised;

MPU6050 initialized with I2C address 104 on bus 1.

From running sudo i2cdetect -y 1 on the pi I get

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

So the unit is present - i have also tried the a python sript which is returning data from the unit.

I have also tested

                    _logger.LogInformation($"MPU6050: Accelerometer X:{_mpu6050.GetAccelerometer().X}, Y:{_mpu6050.GetAccelerometer().Y}, Z:{_mpu6050.GetAccelerometer().Z}");
                    _logger.LogInformation($"MPU6050: Gyroscope X:{_mpu6050.GetGyroscopeReading().X}, Y:{_mpu6050.GetAccelerometer().Y}, Z:{_mpu6050.GetAccelerometer().Z}");

Prior to calling the CalibrateGyroscopeAccelerometer() method which return values. attmepting after the exception leads to 0,0,0 being returned.

I cannot find any detail on this specific error? Can anyone tell me how to debug this and what I may be doing wrong please?

Ellerbach commented 1 month ago

It's been a long time I haven't look at it. But seems you have to set a specific bandwidth before calibrating. Have you tried this? The error message seems to indicate that.

mfitconsultants commented 1 month ago

@Ellerbach

Thanks for the reply. Yes I have tried this, see the example code below;

try
{
    _logger.LogInformation($"Gyroscope bandwidth is set to {_mpu6050.GyroscopeBandwidth}");
    _logger.LogInformation($"Attempting to set the gyroscope bandwidth to Bandwidth0184Hz.");
    _mpu6050.GyroscopeBandwidth = GyroscopeBandwidth.Bandwidth0184Hz;
    _logger.LogInformation($"Gyroscope bandwidth successfully set.");

    _logger.LogInformation($"Attempting to set the accelerometer bandwidth to Bandwidth0184Hz.");
    _mpu6050.AccelerometerBandwidth = AccelerometerBandwidth.Bandwidth0184Hz;
    _logger.LogInformation($"Accelerometer bandwidth successfully set.");
}
catch (IOException ex)
{
    _logger.LogError(ex, $"Failed to set the bandwidth. Exception: {ex.Message}");
}

The output logs shows it appears to be set to the Bandwidth0184Hz value, but then when .CalibrateGyroscopeAccelerometer() is called I get the same error message?

(To note it is only this class using the device so it can't be set elsewhere). I've tried setting this both in the constructor and directly prior to calling .CalibrateGyroscopeAccelerometer().

On top of this I have tried setting the MPU bandwidth directly on the pi

sudo i2cset -y 1 0x68 0x1A 0x01  # Set gyroscope bandwidth to 184Hz
sudo i2cset -y 1 0x68 0x1D 0x01  # Set accelerometer bandwidth to 184Hz

and then confimed the change with

sudo i2cget -y 1 0x68 0x1D
sudo i2cget -y 1 0x68 0x1A

which confirms the output is set to 0x01.

However, then when I run my service and run sudo i2cset it reverts back to 0x00?

Just to note as well if I call;

                    _logger.LogInformation($"Reading MPU values - start");
                    _logger.LogInformation($"MPU6050: Accelerometer X:{_mpu6050.GetAccelerometer().X}, Y:{_mpu6050.GetAccelerometer().Y}, Z:{_mpu6050.GetAccelerometer().Z}");
                    _logger.LogInformation($"MPU6050: Gyroscope X:{_mpu6050.GetGyroscopeReading().X}, Y:{_mpu6050.GetAccelerometer().Y}, Z:{_mpu6050.GetAccelerometer().Z}");
                    _logger.LogInformation($"Reading MPU values - end");

                    _logger.LogInformation($"Gyroscope bandwidth is set to {_mpu6050.GyroscopeBandwidth}");
                    _logger.LogInformation($"Attempting to set the gyroscope bandwidth to Bandwidth0184Hz.");
                    _mpu6050.GyroscopeBandwidth = GyroscopeBandwidth.Bandwidth0184Hz;
                    _logger.LogInformation($"Gyroscope bandwidth now set to {_mpu6050.GyroscopeBandwidth}");

                    _mpu6050.CalibrateGyroscopeAccelerometer();
                    _logger.LogInformation("Calibration complete.");

I do get values returned from the unit the example output first time is

Reading MPU values - start
MPU6050: Accelerometer X:0.4944203, Y:-0.23344299, Z:8.300994
MPU6050: Gyroscope X:-1.1138916, Y:-0.19992298, Z:8.300994
Reading MPU values - end

which then after the calibration failing just then outputs 0?

Equally if do not calibrate the MPU I can read data from the unit (though I am I not sure how accurate this is).

krwq commented 1 month ago

[Triage] @mfitconsultants can you share the stack trace within our code? (current shows only inside your code). Are you perhaps interested in creating a PR with a fix?

mfitconsultants commented 1 month ago

No problem. I will put together a basic test program and PR for it.

Stack trace ex.StackTrace from catching the error on CalibrateGyroscopeAccelerometer() is

System.IO.IOException: Can set GyroscopeBandwidth, desired value Bandwidth0184Hz, stored value Bandwidth0250Hz
...        at Iot.Device.Imu.Mpu6050.set_GyroscopeBandwidth(GyroscopeBandwidth value)

Is there any other information I can try to get? I've ordered another mpu6050 to see if it is the unit as well.

mfitconsultants commented 1 month ago

Added a public repo here

https://github.com/mfitconsultants/MPU6050Test

mfitconsultants commented 1 month ago

The local company ran out of the 6050 so sent me a Adafruit TDK InvenSense ICM-20948 9-DoF IMU (MPU-9250 Upgrade) (STEMMA QT / Qwiic) × 1 as a replacement.

This looks like it isn't supported? I know not really the right forum, but in the short term to get this project off the ground, could you recommend a unit that works well with this library please?

Ellerbach commented 1 month ago

Supported, see here: https://github.com/dotnet/iot/tree/main/src/devices/Mpu6xxx9xxx

mfitconsultants commented 1 month ago

@Ellerbach It doesn't appear the ICM-20948 is supported - whilst it mentions 9250 (it does mention upgrade) it appears not to be supported. I have tested to make sure and receive the error;

Failed to initialize MPU9250 with I2C address 105 on bus 1. Check connections and configuration.
System.IO.IOException: This device does not contain the correct signature 0x71 for a MPU9250
at Iot.Device.Imu.Mpu9250..ctor(I2cDevice i2cDevice, Boolean shouldDispose, I2cDevice i2CDeviceAk8963)
Ellerbach commented 1 month ago

Clone the repository and use the project itself to debug. This clone may have a different signature. We saw this in the past and for some devices, we added the possibility to bypass the check. Also make sure it is at the proper address.