BNO055 I2C Intermittent Problems

photomankc
Posts: 10
Joined: Tue Dec 05, 2017 2:16 pm

BNO055 I2C Intermittent Problems

Postby photomankc » Wed Dec 13, 2017 7:24 pm

I have been fighting with an I2C based IMU unit, the BNO055 from Adafruit. After running into random issues reading results in an larger application I decided to create a test application and board just for the BNO055 object I'm writing. That has allowed me to zero in on two problems that seem to reoccur at random intervals. One seems like it may be external, while the other definitely seems to be internal to the Arduino I2C library interface.

The issue that i'm most concerned about is the second. After minutes to hours of stable operation, the reads to the device will begin to fail. I was finally able to add enough error checking and instrumentation to close on what was going wrong. My code checks the results of the register write and the bytes read and stores a read or write error code based on what went wrong.

The results indicate that my read() function sees a failure to read the number of bytes specified and returns an error code of -18 to indicate this. Looking at the bus with a logic analyzer shows no activity as SCL never falls from high to low. This also produces a condition where the bytes returned from read() are always 0xFF. As I have no code that would alter the bytes on error this must be happening in the library code.

Sometimes this condition can clear in a reset, but often it does not. Wire.reset() has no effect and writes nothing to the bus while this condition persists. Often, only a power cycle will clear this condition.


This is the output showing the moment the condition occurs:
Secs.... Err..Heading..Pitch..Roll.....Temp

Code: Select all

1394.59  0  H 346.81  P 0.94  R -0.19  T 28
1399.73  0  H 346.81  P 0.94  R -0.19  T 28
1404.87  0  H 346.81  P 0.94  R -0.19  T 28
1410.01  0  H 346.81  P 0.94  R -0.19  T 28
1415.15  0  H 346.81  P 0.94  R -0.19  T 28
1420.29  0  H 346.81  P 0.94  R -0.19  T 28
1425.43  0  H 346.81  P 0.94  R -0.19  T 28
1430.57  0  H 346.81  P 0.94  R -0.19  T 28
1434.53^ -18  H 0  P 0  R 0
1435.68  -18  H 0.00  P 0.00  R 0.00  T -1
1440.68  -18  H 0.00  P 0.00  R 0.00  T -1
1445.68  -18  H 0.00  P 0.00  R 0.00  T -1
1450.68  -18  H 0.00  P 0.00  R 0.00  T -1
1455.68  -18  H 0.00  P 0.00  R 0.00  T -1
1460.68  -18  H 0.00  P 0.00  R 0.00  T -1
1465.68  -18  H 0.00  P 0.00  R 0.00  T -1
1470.68  -18  H 0.00  P 0.00  R 0.00  T -1
1475.68  -18  H 0.00  P 0.00  R 0.00  T -1
The -18 is the error code return value being recorded in the IMU object which indicates Wire.requestFrom() did not get the number of bytes we requested. The sensor is read 20 times per second and prints occur every 5 seconds. It also prints out when the total delta of the IMU fields exceeds 1.0 degrees. That actually lets me see the moment the read fails with this line:

Code: Select all

1434.53^ -18  H 0  P 0  R 0
That shows the first error code coming back because the IMU object handled that in in it's update() method by zeroing the object data values, which then triggers the delta output.

At this point the logic analyzer shows no bus activity at all. The CLK line is never toggled again.


Now a reset is attempted:

Code: Select all

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:812
load:0x40078000,len:0
load:0x40078000,len:11392
entry 0x40078a9c

Bosh BNO055 test

    Ping:           0
    BNO055 Present: 0
    Software Rev:   65535
    Boot Rev:       255
    Self Test:      FF
    Calibration:    FF
    Error Status:   255
    System Status:  255
    Units Register: FF
    Data Source:    0
    Operation Mode: 255
