Java API summary#
The everyday tier, which is what most teams need. Every method validates its arguments and throws a message that names the fix rather than an array index exception.
Getting the device#
BBRDigitalExpander exp = hardwareMap.get(BBRDigitalExpander.class, "expander");
Encoders#
| Method | Returns |
|---|---|
getEncoderCount(channel) |
Signed accumulated counts |
getEncoderVelocity(channel) |
Signed counts per second, computed on the board |
getPulseWidthUs(channel) |
Pulse width in µs (PULSE_WIDTH mode only) |
resetEncoder(channel) |
— |
resetAllEncoders() |
— |
Sensors#
| Method | Returns |
|---|---|
isSensorConnected(port) |
boolean |
getSensorType(port) |
COLOR, DISTANCE, or EMPTY |
getColorClass(port) |
0 for no match, else the colour slot 1–7 |
seesColor(port, classSlot) |
boolean |
getDistanceMm(port) |
Distance in mm |
Teaching and triggers#
All of these save to flash automatically, which is why they are setup calls: run them once, or on a button press, never in a match loop. See Set up once.
| Method | Effect |
|---|---|
teachColor(port, classSlot) |
Learn the colour currently in view |
triggerOnColor(output, port, classSlot) |
Output high while that colour is seen |
triggerWhenNear(output, port, maxMm) |
Output high while something is within range |
triggerWhenEncoderPast(output, channel, counts) |
Output high past a threshold |
triggerWhenFacing(output, headingDeg, toleranceDeg) |
Output high while facing that heading |
setEncoderDirection(channel, direction) |
FORWARD or REVERSE for one channel |
setEncoderVelocityWindowMs(ms) |
Window for all encoder velocities, 1–255 ms (default 25): longer is smoother, shorter reacts faster |
clearOutputLatch(output) |
Un-latch one output |
clearAllOutputLatches() |
Un-latch all four |
Heading and pose#
These throw rather than return a heading or pose the board cannot stand behind.
| Method | Returns |
|---|---|
getHeading() |
Degrees, CCW positive |
resetHeading() |
— |
calibrateGyro() |
boolean — false if the robot moved; the previous bias is kept (~1 s, keep it still) |
getPose() |
Pose2D — mm, radians, +X forward, +Y left |
setPose(pose) |
— |
resetLocalizerAndCalibrateImu() |
boolean — false if the robot moved; the pose still resets and the localizer still starts |
waitForLocalizerReady(timeoutMs) |
boolean |
readImu() |
ImuState |
Mounting and direction#
Set once when the board is bolted on. Unlike setEncoderDirection() above, nothing here auto-saves — follow with saveConfigToFlash().
| Method | Effect |
|---|---|
getEncoderDirection(channel) |
Which way that channel counts |
getEncoderVelocityWindowMs() |
Current encoder velocity window in ms |
setEncoderInvertMask(mask) |
All four at once; bit per channel, set = inverted |
getEncoderInvertMask() |
Current mask |
setImuAxisUp(axis) |
Which board axis points up (BBRRegMap.AXIS_*, flat is AXIS_POS_Z) |
getImuAxisUp() |
Current axis-up setting |
Board identity and health#
| Method | Returns |
|---|---|
getFirmwareVersion() |
Firmware on the board, e.g. "1.1.1" |
getCapabilities() |
Capability bits |
isConfigDirty() |
Are there unsaved config changes |
isDataFresh() |
Did the last read actually reach the device |
getRebootCount() |
Reboots seen since the Robot Controller app started |
readDiagnostics() |
Why the board last reset, and I2C bus-trouble counters (firmware 1.1.0+) |
setReadFailurePolicy(policy) |
KEEP_RUNNING (default) or STOP_OPMODE when reads keep failing |
Advanced tier#
The full register map is available underneath: configureOutput(), writeColorClass(), writeDistanceClass(), setChannelMode(), setPwmChannelParams(), setLocalizerParams(), readTelemetry(), runCommand().
Nothing in the advanced tier saves automatically. Call saveConfigToFlash(), or the configuration is gone at power-off. isConfigDirty() tells you whether you have unsaved changes.
Behaviour worth knowing#
Init refuses loudly. A wrong device ID or an unsupported protocol version throws at init rather than producing corrupt data later.
One transaction per snapshot. The everyday getters share a snapshot refreshed roughly every 10 ms, so four questions in one loop iteration cost one transaction and the answers are mutually consistent.
Commands are idempotent. Every command carries a token, so an I2C retry cannot execute it twice.
Config writes are verified by readback. The bus layer never NAKs, so an out-of-range value would otherwise be silently ignored.
IMU access throws when the gyro cannot be trusted rather than returning zeros, because a heading of 0.00 that never changes is indistinguishable from working software.
Failed reads serve the last known good value. When a read cannot reach the device — or, for the localizer, when three reads in a row fail their checksum — the driver returns the previous snapshot and isDataFresh() goes false, rather than throwing. This is what stops a normal OpMode shutdown, or a burst of bus noise mid-match, from detonating your loop. A long streak of failures (over half a second) logs one warning to the Robot Controller log and keeps going, so anything that acts on a reading, like a flywheel controller, should check isDataFresh() first. For bench testing, setReadFailurePolicy(ReadFailurePolicy.STOP_OPMODE) makes that streak throw instead.
Telemetry is checksummed on firmware 1.1.0 and later. Encoder, sensor and localizer readings that arrive damaged are re-read, so a corrupted value never reaches your code looking real. Older firmware has no checksum on the encoder and sensor block.
Reboots are logged. If the board restarts, the driver notices its uptime clock jump backwards, logs expander REBOOTED with the reason (firmware 1.1.0+), and counts it in getRebootCount().
BBRTransportException means the read never reached the device, not that anything is broken. The SDK tears the I2C device down when an OpMode stops, so a read already in flight comes back empty. A polling loop should catch it and break.