jefmenegazzo/mpu-i2c-drivers-python

Question about specifying bus address to calibrate

jdc-cunningham opened this issue · 8 comments

Describe the bug
If you set the address_mpu_master to be MPU9050_ADDRESS_69 and try to calibrate, it throws an OSError: [Errno 121] Remote I/O error. When checking the i2c connected address with i2cdetect -y 1 my particular "bad" 9250 sensor is showing up as 69 even when AD0 is open/not attached to anything. I am only using 1 sensor. Doing the address change fixes the data stream however calibration I can't seem to make it only update 69. I saw one of the "definitions" here seems to cover both 68 and 69 so I wanted to ask if you could pass in a bus address to the mpu.calibrate(). This probably doesn't make sense if you're setting the address for mpu. But yeah that's my issue.

To Reproduce
Steps to reproduce the behavior:

  1. Get a bad sensor (ha) that shows up as 69 in i2cdetect prompt instead of default 68.
  2. Try to calibrate it, shows OSError ... Remote I/O error

Expected behavior
Maybe calibrate the 69 address based on "instantiated" mpu with MPU9050_ADDRESS_69

Screenshots
My wiring (note no AD0, generally 68 is what shows up but a couple of boards showing up as 69)
wiring

Error from wrong bus address
pi-bash-output

Raspberry Pi 4
Python 3.7.3

Additional context
I was trying to flex this so if I do have a bad board and it shows up as 69 I can set that/calibrate it through CLI-args.

Running these two commands in sequence, after changing to 69 seems to be problematic. But if you run them one at a time it works... so I'm not sure.

mpu.calibrateMPU6500()
mpu.calibrateAK8963()

@jdc-cunningham

  • What is your MPU? 9250, 9150, 6050?

  • The OSError: [Errno 121] Remote I/O error occurs when the smbus is unable to access the MPU. There may be a problem with your hardware. You can switch the bus libraries in the header of the mpu_9250 file, between smbus and smbus2 to see if there is any difference (I believe not)

  • 9250 sensor is showing up as 69 even when AD0 is open/not attached to anything: reconfigure your RPi with sudo rasp-config and Interfacing Options → I2C→ Enable and restart. In general, restarting fixes these problems from incorrect addresses (or disconnect the MPU cables and reconnect). Runing the command i2cdetect, in addition to 0x68 or x069, the address 0x0C must also appear (magnetometer).

  • Always use the commands with sudo first, to avoid any permission problems: e.g.: sudo i2cdetect -y 1 , sudo python main.py

  • Try to read any register from your MPU directly with the i2cget, e.g.: sudo i2cget -y 1 0x69 0x75. Please check the first part of this tutorial

  • Check that the SDA and SLC are correctly connected to the RPi and MPU.

  • If you set the address_mpu_master to be MPU9050_ADDRESS_69 and try to calibrate, it throws an OSError: [Errno 121] Remote I/O error: if AD0 is not connected, then always use MPU9050_ADDRESS_68

  • If you need to change the MPU address of an object already instantiated, you can do it by the attribute:: address_mpu_master. e.g.:

mpu.address_mpu_master = MPU9050_ADDRESS_69
mpu.calibrateMPU6500()
mpu.address_mpu_master = MPU9050_ADDRESS_68

Share later if you managed to run correctly, it may be important for other people who use lib :)

@jefmenegazzo

Thanks for the fast response!

I actually bought 4 of these sensors and it seems like a 50% hit rate or so as far as them working fully/correctly out of the box. One of them the gravity was fixed to -8... anyway you've brought up a lot of good points I was not even aware of/considered.

I should have pointed out, I made a visualizer thing and I'm looking at the values being plotted real time on a chart. I will stop the python script and "hot swap" sensors... that's where the recalibration comes in assuming it shows up as the right address(hence trying to specify which by cli args). I wouldn't use this in production, once I figure out which sensors are good I would stick to the one for that given Pi setup.

  • I have an MPU9250 supposedly
  • switch the bus libraries in the header of the mpu_9250

    • I will have to check that out, I would be concerned if future updates would overwrite that change?
  • I am concerned if you'd have to re-enable i2c everytime/reupdate but maybe that's how it goes with my setup.
    • the address 0x0C must also appear

      • that is interesting because I actually have rarely seen that address except one time and I thought it was a fluke/bug
  • Always use the commands with sudo first

    • okay I will try that and see if there is a difference
  • Try to read any register from your MPU directly

    • okay I have not tried that
  • if AD0 is not connected, then always use MPU9050_ADDRESS_68

    • right that's how I thought it would work but weird that some sensors would show up as 69
  • If you need to change the MPU address of an object already instantiated, you can do it by the attribute:: address_mpu_master

    • I see, I actually was re-instatiating it/reassigning it from scratch but that seems nicer to do

I will try these things out that you've suggested thanks