Start loop.
1.70  -18  H 0.00  P 0.00  R 0.00  T -1
6.70  -18  H 0.00  P 0.00  R 0.00  T -1
11.70  -18  H 0.00  P 0.00  R 0.00  T -1
16.70  -18  H 0.00  P 0.00  R 0.00  T -1
21.70  -18  H 0.00  P 0.00  R 0.00  T -1
26.70  -18  H 0.00  P 0.00  R 0.00  T -1
31.70  -18  H 0.00  P 0.00  R 0.00  T -1
Here you see the indications that the bus is dead. All reads are returning 0xFF for all bytes. This happens because the register get/set methods do not check for errors from sys_imu.read() before they return the result so that comes directly from the library. The -18 error code continues to be presented indicating incorrect number of bytes read by Wire.requestFrom()

After a power cycle the device may then go on to operate for anywhere from 10 minutes to 4 hours or more. It's been extremely frustrating to try and debug since I can never capture exactly the moment when the bus stops working and the moments preceeding that event. I have some ideas now though that I may try since I can detect this error. I can perhaps trigger the logic analyzer with a free GPIO used to flag detection of the error. That might give some insight, or it might show nothing more than everything working fine until it isn't.


After a power cycle and reset:

Code: Select all

ets Jun  8 2016 00:22:57

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:812
load:0x40078000,len:0
load:0x40078000,len:11392
entry 0x40078a9c
    Ping:           1
    BNO055 Present: 1
    Software Rev:   785
    Boot Rev:       21
    Self Test:      F
    Calibration:    3C
    Error Status:   0
    System Status:  5
    Units Register: 80
    Data Source:    0
    Operation Mode: 8
Start loop.
1.69  0  H 0.00  P 0.00  R 0.00  T 28
1.75^ 0  H 313  P 1  R 0
6.83  0  H 313.44  P 0.62  R -0.19  T 28
I'll post some of the code next.

photomankc
Posts: 10
Joined: Tue Dec 05, 2017 2:16 pm

Re: BNO055 I2C Intermittent Problems

Postby photomankc » Thu Dec 14, 2017 6:38 pm

I figured out the external issue I mentioned before I completely verified the new code to flag errors on GPIO. A case where the device went missing then returned zeros.

Static - gotta love winter.

I caught it when I sat down, touched the laptop and got a little snap to earth ground. The trigger fired and it was clearly the device resetting just after that moment that caused that type of error (NAK on address). This is easily tested for and recovered from by putting it back into a fusion operation mode after the device is online again from the spurious reset and continue on. It's costly in terms of time at 400 to 700ms, but at least it's recoverable.

You can see spikes appear on the SCL and SDA just before the next read that throws the error flag. Then 20 or so reads fail with NAKs and the device appears again but now reading only zeros.


I'm still waiting to see if I can catch and reproduce the more nasty error where the bus is unresponsive. It ran for 6 hours last night and 5 hours this morning without it cropping up so it seems to know when I'm looking for it. As soon as I have results and know my code operated as intended I'll post that up.
Attachments
return-from-reset.png
return-from-reset.png (84.61 KiB) Viewed 9771 times
static-hit.png
static-hit.png (78.63 KiB) Viewed 9771 times

photomankc
Posts: 10
Joined: Tue Dec 05, 2017 2:16 pm

Re: BNO055 I2C Intermittent Problems

Postby photomankc » Mon Dec 18, 2017 9:11 pm

Code:

Code: Select all

void setup()
{
  Wire.begin(21, 22, 400000);
  Serial.begin(115200);

  pinMode(13, OUTPUT);                // Error flag pin
  pinMode(2,  OUTPUT);                // Activity LED

  delay(1500);

  // Setup OLED display
  oled.init();
  oled.setFont(font8x8_spc);          // Set special 8x8 font
  oled.clearDisplay();                // Clear screen
  oled.setTextXY(0,0);
  oled.putChar(133);
  oled.putChar(134);
  oled.setTextXY(4,0);
  oled.putString("H:000(-000,-000)");  // Put the fully formatted string out.

  init_imu();
}

Code: Select all

