Skip to content

API guide

How to do the common jobs with the driver: start the hub, enable reports, read them, stop, and handle errors.

Every snippet on this page is compiled by the host CI from tests/docs/api_tasks.cpp, with Imu standing for BNO085<MyI2c>. The API reference lists every member.

The shape of the API

One BNO085<Comm> object per sensor. Its calls fall into four groups; the port's GetInterfaceType() decides which groups work:

Group Calls Interface
SH-2 session Begin(), Close(), Update() I2C, SPI, UART
Reports EnableSensor(), DisableSensor(), SetCallback(), HasNewData(), GetLatest() I2C, SPI, UART
UART-RVC BeginRvc(), ServiceRvc(), SetRvcCallback(), CloseRvc() UARTRVC (UART-RVC mode)
Firmware update EnterBootloader(), Dfu(), DfuFromMemory(), RunDfuFromMemory(), ExitBootloaderAndReboot() I2C, SPI, UART (Firmware update)
Pins and state HardwareReset(), SetBootPin(), SetWakePin(), SelectInterface(), GetState(), GetLastError() any

Calls that can fail return bool (or an SH-2 status int for firmware updates) and never throw. After a false, GetLastError() holds the reason as an SH2_ERR_* code.

Start the hub

bool start_imu(Imu& imu) {
  imu.HardwareReset(); // optional: NRST low 2 ms, then 94 ms for the hub to boot
  if (!imu.Begin()) {
    std::printf("Begin failed: %d\n", imu.GetLastError()); // SH2_ERR_* (sh2_err.h)
    return false;
  }
  return imu.GetState() == BNO085DriverState::Sh2Active;
}

Begin() opens your port, starts the CEVA SH-2 library, waits up to 200 ms for the hub's reset message and sends a reinitialise command. Calling it again while the session is open returns true and does nothing.

Enable reports

bool enable_reports(Imu& imu) {
  bool ok = true;
  ok &= imu.EnableSensor(BNO085Sensor::GameRotationVector, 5);  // 200 Hz, no magnetometer
  ok &= imu.EnableSensor(BNO085Sensor::LinearAcceleration, 10); // 100 Hz
  ok &= imu.EnableSensor(BNO085Sensor::Gyroscope, 10);          // 100 Hz
  ok &= imu.EnableSensor(BNO085Sensor::StepCounter, 0);         // on change
  ok &= imu.EnableSensor(BNO085Sensor::TapDetector, 0);         // on change
  return ok;
}

EnableSensor(sensor, interval_ms, sensitivity) takes the report period in milliseconds; 0 suits on-change reports such as the step counter. The hub picks a rate between 0.9 and 2.1 times the one you ask for, and not every report can run at its maximum at once. The driver remembers each interval and re-sends it when the hub reports a reset. Configuration lists the report types and their maximum rates.

Read reports with a callback

void use_callbacks(Imu& imu) {
  imu.SetCallback([](const SensorEvent& e) {
    switch (e.sensor) {
    case BNO085Sensor::GameRotationVector:
      std::printf("q %.3f %.3f %.3f %.3f\n", e.rotation.w, e.rotation.x, e.rotation.y,
                  e.rotation.z);
      break;
    case BNO085Sensor::StepCounter:
      std::printf("steps %u\n", static_cast<unsigned>(e.stepCount));
      break;
    case BNO085Sensor::TapDetector:
      std::printf("%s tap\n", e.tap.doubleTap ? "double" : "single");
      break;
    default:
      break;
    }
  });
  for (;;) {
    imu.Update(); // the callback runs from here, on this thread
  }
}

The callback runs inside Update(), on the thread that calls it, once per report. Keep it short: copy the data out. SensorEvent is one struct for every report type; the fields that matter depend on e.sensor:

Report Field Unit
Rotation vectors (RotationVector, GameRotationVector, GeomagneticRotationVector, ARVRStabilized*) rotation (w, x, y, z, accuracy) unit quaternion
GyroIntegratedRV rotation, angularVelocity quaternion, rad/s
Accelerometer, LinearAcceleration, Gravity vector m/s²
Gyroscope / GyroUncalibrated vector (+ bias) rad/s
Magnetometer / MagneticFieldUncalibrated vector (+ bias) µT
RawAccelerometer, RawGyroscope, RawMagnetometer raw ADC counts
Pressure, AmbientLight, Humidity, Proximity, Temperature scalar hPa, lux, %, cm, °C
StepCounter stepCount, latencyUs steps, µs
TapDetector tap (doubleTap, direction)
StabilityClassifier, PersonalActivityClassifier classification, activity
Gesture detectors (shake, flip, pickup, tilt, ...) detected, eventFlags

Read reports by polling

void poll(Imu& imu) {
  imu.Update();
  if (imu.HasNewData(BNO085Sensor::LinearAcceleration)) {
    const SensorEvent e = imu.GetLatest(BNO085Sensor::LinearAcceleration); // clears the flag
    std::printf("a %.2f %.2f %.2f m/s^2 (accuracy %u)\n", e.vector.x, e.vector.y, e.vector.z,
                static_cast<unsigned>(e.vector.accuracy));
  }
}

HasNewData() is set when a report arrives and cleared by GetLatest(). The callback does not clear it, so you can mix both styles. Outside an SH-2 session HasNewData() returns false and GetLatest() a default event.

Stop reports and close

void stop(Imu& imu) {
  imu.DisableSensor(BNO085Sensor::Gyroscope); // report interval 0: the hub stops it
  imu.Close();                                // sh2_close(), then the port's Close()
}

The destructor closes an open SH-2 or RVC session too. Close the session before you power the sensor down.

Handle errors

const char* sh2_error_name(int code) {
  switch (code) {
  case SH2_OK:
    return "ok";
  case SH2_ERR:
    return "general error (transport did not open, hub did not answer)";
  case SH2_ERR_BAD_PARAM:
    return "bad parameter, or a call for the wrong interface type";
  case SH2_ERR_OP_IN_PROGRESS:
    return "wrong driver state (not started, or another mode is active)";
  case SH2_ERR_IO:
    return "bus error";
  case SH2_ERR_HUB:
    return "the hub reported an error";
  case SH2_ERR_TIMEOUT:
    return "the hub did not answer in time";
  default:
    return "unknown";
  }
}

bool enable_or_report(Imu& imu) {
  if (!imu.EnableSensor(BNO085Sensor::RotationVector, 10)) {
    std::printf("EnableSensor: %s\n", sh2_error_name(imu.GetLastError()));
    return false;
  }
  return true;
}
Signal Meaning Where to look
Begin() is false, SH2_ERR The port's Open() failed, or the SH-2 library could not start Bus pins, address, power
SH2_ERR_TIMEOUT The hub did not answer a command in time H_INTN wiring, Read() contract, PS0/PS1 straps
SH2_ERR_BAD_PARAM Wrong interface for the call (SH-2 call on UART-RVC, or the reverse), or a bad DFU image GetInterfaceType()
SH2_ERR_OP_IN_PROGRESS Wrong state: reports before Begin(), a call during a firmware update GetState()
BNO08X_ERR_SH2_SESSION_IN_USE (-100) Begin() refused: another BNO085 object holds the one SH-2 session of the process Close() the other object first (Architecture)
accuracy 0 or 1 Calibration not settled yet Move the device; keep it away from magnetic fields
Reports stop, then resume The hub reset; the driver re-enabled your reports Supply, NRST, ESD

Troubleshooting maps symptoms to causes.

Control pins

HardwareReset(lowMs) holds NRST active for lowMs (default 2 ms) and then waits RESET_STARTUP_MS (94 ms, the start-up time of datasheet figure 6-8). SetBootPin(true) asserts BOOTN (low) for the bootloader; SetWakePin(true) asserts WAKE in SPI mode. Each forwards to your port's GpioSet(), so a pin the board does not wire is a no-op. All four return false when the port's GpioSet() reports a GPIO error (Porting). Hardware has the levels.