Offhand I don't know what values make sense for the magnetometer, at one point the Z-axis was showing 70's range, and the other two were in the 30's... I'll have to figure out what values make sense for that.

You don't recommend or mentioned having to pull down the AD0 to ground if it's not used right? I thought it's "at zero/low" already when it's not connected as far as making sure something shows up as 68 vs. 69.

Yeah... so I bought these sensors labeled as 9250 from Amazon however the board appears to be for both, the boards are supposed to check which one they are and in my case they're blank(9250/6500). I think the SMBUS error I'm having is because the magnetometer isn't there, hence why it doesn't show up in the address grid... that's great.

Anyway I think this is good. I'm pretty sure I actually received 6500's as the 0x0C address doesn't show up. I just re-tested all four. I see Arduino forums mentioning that address for the mag. I don't know if I have to mess around with registers to get them to show up? The (x, y, z) values are not 0 though. Right now I can't tell if they're accurate(don't understand raw mag values). But I guess if I really needed it to matter/be accurate I'd just buy better/dedicated sensors vs. cheaper/all in one solutions like this one.

@jdc-cunningham

  • One of them the gravity was fixed to -8
    I also had this type of problem with some MPUs I bought, but calibrating the lib should solve this.

  • I will have to check that out, I would be concerned if future updates would overwrite that change?
    No need to worry, it's just a simple change to see if anything changes. In my tests smbus2 was better than smbus, but the last one is more widely used. Switching between libraries does not cause any changes to the code.

try:
    # smbus2 is faster than smbus
    # import smbus <-- UNCOMMENT THIS
    import smbus2 as smbus <-- COMMENT THIS
except ImportError:
    print("\n", "Using Fake SMBus", "\n", "Install requirements.", "\n")
    from mpu9250_jmdev.fake_smbus import FakeSmbus as smbus
  • I am concerned if you'd have to re-enable i2c everytime/reupdate but maybe that's how it goes with my setup.
    It is not necessary, I just asked this time for the settings to be reset, in case there is a problem.

  • that is interesting because I actually have rarely seen that address except one time and I thought it was a fluke/bug
    If sometimes it appears and sometimes it doesn't, it can be a problem with the cables. Have you tried changing the jumper cables to see if it works?

  • You don't recommend or mentioned having to pull down the AD0 to ground if it's not used right? I thought it's "at zero/low" already when it's not connected as far as making sure something shows up as 68 vs. 69.
    When AD0 is connected, the MPU address goes to 0x69. Otherwise, it is 0x68. As the I2C of your RPi is showing 0x69, have you tried to feed the AD0 with 3.3V and instantiate the object with address MPU9050_ADDRESS_69?

  • The AK address 0x0C will not appear if BYPASS_EN disabled (INT_PIN_CFG, 0x00) and Master Enabled (USER_CTRL, 0x20).

Can you provide your source code and a photo of the MPU?

To be clear, if I pull out a brand new sensor, plug it in, enable i2C, use sudo... I should not expect the 0x0C magnetometer address to show up by default?

Thanks for the sample code related to smbus

I've attached the four sensors I own/tested. I did look at pictures of each board 9250/6050 and they look exactly the same so it's not impossible I got 6050's. However... the Magnetometer values are not 0. correction - the 6050's are different in size(smaller) and they are labeled differently eg. ITG/MPU.

Have you tried changing the jumper cables to see if it works?

Are you talking about switching like the actual wire material? That would be interesting... in my latest test run all four of the sensors did not show the 0x0C address.

As the I2C of your RPi is showing 0x69, have you tried to feed the AD0 with 3.3V

No I have not tried that(the 3.3V). I was just changing the mpu.address_mpu_master like you mentioned above and re-calibrating.

AK address 0x0C will not appear if BYPASS_EN disabled (INT_PIN_CFG, 0x00) and Master Enabled (USER_CTRL, 0x20)

How would you check this? Use the get_register() method?

My source code is pretty much what you have but I'm feeding the data to a websocket server to display on a websocket client. Although I added the CLI-args so I can adapt to the board that I'm swapping.

Thanks

The orange caps are from 2021, others are from 2019

sensors

@jdc-cunningham

Analyzing your code, it seems to me that the problem is much simpler than what we are talking about.

To be clear:

  • If you run your actual code without calibrating, you can get values from the sensors.
  • If you try to calibrate, the error occurs.

Am I right?

  • I checked your code and it has some problems. In your case to change the address, you really need to reinstate (changing address_mpu_master in your case does not solve it). It is also missing to configure the sensor after instantiating. Below your code with the corrections:
# based on
# https://github.com/Intelligent-Vehicle-Perception/MPU-9250-Sensors-Data-Collect

from dotenv import load_dotenv
load_dotenv()

import os, sys
import time
import asyncio
import websockets
from mpu9250_jmdev.registers import *
from mpu9250_jmdev.mpu_9250 import MPU9250

# these are primarily for calibrating and adapting to the sensor board if it has problems
# ex. $python3 sensor-read-websocket-server.py
# ex. $python3 sensor-read-websocket-server.py all
# ex. $python3 sensor-read-websocket-server.py mpu 69
def get_cli_args(name='default', calibrate='', address=''):
  
  global mpu

  mpu = MPU9250(
    address_ak=AK8963_ADDRESS,
    address_mpu_master=MPU9050_ADDRESS_69 if address == '69' else MPU9050_ADDRESS_68,
    address_mpu_slave=None,
    bus=1,
    gfs=GFS_1000,
    afs=AFS_8G,
    mfs=AK8963_BIT_16,
    mode=AK8963_MODE_C100HZ
  )

  mpu.configure() # apply settings to registers

  if (address == '69'):
    print('change bus address to 69')

  if (calibrate == 'all'):
    print('calibrating...')
    mpu.calibrateMPU6500()
    mpu.calibrateAK8963()
    mpu.configureMPU6500(mpu.gfs, mpu.afs)
    mpu.configureAK8963(mpu.mfs, mpu.mode)
  elif (calibrate == 'mpu'):
    print('calibrating...')
    mpu.calibrateMPU6500()
    mpu.configureMPU6500(mpu.gfs, mpu.afs)
  elif (calibrate == 'mag'):
    print('calibrating...')
    mpu.calibrateAK8963()
    mpu.configureAK8963(mpu.mfs, mpu.mode)

# check for cli args related to calibration
get_cli_args(*sys.argv)

async def streamMpuData(websocket, path):
  while True:
    accelData = mpu.readAccelerometerMaster()
    gyroData = mpu.readGyroscopeMaster()
    magData = mpu.readMagnetometerMaster()
    # tmpData = mpu.readTemperatureMaster() # garbage

    # this is super ugly
    dataStr = (
      str(accelData[0]) + "," +
      str(accelData[1]) + "," +
      str(accelData[2]) + "," +
      str(gyroData[0]) + "," +
      str(gyroData[1]) + "," +
      str(gyroData[2]) + "," +
      str(magData[0]) + "," +
      str(magData[1]) + "," +
      str(magData[2])
    )

    await websocket.send(
      dataStr
    )

    time.sleep(0.1)

# websocket server
# https://websockets.readthedocs.io/en/stable/intro.html (browser-based example)
# PI_ADDR if internal would be a 192... type address
start_server = websockets.serve(streamMpuData, os.getenv("PI_ADDR"), os.getenv("PI_SOCKET_PORT"))

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

Can you test this code?

Oh wow... I was not even aware of these lines:

mpu.configureMPU6500(mpu.gfs, mpu.afs)
mpu.configureAK8963(mpu.mfs, mpu.mode)

I didn't see them on the home page/README about calibration. Granted they may be ran if you just use the mpu.calibrate()

Yeah I mean generally the calibration seems to help. As in for the sensors that are not stuck with G in -7/-8 it will make it get very close to 1 as expected.

address_mpu_master=MPU9050_ADDRESS_69 if address == '69' else MPU9050_ADDRESS_68

Oh that's nice, I like that, automatically checks. In the past I had just re-instantiated it, but your assignment approach from a response earlier seemed cleaner.

Thanks a lot for the fixes.

I tested one and I think we're good. I still have the OSError [Errno 121] error but I think that's because of the bad magnetometer; it happens if I try to calibrate mpu.calibrateAK8963(). At some point I'll investigate on my own what's up with the magnetometer and the things you mentioned above about:

BYPASS_EN disabled (INT_PIN_CFG, 0x00) and Master Enabled (USER_CTRL, 0x20)

But yeah thanks for your time. I'll close this if you have nothing else to add.

@jdc-cunningham

  • I didn't see them on the home page/README about calibration. Granted they may be ran if you just use the mpu.calibrate():
    In general, all sensors are configured and calibrated in a single call. In your particular case you do this separately, although it is not common. See the code below:
 mpu.configure() --> calls mpu.configureMPU6500(mpu.gfs, mpu.afs) and mpu.configureAK8963(mpu.mfs, mpu.mode)
 mpu.calibrate() --> calls mpu.calibrateMPU6500() and mpu.calibrateAK8963()
  • About BYPASS_EN disabled (INT_PIN_CFG, 0x00) and Master Enabled (USER_CTRL, 0x20)
    These registers are automatically determined when you call mpu.configure() or mpu.configureMPU6500(mpu.gfs, mpu.afs) and mpu.configureAK8963(mpu.mfs, mpu.mode). Don't worry about them.

  • I still have the OSError [Errno 121]
    I recommend that you run mpu.reset() and restart RPi.

Alright thanks a lot for the help and making/maintaining this library.
I'll close this out.