void init_imu()
{
  bool    ping = false;

  ping = sys_imu.ping();
  Serial.print("    Ping:           ");
  Serial.println(ping);

  while (!ping)
  {
    delay(250);
    ping = sys_imu.ping();
    Serial.print('.');
  }

  Serial.print("    BNO055 Present: ");
  Serial.println(sys_imu.isPresent());
  digitalWrite(13, HIGH);

  // Put IMU in configuration mode.
  sys_imu.enable(true);
  sys_imu.setOprMode(sys_imu.OPR_MODE_CFG);
  sys_imu.setDataSource(sys_imu.SOURCE_EULER);
  sys_imu.setTempUnits(sys_imu.TEMP_UNIT_C);

  // Put IMU in fusion mode.
  sys_imu.setOprMode(sys_imu.OPR_MODE_IMU);

  // Read IMU configuration values.
  Serial.print("    Software Rev:   ");
  Serial.println(sys_imu.getSoftwareRev());
  Serial.print("    Boot Rev:       ");
  Serial.println(sys_imu.getBootRev());
  Serial.print("    Self Test:      ");
  Serial.println(sys_imu.getSelfTest(), HEX);
  Serial.print("    Calibration:    ");
  Serial.println(sys_imu.getCalStatus(), HEX);
  Serial.print("    Error Status:   ");
  Serial.println(sys_imu.getSysError());
  Serial.print("    System Status:  ");
  Serial.println(sys_imu.getSysStatus());
  Serial.print("    Units Register: ");
  Serial.println(sys_imu.getUnits(), HEX);
  Serial.print("    Data Source:    ");
  Serial.println(sys_imu.getDataSource());
  Serial.print("    Operation Mode: ");
  Serial.println(sys_imu.getOprMode());
}

Code: Select all

void loop()
{
  static int          loop_count;
  static EulerVector  prev_euler;

  uint                print_count = 100;
  EulerVector         euler;

  double  h_delta;
  double  p_delta;
  double  r_delta;
  double  total_delta;

  Serial.print("Start loop.\n");
  while(1)
  {
    digitalWrite(2, HIGH);
    sys_imu.update();

    if (sys_imu.rx_errs or sys_imu.tx_errs)
    {
      digitalWrite(13, LOW);
      
      /* Serial output for error counts and values */
     
      sys_imu.rx_errs = 0;
      sys_imu.tx_errs = 0;
      init_imu();
      delay(1000);
    }
    else
    {
      euler = sys_imu.get();
      h_delta      = abs(prev_euler.heading - euler.heading);
      p_delta      = abs(prev_euler.pitch - euler.pitch);
      r_delta      = abs(prev_euler.roll - euler.roll);
      total_delta  = h_delta + p_delta + r_delta;

      if (total_delta > 2.0)
      {
        // Update 1 line of the OLED with delta information.
        oled.clearLine(4,0);
        oled.setTextXY(4,0);
        oled.putString("H:");
        oled.putNumber((int)(euler.heading + 0.5));
        oled.putString(" (");
        oled.putNumber((int)(euler.pitch + 0.5));
        oled.putString(",");
        oled.putNumber((int)(euler.roll + 0.5));
        oled.putString(")");
        prev_euler = euler;
      }

      if (print_count == 100)
      {
	/* Lots of formatted serial output */
      }

      print_count++;
      loop_count++;

      delay(5);
      digitalWrite(2, LOW);
      delay(45);
    }
  }
}



These are the member functions that are the final destination of any I2C_BNO055 get/set operations that hit the I2C bus. These are the only functions that set the m_lastError variable that records any read/write error event. The other functions up the chain only report false/true (success/fail) if anything at all. The -18 error code reports up from read() based on the fact that there were not as many bytes read as were requested.

Code: Select all

/** @brief write byte sized register on device.
 *
 *  @param uint8_t reg: Register address.
 *  @param uint8_t val: The value to store in the register.
 *  @return int: 0 on success, error otherwise.
 */
