BB.Sensor.BMI323 (bb_sensor_bmi323 v0.1.0)

Copy Markdown View Source

A BB sensor that publishes BB.Message.Sensor.Imu messages from a Bosch BMI323 6-DoF inertial measurement unit (3-axis accelerometer + 3-axis gyroscope) over I2C.

The BMI323 has no magnetometer, so this sensor cannot determine orientation on its own — every published Imu carries BB.Math.Quaternion.identity/0 for the orientation field. You almost always want to pair this sensor with an orientation estimator such as the ones in bb_estimator_ahrs (Madgwick, Mahony, Complementary). See Pairing with an AHRS estimator.

Modes

Pick based on output data rate:

  • :polling — periodically calls BMI323.read_imu/1 at publish_rate. Low-overhead, low-jitter at modest rates. Use when ODR ≤ 200 Hz. Above ~200 Hz the BEAM scheduler starts to lose samples between polls.
  • :interrupt — runs BMI323.Sampler which buffers samples in the chip's on-chip 2 KB FIFO and fires the host's INT1 GPIO when a configurable watermark is reached. Reliable up to the chip's 6.4 kHz ODR. Requires INT1 wired to a host GPIO.

In interrupt mode, samples arrive in bursts of watermark_frames at once. With ODR 800 Hz and watermark 8, you'll see batches every 10 ms. Downstream consumers should be designed to handle a burst (an estimator like the AHRS filters integrates each sample with its own dt, so it copes naturally).

Coordinate frame

Axes are the chip's own +X / +Y / +Z (see the BMI323 datasheet §3.2 for the silkscreen orientation). The BB topology entity this sensor attaches to is its coordinate frame — orient the IMU on the link as appropriate and apply a static transform in your downstream consumer if the chip mounting axes don't match the link axes.

Example DSL Usage

topology do
  link :base do
    sensor :imu, {BB.Sensor.BMI323,
      bus: "i2c-1",
      address: 0x68,
      mode: :polling,
      accelerometer_range: 8,
      accelerometer_odr: 200,
      gyroscope_range: 2000,
      gyroscope_odr: 200,
      publish_rate: ~u(100 hertz)
    }
  end
end

Or in interrupt mode, with INT1 wired to GPIO 17:

sensor :imu, {BB.Sensor.BMI323,
  bus: "i2c-1",
  address: 0x68,
  mode: :interrupt,
  int1_pin: 17,
  accelerometer_range: 8,
  accelerometer_odr: 800,
  gyroscope_range: 2000,
  gyroscope_odr: 800,
  watermark_frames: 8
}

Pairing with an AHRS estimator

The BMI323 produces raw acceleration + angular-velocity samples; turning those into an orientation needs sensor fusion. Attach an estimator from bb_estimator_ahrs:

sensor :imu, {BB.Sensor.BMI323, bus: "i2c-1", ...} do
  estimator :orientation, {BB.Estimator.Ahrs.Madgwick, beta: 0.1}
end

The estimator subscribes to this sensor's Imu messages, replaces the identity quaternion with a fused orientation, and republishes. See BB.Estimator.Ahrs.Madgwick, BB.Estimator.Ahrs.Mahony, and BB.Estimator.Ahrs.Complementary for the algorithm choices.

Options

  • bus — I2C bus name (e.g. "i2c-1") — required.
  • address — I2C address (0x68 or 0x69, default 0x68).
  • mode:polling or :interrupt (default :polling).
  • accelerometer_range2 | 4 | 8 | 16 g (default 8).

  • accelerometer_odr — output data rate in Hz (default 200).
  • accelerometer_mode:disabled | :low_power | :normal | :high_performance (default :normal). Setting either axis to :disabled powers it down — the IMU will publish constant / invalid values for that axis until the mode is changed back.

  • gyroscope_range125 | 250 | 500 | 1000 | 2000 °/s (default 2000).

  • gyroscope_odr — output data rate in Hz (default 200).
  • gyroscope_mode — as for accelerometer (default :normal).
  • publish_rate — polling rate (default ~u(100 hertz)). Ignored in interrupt mode.
  • int1_pin — GPIO pin number wired to BMI323's INT1. Required when mode: :interrupt.
  • watermark_frames — FIFO frames per interrupt (default 8). Interrupt mode only.

Published Messages

BB.Message.Sensor.Imu published to [:sensor | path] where path is the sensor's position in the topology. Fields:

Runtime parameter changes

Options handled live (no restart):

Options that trigger {:stop, :reconfigure} (supervisor restarts the sensor with new params):

  • mode, bus, address, int1_pin, watermark_frames.

Error handling

A single failed read or reconfiguration is logged at warning level and does not crash the process — the polling loop or interrupt handler continues. Persistent failures will manifest as silence on the topic rather than a crash.

Troubleshooting

  • {:stop, {:chip_id_mismatch, got: id, expected: 0x43}} — the device at the configured I2C address is not a BMI323. Check address (0x68 when SDO is tied to GND, 0x69 when tied to VDDIO), the physical bus, and that the chip is actually powered.
  • {:stop, :no_such_bus} from Wafer.Driver.Circuits.I2C.acquire/1 — the bus string doesn't match any /dev/i2c-* device. On Linux list available buses with i2cdetect -l.
  • {:stop, :int1_pin_required_for_interrupt_mode}mode: :interrupt was set without an int1_pin.
  • GPIO acquire failure in interrupt mode — the pin may already be exported, owned by another process, or not exist on this board.
  • Constant / silently-wrong samples after changing *_mode — check you didn't leave an axis on :disabled; that powers it down.