UART-RVC mode¶
Heading, pitch, roll and acceleration at 100 Hz over one UART line, with no protocol stack and no commands.
UART-RVC is a simplified output mode CEVA designed for ground robots such as robot vacuum cleaners. The hub sends a fixed 19-byte frame every 10 ms and listens to nothing. You cannot choose reports, change the rate or update firmware in this mode; for any of that, use SH-2 over I2C, SPI or UART.
Select it¶
Strap PS1 low and PS0 high, then reset (datasheet figure 1-5). The UART runs at 115200 bit/s, 8 data bits, no parity, 1 stop bit, and needs the external 32.768 kHz crystal or clock: the internal oscillator is not accurate enough. Only the chip's TX pin (pin 20) is used; connect it to your MCU's RX.
Use it¶
The port reports BNO085Interface::UARTRVC and returns received bytes from Read() without blocking:
class MyRvcUart : public bno08x::CommInterface<MyRvcUart> {
public:
static constexpr int kRstnGpio = 16; // NRST, active low (optional)
BNO085Interface GetInterfaceType() noexcept {
return BNO085Interface::UARTRVC;
}
bool Open() noexcept {
return platform_uart_open(115200);
}
void Close() noexcept {
platform_uart_close();
}
// RVC is transmit-only on the sensor side: the driver never writes.
int Write(const uint8_t* /*data*/, uint32_t /*length*/) noexcept {
return 0;
}
// Non-blocking: whatever the UART has received, up to length bytes. ServiceRvc() calls it until
// it returns 0.
int Read(uint8_t* data, uint32_t length) noexcept {
return platform_uart_read(data, length);
}
// No interrupt line in RVC mode.
bool DataAvailable() noexcept {
return true;
}
void Delay(uint32_t ms) noexcept {
platform_delay_ms(ms);
}
uint32_t GetTimeUs() noexcept {
return platform_micros();
}
bool GpioSet(bno08x::CtrlPin pin, bno08x::GpioSignal signal) noexcept {
if (pin == bno08x::CtrlPin::RSTN) {
return platform_gpio_write(kRstnGpio, signal != bno08x::GpioSignal::ACTIVE);
}
return true; // only NRST is wired
}
};
int stream_heading(MyRvcUart& uart) {
BNO085<MyRvcUart> imu(uart);
imu.SetRvcCallback([](const RvcSensorValue& v) {
std::printf("#%u yaw %.2f pitch %.2f roll %.2f deg a %.3f %.3f %.3f g\n",
static_cast<unsigned>(v.index), v.yaw_deg, v.pitch_deg, v.roll_deg, v.acc_x_g,
v.acc_y_g, v.acc_z_g);
});
if (!imu.BeginRvc()) { // opens the UART; fails unless the port reports UARTRVC
return imu.GetLastError();
}
for (;;) {
imu.ServiceRvc(); // parse what has arrived; one frame every 10 ms
uart.Delay(5);
}
}
BeginRvc() opens the port and starts the parser; it fails with SH2_ERR_BAD_PARAM on any other interface type.
ServiceRvc() reads every byte that has arrived and calls your callback once per valid frame. CloseRvc() (or
Close()) closes the port. SH-2 calls on an RVC object fail the same way, so one object cannot mix the modes.
Frame format¶
| Offset | Bytes | Field | Unit | RvcSensorValue |
|---|---|---|---|---|
0 |
2 | Header 0xAA 0xAA |
||
2 |
1 | Index, counts 0 to 255 | index |
|
3 |
2 | Yaw, little-endian int16, ±180° |
0.01° | yaw_deg |
5 |
2 | Pitch, ±90° | 0.01° | pitch_deg |
7 |
2 | Roll, ±180° | 0.01° | roll_deg |
9 |
2 | X acceleration | mg | acc_x_g |
11 |
2 | Y acceleration | mg | acc_y_g |
13 |
2 | Z acceleration | mg | acc_z_g |
15 |
1 | Motion intent (BNO086; reserved otherwise) | mi |
|
16 |
1 | Motion request (BNO086; reserved otherwise) | mr |
|
17 |
1 | Reserved, zero | ||
18 |
1 | Checksum: 8-bit sum of bytes 2 to 17 |
Yaw is measured from the orientation at reset. Apply the rotations in the order yaw, pitch, roll. The driver
converts to degrees and g (RvcSensorValue) and stamps each frame with GetTimeUs().
The datasheet's example frame, AA AA DE 01 00 92 FF 25 08 8D FE EC FF D1 03 00 00 00 E7, decodes to index 222,
yaw 0.01°, pitch −1.10°, roll 20.85° and acceleration (−0.371, −0.020, 0.977) g. The host tests feed exactly this
frame to the parser (Testing and CI).
How the parser finds frames¶
The parser keeps a 19-byte sliding window. Each byte shifts the window; when it starts with 0xAA 0xAA and the
checksum matches, the frame is decoded, the callback runs and the window empties. A frame with a wrong checksum is
skipped and the window keeps sliding, so the parser recovers by itself after noise or a partial frame.
Next¶
Porting has the port's contract; the rvc_mode ESP32 example runs it
on hardware.