inline bool BNO055::write(uint16_t reg, uint8_t* byte, uint count)
{
  int     err     = 0;
  uint    sent    = 0;
  uint8_t lsb     = reg;

  m_lastError  = BNO_OK;

  if (!isReady())
  {
    m_lastError = ERR_BNO_NRDY;
    tx_errs++;
    return true;
  }

  Wire.beginTransmission(m_address);
  sent += Wire.write(lsb);
  sent += Wire.write(byte, count);
  err = Wire.endTransmission();

  // Errors:
  // -21 = Data too long
  // -22 = NAK on address
  // -23 = NAK on data
  // -24 = Other write
  // -25 = Write count mis-match
  if(err)
  {
    m_lastError = ERR_BNO_WRITE - err;
    tx_errs++;
    return true;
  }
  else if(sent != (count+1))
  {
    m_lastError = ERR_BNO_WRITE - 5;
    tx_errs++;
    return true;
  }

  return false;
}

Code: Select all

/** @brief Read data bytes from the I2C bus.
 *
 *  @param uint8_t reg: The register address to read from.
 *  @param uint8_t* buffer: Buffer to read the bytes into.
 *  @param uint count: The number of bytes to be read.
 *  @return bool: 0=success, 1 failure.
 */
inline bool BNO055::read(uint16_t reg, uint8_t* buffer, uint count)
{
  int     err     = 0;
  uint    rx_read = 0;
  uint8_t lsb     = reg;

  m_lastError = 0;

  if (!isReady())
  {
    m_lastError = ERR_BNO_NRDY;
    rx_errs++;
    return true;
  }

  Wire.beginTransmission(m_address);
  Wire.write(lsb);
  err = Wire.endTransmission(false);

  rx_read = Wire.requestFrom(m_address, count, true);
  for (uint8_t i=0; i<count; i++)
  {
    buffer[i] = Wire.read();
  }

  // Errors:
  // -14 = Data too long
  // -15 = NAK on address
  // -16 = NAK on data
  // -17 = Other write
  // -18 = Read count mis-match
  if (err)
  {
    m_lastError = ERR_BNO_READ - err;
    rx_errs++;
    return true;
  }
  if (rx_read != count)
  {
    m_lastError = ERR_BNO_READ - 5;
    rx_errs++;
    return true;
  }

  return false;
}


This has been maddening. I've watched the thing for almost a week and it patently refused to fail. Now today when I don't have the logic analyzer on hand it's failed twice. I know because the LED freezes while on, and that indicates it's now locked in the loop trying to ping the bno055 since it attempts recovery by running init_imu() again.

photomankc
Posts: 10
Joined: Tue Dec 05, 2017 2:16 pm

Re: BNO055 I2C Intermittent Problems

Postby photomankc » Tue Dec 19, 2017 6:07 pm

Here's a copy of the project if anyone has a chance to take a deeper look. I stripped it down to just the stuff used in this example but there are some support headers that had to come with it.

Just set the sketchbook location to the top level folder and it should compile for "Node32s".
Attachments
apptemp.zip
(21.74 KiB) Downloaded 534 times

esimspl
Posts: 1
Joined: Sun Oct 21, 2018 3:26 pm

Re: BNO055 I2C Intermittent Problems

Postby esimspl » Sun Oct 21, 2018 3:28 pm

I'm also trying to use the BNO055 with the esp32 and having very similar problems. Were you ever able to resolve these issues?

Thanks.

goran_andersson
Posts: 1
Joined: Thu Nov 22, 2018 2:28 pm

Re: BNO055 I2C Intermittent Problems

Postby goran_andersson » Thu Nov 22, 2018 2:52 pm

HI!
I am also struggling with the bno055. In order to familiarise my self with the sensor, I am reading the temperature from the accelerometer.

After a few readouts (2- 5) the the communication get stuck:

Chip ID: a0
System Status Code: 00 System Error Code: 00 Selftest: 00
Temperature (Celsius): 25.0 Temperature (Fahrenheit): 78.0
Temperature (Celsius): 25.0 Temperature (Fahrenheit): 78.0

There is 1000ms delay between the temperature readouts.

I also notice that the selftest returns 0x00, would have expected 0x0f.

I strongly suspect the so called "Clock stretching" used by the bno055 being the problem, as being not supported by the Raspberry Pi I am using. (see the datasheet chapter4.6)

Who is online

Users browsing this forum: No registered users and 128 guests