BNO08x Driver
API reference
Loading...
Searching...
No Matches
bno08x.ipp
Go to the documentation of this file.
1
6#ifndef BNO085_HEADER_INCLUDED
7#include "bno08x.hpp"
8#endif
9
10#include <algorithm>
12
13// ============================================================================
16// ============================================================================
17
29template <typename CommType>
31 halWrapper_.comm = &io_;
32 halWrapper_.hal.open = halOpen;
33 halWrapper_.hal.close = halClose;
34 halWrapper_.hal.read = halRead;
35 halWrapper_.hal.write = halWrite;
36 halWrapper_.hal.getTimeUs = halGetTimeUs;
37}
38
39template <typename CommType>
40bool BNO085<CommType>::Begin() noexcept {
41 if (state_ == BNO085DriverState::Sh2Active)
42 return true;
43 if (state_ != BNO085DriverState::Closed) {
44 last_error_ = SH2_ERR_OP_IN_PROGRESS;
45 return false;
46 }
47 if (io_.GetInterfaceType() == BNO085Interface::UARTRVC) {
48 last_error_ = SH2_ERR_BAD_PARAM;
49 return false;
50 }
51
52 // The SH-2 library has one global session; a second sh2_open() would reset the first.
53 if (!acquireSh2Session()) {
55 return false;
56 }
57
58 last_error_ = 0;
59 prepareHalWrapper();
60
61 int status = sh2_open(halWrapper_.asHal(), asyncCallback, this);
62 if (status != SH2_OK) {
63 last_error_ = status;
64 io_.Close();
65 releaseSh2Session();
66 return false;
67 }
68 status = sh2_reinitialize();
69 if (status != SH2_OK) {
70 last_error_ = status;
71 sh2_close();
72 io_.Close();
73 releaseSh2Session();
74 return false;
75 }
76 status = sh2_setSensorCallback(sensorCallback, this);
77 if (status != SH2_OK) {
78 last_error_ = status;
79 sh2_close();
80 io_.Close();
81 releaseSh2Session();
82 return false;
83 }
84
85 new_flag_.fill(false);
86 last_interval_.fill(0);
87 last_sensitivity_.fill(0);
89 return true;
90}
91
99template <typename CommType>
100bool BNO085<CommType>::EnableSensor(BNO085Sensor sensor, uint32_t interval_ms,
101 float sensitivity) noexcept {
102 if (state_ != BNO085DriverState::Sh2Active) {
103 last_error_ = SH2_ERR_OP_IN_PROGRESS;
104 return false;
105 }
106 auto id = static_cast<uint8_t>(sensor);
107 if (id >= last_interval_.size())
108 return false;
109 uint32_t interval_us = interval_ms * 1000;
110 if (!configure(sensor, interval_us, sensitivity, 0))
111 return false;
112 last_interval_[id] = interval_us;
113 last_sensitivity_[id] = sensitivity;
114 return true;
115}
116
118template <typename CommType>
120 if (state_ != BNO085DriverState::Sh2Active) {
121 last_error_ = SH2_ERR_OP_IN_PROGRESS;
122 return false;
123 }
124 auto id = static_cast<uint8_t>(sensor);
125 if (id >= last_interval_.size())
126 return false;
127 if (!configure(sensor, 0, 0, 0))
128 return false;
129 last_interval_[id] = 0;
130 last_sensitivity_[id] = 0.0f;
131 new_flag_[id] = false;
132 return true;
133}
134
136template <typename CommType>
138 callback_ = cb;
139}
140
142template <typename CommType>
144 rvc_cb_ = cb;
145}
146
148template <typename CommType>
150 if (state_ != BNO085DriverState::Sh2Active)
151 return false;
152 auto id = static_cast<uint8_t>(sensor);
153 if (id >= new_flag_.size())
154 return false;
155 return new_flag_[id];
156}
157
161template <typename CommType>
163 const sh2_SensorValue_t& val) const noexcept {
164 SensorEvent out{};
165 out.sensor = sensor;
166 out.timestamp = val.timestamp;
167 out.detected = false;
168 const uint8_t accuracy = val.status & 0x03;
169
170 switch (sensor) {
172 out.raw.x = val.un.rawAccelerometer.x;
173 out.raw.y = val.un.rawAccelerometer.y;
174 out.raw.z = val.un.rawAccelerometer.z;
175 out.raw.sensorTimeUs = val.un.rawAccelerometer.timestamp;
176 break;
178 out.vector.x = val.un.accelerometer.x;
179 out.vector.y = val.un.accelerometer.y;
180 out.vector.z = val.un.accelerometer.z;
181 out.vector.accuracy = accuracy;
182 break;
184 out.vector.x = val.un.linearAcceleration.x;
185 out.vector.y = val.un.linearAcceleration.y;
186 out.vector.z = val.un.linearAcceleration.z;
187 out.vector.accuracy = accuracy;
188 break;
190 out.vector.x = val.un.gravity.x;
191 out.vector.y = val.un.gravity.y;
192 out.vector.z = val.un.gravity.z;
193 out.vector.accuracy = accuracy;
194 break;
196 out.raw.x = val.un.rawGyroscope.x;
197 out.raw.y = val.un.rawGyroscope.y;
198 out.raw.z = val.un.rawGyroscope.z;
199 out.raw.temperature = val.un.rawGyroscope.temperature;
200 out.raw.sensorTimeUs = val.un.rawGyroscope.timestamp;
201 break;
203 out.vector.x = val.un.gyroscope.x;
204 out.vector.y = val.un.gyroscope.y;
205 out.vector.z = val.un.gyroscope.z;
206 out.vector.accuracy = accuracy;
207 break;
209 out.vector.x = val.un.gyroscopeUncal.x;
210 out.vector.y = val.un.gyroscopeUncal.y;
211 out.vector.z = val.un.gyroscopeUncal.z;
212 out.vector.accuracy = accuracy;
213 out.bias.x = val.un.gyroscopeUncal.biasX;
214 out.bias.y = val.un.gyroscopeUncal.biasY;
215 out.bias.z = val.un.gyroscopeUncal.biasZ;
216 break;
218 out.raw.x = val.un.rawMagnetometer.x;
219 out.raw.y = val.un.rawMagnetometer.y;
220 out.raw.z = val.un.rawMagnetometer.z;
221 out.raw.sensorTimeUs = val.un.rawMagnetometer.timestamp;
222 break;
224 out.vector.x = val.un.magneticField.x;
225 out.vector.y = val.un.magneticField.y;
226 out.vector.z = val.un.magneticField.z;
227 out.vector.accuracy = accuracy;
228 break;
230 out.vector.x = val.un.magneticFieldUncal.x;
231 out.vector.y = val.un.magneticFieldUncal.y;
232 out.vector.z = val.un.magneticFieldUncal.z;
233 out.vector.accuracy = accuracy;
234 out.bias.x = val.un.magneticFieldUncal.biasX;
235 out.bias.y = val.un.magneticFieldUncal.biasY;
236 out.bias.z = val.un.magneticFieldUncal.biasZ;
237 break;
239 out.rotation.w = val.un.rotationVector.real;
240 out.rotation.x = val.un.rotationVector.i;
241 out.rotation.y = val.un.rotationVector.j;
242 out.rotation.z = val.un.rotationVector.k;
243 out.rotation.accuracy = accuracy;
244 break;
246 out.rotation.w = val.un.gameRotationVector.real;
247 out.rotation.x = val.un.gameRotationVector.i;
248 out.rotation.y = val.un.gameRotationVector.j;
249 out.rotation.z = val.un.gameRotationVector.k;
250 out.rotation.accuracy = accuracy;
251 break;
253 out.rotation.w = val.un.geoMagRotationVector.real;
254 out.rotation.x = val.un.geoMagRotationVector.i;
255 out.rotation.y = val.un.geoMagRotationVector.j;
256 out.rotation.z = val.un.geoMagRotationVector.k;
257 out.rotation.accuracy = accuracy;
258 break;
260 out.rotation.w = val.un.arvrStabilizedRV.real;
261 out.rotation.x = val.un.arvrStabilizedRV.i;
262 out.rotation.y = val.un.arvrStabilizedRV.j;
263 out.rotation.z = val.un.arvrStabilizedRV.k;
264 out.rotation.accuracy = accuracy;
265 break;
267 out.rotation.w = val.un.arvrStabilizedGRV.real;
268 out.rotation.x = val.un.arvrStabilizedGRV.i;
269 out.rotation.y = val.un.arvrStabilizedGRV.j;
270 out.rotation.z = val.un.arvrStabilizedGRV.k;
271 out.rotation.accuracy = accuracy;
272 break;
274 out.rotation.w = val.un.gyroIntegratedRV.real;
275 out.rotation.x = val.un.gyroIntegratedRV.i;
276 out.rotation.y = val.un.gyroIntegratedRV.j;
277 out.rotation.z = val.un.gyroIntegratedRV.k;
278 out.rotation.accuracy = accuracy;
279 out.angularVelocity.x = val.un.gyroIntegratedRV.angVelX;
280 out.angularVelocity.y = val.un.gyroIntegratedRV.angVelY;
281 out.angularVelocity.z = val.un.gyroIntegratedRV.angVelZ;
282 break;
284 out.scalar = val.un.pressure.value;
285 break;
287 out.scalar = val.un.ambientLight.value;
288 break;
290 out.scalar = val.un.humidity.value;
291 break;
293 out.scalar = val.un.proximity.value;
294 break;
296 out.scalar = val.un.temperature.value;
297 break;
299 out.latencyUs = val.un.stepDetector.latency;
300 out.detected = true;
301 out.eventFlags = 1;
302 break;
304 out.latencyUs = val.un.stepCounter.latency;
305 out.stepCount = val.un.stepCounter.steps;
306 break;
308 out.eventFlags = val.un.sigMotion.motion;
309 out.detected = out.eventFlags != 0;
310 break;
312 out.classification = val.un.stabilityClassifier.classification;
313 break;
315 out.eventFlags = val.un.tapDetector.flags;
316 out.tap.doubleTap = (val.un.tapDetector.flags & TAPDET_DOUBLE);
317 if (val.un.tapDetector.flags & TAPDET_X)
318 out.tap.direction = (val.un.tapDetector.flags & TAPDET_X_POS) ? 0 : 1;
319 else if (val.un.tapDetector.flags & TAPDET_Y)
320 out.tap.direction = (val.un.tapDetector.flags & TAPDET_Y_POS) ? 2 : 3;
321 else if (val.un.tapDetector.flags & TAPDET_Z)
322 out.tap.direction = (val.un.tapDetector.flags & TAPDET_Z_POS) ? 4 : 5;
323 else
324 out.tap.direction = 0;
325 out.detected = val.un.tapDetector.flags & (TAPDET_X | TAPDET_Y | TAPDET_Z);
326 break;
328 out.eventFlags = val.un.shakeDetector.shake;
329 out.detected = out.eventFlags != 0;
330 break;
332 out.eventFlags = val.un.flipDetector.flip;
333 out.detected = out.eventFlags != 0;
334 break;
336 out.eventFlags = val.un.pickupDetector.pickup;
337 out.detected = out.eventFlags != 0;
338 break;
340 out.eventFlags = val.un.stabilityDetector.stability;
341 out.detected = out.eventFlags != 0;
342 break;
344 out.activity.page = val.un.personalActivityClassifier.page;
345 out.activity.lastPage = val.un.personalActivityClassifier.lastPage;
346 out.activity.mostLikelyState = val.un.personalActivityClassifier.mostLikelyState;
347 for (std::size_t i = 0; i < out.activity.confidence.size(); ++i) {
348 out.activity.confidence[i] = val.un.personalActivityClassifier.confidence[i];
349 }
350 out.classification = out.activity.mostLikelyState;
351 break;
353 out.sleepState = val.un.sleepDetector.sleepState;
354 out.detected = out.sleepState != 0;
355 break;
357 out.eventFlags = val.un.tiltDetector.tilt;
358 out.detected = out.eventFlags != 0;
359 break;
361 out.eventFlags = val.un.pocketDetector.pocket;
362 out.detected = out.eventFlags != 0;
363 break;
365 out.eventFlags = val.un.circleDetector.circle;
366 out.detected = out.eventFlags != 0;
367 break;
369 out.scalar = static_cast<float>(val.un.heartRateMonitor.heartRate);
370 break;
372 out.motionIntent = static_cast<uint8_t>(val.un.izroRequest.intent);
373 out.motionRequest = static_cast<uint8_t>(val.un.izroRequest.request);
374 break;
376 out.rawOpticalFlow.timestamp = val.un.rawOptFlow.timestamp;
377 out.rawOpticalFlow.dt = val.un.rawOptFlow.dt;
378 out.rawOpticalFlow.dx = val.un.rawOptFlow.dx;
379 out.rawOpticalFlow.dy = val.un.rawOptFlow.dy;
380 out.rawOpticalFlow.iq = val.un.rawOptFlow.iq;
381 out.rawOpticalFlow.resX = val.un.rawOptFlow.resX;
382 out.rawOpticalFlow.resY = val.un.rawOptFlow.resY;
383 out.rawOpticalFlow.shutter = val.un.rawOptFlow.shutter;
384 out.rawOpticalFlow.frameMax = val.un.rawOptFlow.frameMax;
385 out.rawOpticalFlow.frameAvg = val.un.rawOptFlow.frameAvg;
386 out.rawOpticalFlow.frameMin = val.un.rawOptFlow.frameMin;
387 out.rawOpticalFlow.laserOn = val.un.rawOptFlow.laserOn;
388 break;
390 out.deadReckoningPose.timestamp = val.un.deadReckoningPose.timestamp;
391 out.deadReckoningPose.linearPosition.x = val.un.deadReckoningPose.linPosX;
392 out.deadReckoningPose.linearPosition.y = val.un.deadReckoningPose.linPosY;
393 out.deadReckoningPose.linearPosition.z = val.un.deadReckoningPose.linPosZ;
394 out.deadReckoningPose.rotation.w = val.un.deadReckoningPose.real;
395 out.deadReckoningPose.rotation.x = val.un.deadReckoningPose.i;
396 out.deadReckoningPose.rotation.y = val.un.deadReckoningPose.j;
397 out.deadReckoningPose.rotation.z = val.un.deadReckoningPose.k;
398 out.deadReckoningPose.linearVelocity.x = val.un.deadReckoningPose.linVelX;
399 out.deadReckoningPose.linearVelocity.y = val.un.deadReckoningPose.linVelY;
400 out.deadReckoningPose.linearVelocity.z = val.un.deadReckoningPose.linVelZ;
401 out.deadReckoningPose.angularVelocity.x = val.un.deadReckoningPose.angVelX;
402 out.deadReckoningPose.angularVelocity.y = val.un.deadReckoningPose.angVelY;
403 out.deadReckoningPose.angularVelocity.z = val.un.deadReckoningPose.angVelZ;
404 break;
406 out.wheelEncoder.timestamp = val.un.wheelEncoder.timestamp;
407 out.wheelEncoder.wheelIndex = val.un.wheelEncoder.wheelIndex;
408 out.wheelEncoder.dataType = val.un.wheelEncoder.dataType;
409 out.wheelEncoder.data = val.un.wheelEncoder.data;
410 break;
411 default:
412 break;
413 }
414 return out;
415}
416
420template <typename CommType>
422 SensorEvent out{};
423 out.sensor = sensor;
424 if (state_ != BNO085DriverState::Sh2Active)
425 return out;
426 const auto id = static_cast<uint8_t>(sensor);
427 if (id >= latest_.size())
428 return out;
429 out = toSensorEvent(sensor, latest_[id]);
430 new_flag_[id] = false;
431 return out;
432}
433
435template <typename CommType>
437 if (state_ == BNO085DriverState::Sh2Active) {
438 sh2_service();
439 }
440}
441
443template <typename CommType>
444void BNO085<CommType>::Close() noexcept {
445 if (state_ == BNO085DriverState::DfuInProgress) {
446 last_error_ = SH2_ERR_OP_IN_PROGRESS;
447 return;
448 }
449 if (state_ == BNO085DriverState::RvcActive) {
450 io_.Close();
452 rvc_frame_len_ = 0;
453 return;
454 }
455 if (state_ != BNO085DriverState::Sh2Active)
456 return;
457 sh2_close();
458 io_.Close();
459 releaseSh2Session();
461 new_flag_.fill(false);
462 last_interval_.fill(0);
463 last_sensitivity_.fill(0.0f);
464}
465
467template <typename CommType>
469 const void* expected = nullptr;
470 return bno08x::detail::Sh2SessionOwner().compare_exchange_strong(expected, this) ||
471 expected == this;
472}
473
475template <typename CommType>
477 const void* expected = this;
478 bno08x::detail::Sh2SessionOwner().compare_exchange_strong(expected, nullptr);
479}
480
481// ---- SH-2 HAL callback bridges (CRTP CommInterface -> C sh2_Hal_t) ---------
482
484template <typename CommType>
485int BNO085<CommType>::halOpen(sh2_Hal_t* self) {
486 auto* t = reinterpret_cast<CommHal*>(self);
487 return t->comm->Open() ? SH2_OK : SH2_ERR;
488}
489
491template <typename CommType>
492void BNO085<CommType>::halClose(sh2_Hal_t* self) {
493 reinterpret_cast<CommHal*>(self)->comm->Close();
494}
495
497template <typename CommType>
498int BNO085<CommType>::halRead(sh2_Hal_t* self, uint8_t* buf, unsigned len, uint32_t* t) {
499 auto* th = reinterpret_cast<CommHal*>(self);
500 int ret = th->comm->Read(buf, len);
501 *t = th->comm->GetTimeUs();
502 return ret;
503}
504
506template <typename CommType>
507int BNO085<CommType>::halWrite(sh2_Hal_t* self, uint8_t* buf, unsigned len) {
508 return reinterpret_cast<CommHal*>(self)->comm->Write(buf, len);
509}
510
512template <typename CommType>
513uint32_t BNO085<CommType>::halGetTimeUs(sh2_Hal_t* self) {
514 return reinterpret_cast<CommHal*>(self)->comm->GetTimeUs();
515}
516
517// ---- SH-2 event callback trampolines --------------------------------------
518
520template <typename CommType>
521void BNO085<CommType>::sensorCallback(void* cookie, sh2_SensorEvent_t* event) {
522 static_cast<BNO085<CommType>*>(cookie)->handleSensorEvent(event);
523}
524
526template <typename CommType>
527void BNO085<CommType>::asyncCallback(void* cookie, sh2_AsyncEvent_t* event) {
528 static_cast<BNO085<CommType>*>(cookie)->handleAsyncEvent(event);
529}
530
537template <typename CommType>
538void BNO085<CommType>::handleSensorEvent(const sh2_SensorEvent_t* event) noexcept {
539 sh2_SensorValue_t value;
540 int decode_status = sh2_decodeSensorEvent(&value, event);
541 if (decode_status != SH2_OK) {
542 last_error_ = decode_status;
543 return;
544 }
545 uint8_t id = value.sensorId;
546 if (id >= latest_.size())
547 return;
548 latest_[id] = value;
549 new_flag_[id] = true;
550 if (callback_) {
551 callback_(toSensorEvent(static_cast<BNO085Sensor>(id), value));
552 }
553}
554
561template <typename CommType>
562void BNO085<CommType>::handleAsyncEvent(const sh2_AsyncEvent_t* event) noexcept {
563 if (event->eventId == SH2_RESET) {
564 for (uint8_t id = 0; id < last_interval_.size(); ++id) {
565 if (last_interval_[id])
566 configure(static_cast<BNO085Sensor>(id), last_interval_[id], last_sensitivity_[id], 0);
567 }
568 }
569}
570
580template <typename CommType>
581bool BNO085<CommType>::configure(BNO085Sensor sensor, uint32_t interval_us, float sensitivity,
582 uint32_t batch_us) noexcept {
583 sh2_SensorConfig_t cfg{};
584 cfg.reportInterval_us = interval_us;
585 cfg.batchInterval_us = batch_us;
586 cfg.sensorSpecific = 0;
587 const float clamped_sensitivity = std::clamp(sensitivity, 0.0f, 65535.0f);
588 cfg.changeSensitivity = static_cast<uint16_t>(clamped_sensitivity + 0.5f);
589 cfg.changeSensitivityEnabled = clamped_sensitivity > 0.0f;
590 int status = sh2_setSensorConfig(static_cast<sh2_SensorId_t>(sensor), &cfg);
591 if (status != SH2_OK) {
592 last_error_ = status;
593 return false;
594 }
595 return true;
596}
597
598// ============================================================================
601// ============================================================================
602
609template <typename CommType>
610bool BNO085<CommType>::setPin(bno08x::CtrlPin pin, bool active) noexcept {
611 return bno08x::detail::GpioSet(io_, pin,
613}
614
626template <typename CommType>
627bool BNO085<CommType>::HardwareReset(uint32_t lowMs) noexcept {
628 using bno08x::CtrlPin;
629 if (!setPin(CtrlPin::RSTN, true)) { // Assert reset
630 return false;
631 }
632 io_.Delay(lowMs);
633 if (!setPin(CtrlPin::RSTN, false)) { // Release reset
634 return false;
635 }
636 io_.Delay(RESET_STARTUP_MS); // t1 + t2 (datasheet 6.5.3)
637 return true;
638}
639
641template <typename CommType>
642bool BNO085<CommType>::SetBootPin(bool state) noexcept {
643 return setPin(bno08x::CtrlPin::BOOTN, state);
644}
645
647template <typename CommType>
648bool BNO085<CommType>::EnterBootloader(uint32_t resetLowMs, uint32_t settleMs) noexcept {
649 if (io_.GetInterfaceType() == BNO085Interface::UARTRVC) {
650 last_error_ = SH2_ERR_BAD_PARAM;
651 return false;
652 }
653 if (state_ == BNO085DriverState::DfuInProgress) {
654 last_error_ = SH2_ERR_OP_IN_PROGRESS;
655 return false;
656 }
657 if (state_ != BNO085DriverState::Closed) {
658 Close();
659 }
660 if (state_ != BNO085DriverState::Closed) {
661 last_error_ = SH2_ERR_OP_IN_PROGRESS;
662 return false;
663 }
664
665 if (!io_.Open()) {
666 last_error_ = SH2_ERR;
667 return false;
668 }
669 // Assert BOOTN, reset, then release BOOTN (BOOTN is sampled at reset, datasheet section 1.4).
670 const bool ok = setPin(bno08x::CtrlPin::BOOTN, true) && HardwareReset(resetLowMs);
671 const bool released = setPin(bno08x::CtrlPin::BOOTN, false);
672 if (!ok || !released) {
673 io_.Close();
674 last_error_ = SH2_ERR_IO;
675 return false;
676 }
677 if (settleMs) {
678 io_.Delay(settleMs);
679 }
680 last_error_ = SH2_OK;
681 return true;
682}
683
685template <typename CommType>
686bool BNO085<CommType>::ExitBootloaderAndReboot(uint32_t resetLowMs, uint32_t settleMs) noexcept {
687 if (io_.GetInterfaceType() == BNO085Interface::UARTRVC) {
688 last_error_ = SH2_ERR_BAD_PARAM;
689 return false;
690 }
691 if (state_ == BNO085DriverState::DfuInProgress) {
692 last_error_ = SH2_ERR_OP_IN_PROGRESS;
693 return false;
694 }
695 if (state_ != BNO085DriverState::Closed) {
696 Close();
697 }
698 if (state_ != BNO085DriverState::Closed) {
699 last_error_ = SH2_ERR_OP_IN_PROGRESS;
700 return false;
701 }
702
703 if (!io_.Open()) {
704 last_error_ = SH2_ERR;
705 return false;
706 }
707 // BOOTN high at reset boots the application (datasheet section 1.4).
708 if (!setPin(bno08x::CtrlPin::BOOTN, false) || !HardwareReset(resetLowMs)) {
709 io_.Close();
710 last_error_ = SH2_ERR_IO;
711 return false;
712 }
713 if (settleMs) {
714 io_.Delay(settleMs);
715 }
716 last_error_ = SH2_OK;
717 return true;
718}
719
721template <typename CommType>
722bool BNO085<CommType>::SetWakePin(bool state) noexcept {
723 return setPin(bno08x::CtrlPin::WAKE, state);
724}
725
735template <typename CommType>
737 using bno08x::CtrlPin;
738 const auto bits = static_cast<uint8_t>(iface); // the enum value is the PS1:PS0 pair
739 if (bits > 3) {
740 return false;
741 }
742 const bool ps1 = setPin(CtrlPin::PS1, (bits & 0x2U) != 0);
743 const bool ps0 = setPin(CtrlPin::PS0, (bits & 0x1U) != 0);
744 return ps1 && ps0;
745}
746
747// ============================================================================
766// ============================================================================
767
773template <typename CommType>
775 if (state_ == BNO085DriverState::RvcActive)
776 return true;
777 if (state_ != BNO085DriverState::Closed) {
778 last_error_ = SH2_ERR_OP_IN_PROGRESS;
779 return false;
780 }
781 if (io_.GetInterfaceType() != BNO085Interface::UARTRVC) {
782 last_error_ = SH2_ERR_BAD_PARAM;
783 return false;
784 }
785 if (!io_.Open()) {
786 last_error_ = SH2_ERR;
787 return false;
788 }
789 last_error_ = SH2_OK;
790 rvc_frame_len_ = 0;
792 return true;
793}
794
802template <typename CommType>
804 if (state_ != BNO085DriverState::RvcActive)
805 return;
806 uint8_t c;
807 while (io_.Read(&c, 1) == 1) {
808 rvcProcessByte(c);
809 }
810}
811
813template <typename CommType>
815 if (state_ == BNO085DriverState::RvcActive) {
816 io_.Close();
818 rvc_frame_len_ = 0;
819 }
820}
821
832template <typename CommType>
833void BNO085<CommType>::rvcProcessByte(uint8_t c) noexcept {
834 if (rvc_frame_len_ == RVC_FRAME_LEN_) {
835 std::memmove(rvc_frame_, rvc_frame_ + 1, RVC_FRAME_LEN_ - 1);
836 rvc_frame_[RVC_FRAME_LEN_ - 1] = c;
837 } else {
838 rvc_frame_[rvc_frame_len_++] = c;
839 }
840
841 if (rvc_frame_len_ == RVC_FRAME_LEN_ && rvc_frame_[0] == 0xAA && rvc_frame_[1] == 0xAA) {
842 uint8_t check = 0;
843 for (int i = 2; i < RVC_FRAME_LEN_ - 1; ++i)
844 check += rvc_frame_[i];
845
846 if (check == rvc_frame_[RVC_FRAME_LEN_ - 1]) {
847 RvcSensorEvent event{};
848 event.timestamp_uS = io_.GetTimeUs();
849 event.index = rvc_frame_[2];
850 event.yaw = static_cast<int16_t>((rvc_frame_[4] << 8) | rvc_frame_[3]);
851 event.pitch = static_cast<int16_t>((rvc_frame_[6] << 8) | rvc_frame_[5]);
852 event.roll = static_cast<int16_t>((rvc_frame_[8] << 8) | rvc_frame_[7]);
853 event.acc_x = static_cast<int16_t>((rvc_frame_[10] << 8) | rvc_frame_[9]);
854 event.acc_y = static_cast<int16_t>((rvc_frame_[12] << 8) | rvc_frame_[11]);
855 event.acc_z = static_cast<int16_t>((rvc_frame_[14] << 8) | rvc_frame_[13]);
856 event.mi = rvc_frame_[15];
857 event.mr = rvc_frame_[16];
858
859 if (rvc_cb_) {
860 RvcSensorValue val;
861 decodeRvc(&val, &event);
862 rvc_cb_(val);
863 }
864 rvc_frame_len_ = 0;
865 }
866 }
867}
868
875template <typename CommType>
876void BNO085<CommType>::decodeRvc(RvcSensorValue* out, const RvcSensorEvent* in) noexcept {
877 out->index = in->index;
878 out->yaw_deg = 0.01f * in->yaw;
879 out->pitch_deg = 0.01f * in->pitch;
880 out->roll_deg = 0.01f * in->roll;
881 out->acc_x_g = 0.001f * in->acc_x;
882 out->acc_y_g = 0.001f * in->acc_y;
883 out->acc_z_g = 0.001f * in->acc_z;
884 out->mi = in->mi;
885 out->mr = in->mr;
886 out->timestamp_uS = in->timestamp_uS;
887}
888
889// ============================================================================
902// ============================================================================
903
906static constexpr uint8_t DFU_ACK = 's';
907static constexpr uint32_t DFU_MAX_PACKET_LEN = 64;
908static constexpr uint32_t DFU_MAX_ATTEMPTS = 5;
909static constexpr uint32_t DFU_DELAY_POST_US = 10000;
910static constexpr uint32_t DFU_SEND_TIMEOUT_US = 100000;
912
913template <typename CommType>
914bool BNO085<CommType>::dfuIsKnownPartNumber(const char* part) noexcept {
915 if (!part)
916 return false;
917 return std::strcmp(part, "1000-3608") == 0 || std::strcmp(part, "1000-3676") == 0 ||
918 std::strcmp(part, "1000-4148") == 0 || std::strcmp(part, "1000-4563") == 0;
919}
920
922template <typename CommType>
923void BNO085<CommType>::dfuWrite32be(uint8_t* buf, uint32_t value) noexcept {
924 *buf++ = (value >> 24) & 0xFF;
925 *buf++ = (value >> 16) & 0xFF;
926 *buf++ = (value >> 8) & 0xFF;
927 *buf++ = (value >> 0) & 0xFF;
928}
929
931template <typename CommType>
932void BNO085<CommType>::dfuAppendCrc(uint8_t* packet, uint8_t len) noexcept {
933 uint16_t crc = 0xFFFF;
934 for (int n = 0; n < len; n++) {
935 uint16_t x = static_cast<uint16_t>(packet[n]) << 8;
936 for (int i = 0; i < 8; i++) {
937 if ((crc ^ x) & 0x8000)
938 crc = (crc << 1) ^ 0x1021;
939 else
940 crc = crc << 1;
941 x <<= 1;
942 }
943 }
944 packet[len] = (crc >> 8) & 0xFF;
945 packet[len + 1] = crc & 0xFF;
946}
947
956template <typename CommType>
957int BNO085<CommType>::dfuSend(uint8_t* p_data, uint32_t len) noexcept {
958 unsigned int retries = 0;
959 int status = SH2_OK;
960 uint8_t ack = 0;
961 bool got_ack = false;
962 uint32_t t;
963 sh2_Hal_t* hal = halWrapper_.asHal();
964
965 while (!got_ack && (retries < DFU_MAX_ATTEMPTS)) {
966 uint32_t now = hal->getTimeUs(hal);
967 uint32_t start = now;
968 status = 0;
969 while ((status == 0) && ((now - start) < DFU_SEND_TIMEOUT_US)) {
970 status = hal->write(hal, p_data, len);
971 now = hal->getTimeUs(hal);
972 }
973 if (status == 0)
974 status = SH2_ERR_TIMEOUT;
975 if (status > 0) {
976 status = 0;
977 while ((status == 0) && ((now - start) < DFU_SEND_TIMEOUT_US)) {
978 status = hal->read(hal, &ack, 1, &t);
979 now = hal->getTimeUs(hal);
980 }
981 if (status == 0)
982 status = SH2_ERR_TIMEOUT;
983 }
984 if (status > 0) {
985 if (ack == DFU_ACK) {
986 got_ack = true;
987 status = SH2_OK;
988 } else {
989 got_ack = false;
990 status = SH2_ERR_HUB;
991 }
992 }
993 if (!got_ack)
994 retries++;
995 }
996 return (status >= 0) ? SH2_OK : status;
997}
998
1000template <typename CommType>
1001int BNO085<CommType>::dfuSendAppSize(uint8_t* dfu_buff, uint32_t app_size) noexcept {
1002 dfuWrite32be(dfu_buff, app_size);
1003 dfuAppendCrc(dfu_buff, 4);
1004 return dfuSend(dfu_buff, 6);
1005}
1006
1008template <typename CommType>
1009int BNO085<CommType>::dfuSendPktSize(uint8_t* dfu_buff, uint8_t packet_len) noexcept {
1010 dfu_buff[0] = packet_len;
1011 dfuAppendCrc(dfu_buff, 1);
1012 return dfuSend(dfu_buff, 3);
1013}
1014
1016template <typename CommType>
1017int BNO085<CommType>::dfuSendPkt(uint8_t* dfu_buff, uint8_t* p_data, uint32_t len) noexcept {
1018 std::memcpy(dfu_buff, p_data, len);
1019 dfuAppendCrc(dfu_buff, len);
1020 return dfuSend(dfu_buff, len + 2);
1021}
1022
1033template <typename CommType>
1034int BNO085<CommType>::dfuSelectBootloaderAddress(uint8_t& appAddress, bool& switched) noexcept {
1035 switched = false;
1036 appAddress = 0;
1038 appAddress = io_.GetI2cAddress();
1039 const uint8_t boot = BootloaderI2cAddress(appAddress);
1040 if (boot == 0) {
1041 return SH2_ERR_BAD_PARAM;
1042 }
1043 if (boot == appAddress) {
1044 return SH2_OK; // the port already talks to the bootloader
1045 }
1046 if (!io_.SetI2cAddress(boot)) {
1047 return SH2_ERR_IO;
1048 }
1049 switched = true;
1050 }
1051 return SH2_OK;
1052}
1053
1055template <typename CommType>
1056bool BNO085<CommType>::dfuRestoreAppAddress(uint8_t appAddress) noexcept {
1058 return io_.SetI2cAddress(appAddress);
1059 } else {
1060 (void)appAddress;
1061 return true;
1062 }
1063}
1064
1066template <typename CommType>
1067int BNO085<CommType>::Dfu(const HcBin_t& fw) noexcept {
1068 DfuOptions options{};
1069 return DfuWithOptions(fw, options);
1070}
1071
1073template <typename CommType>
1075 const DfuOptions& options) noexcept {
1076 if (!image.data || image.length == 0) {
1077 last_error_ = SH2_ERR_BAD_PARAM;
1078 return SH2_ERR_BAD_PARAM;
1079 }
1080 MemoryFirmware memfw(image.data, image.length, image.format ? image.format : "BNO_V1",
1081 image.partNumber ? image.partNumber : "unknown", image.preferredPacketLen);
1082 return DfuWithOptions(memfw.hcbin(), options);
1083}
1084
1086template <typename CommType>
1087int BNO085<CommType>::DfuFromMemory(const uint8_t* data, uint32_t len, const char* partNumber,
1088 const DfuOptions& options) noexcept {
1089 DfuMemoryImage image{};
1090 image.data = data;
1091 image.length = len;
1092 image.partNumber = partNumber ? partNumber : "unknown";
1093 return DfuFromMemory(image, options);
1094}
1095
1097template <typename CommType>
1099 uint32_t enterResetLowMs, uint32_t enterSettleMs,
1100 uint32_t exitResetLowMs, uint32_t exitSettleMs) noexcept {
1101 if (!EnterBootloader(enterResetLowMs, enterSettleMs))
1102 return last_error_;
1103
1104 int status = DfuFromMemory(image, options);
1105 const int dfu_status = status;
1106 if (!ExitBootloaderAndReboot(exitResetLowMs, exitSettleMs) && status == SH2_OK) {
1107 // Reboot failure becomes the workflow result only if DFU transfer succeeded.
1108 status = last_error_;
1109 }
1110
1111 if (dfu_status != SH2_OK) {
1112 return dfu_status;
1113 }
1114 return status;
1115}
1116
1124template <typename CommType>
1125int BNO085<CommType>::DfuWithOptions(const HcBin_t& fw, const DfuOptions& options) noexcept {
1126 if (io_.GetInterfaceType() == BNO085Interface::UARTRVC) {
1127 last_error_ = SH2_ERR_BAD_PARAM;
1128 return SH2_ERR_BAD_PARAM;
1129 }
1131 last_error_ = SH2_ERR_OP_IN_PROGRESS;
1132 return SH2_ERR_OP_IN_PROGRESS;
1133 }
1134 if (state_ == BNO085DriverState::Sh2Active) {
1135 // DFU uses the same transport HAL and must not race an active SH-2 session.
1136 Close();
1137 }
1138 if (state_ != BNO085DriverState::Closed) {
1139 last_error_ = SH2_ERR_OP_IN_PROGRESS;
1140 return SH2_ERR_OP_IN_PROGRESS;
1141 }
1143 prepareHalWrapper();
1144
1145 int rc, status = SH2_OK;
1146 uint32_t app_len = 0;
1147 uint8_t packet_len = 0;
1148 uint32_t offset = 0;
1149 const char* meta = nullptr;
1150 bool fw_opened = false;
1151 bool hal_opened = false;
1152 uint8_t app_address = 0;
1153 bool address_switched = false;
1154 uint8_t dfu_buff[DFU_MAX_PACKET_LEN + 2];
1155 sh2_Hal_t* hal = halWrapper_.asHal();
1156
1157 rc = fw.open();
1158 if (rc != 0) {
1159 status = SH2_ERR;
1160 goto dfu_end;
1161 }
1162 fw_opened = true;
1163
1164 if (options.requireFormatMatch) {
1165 const char* expected_format = options.requiredFormat ? options.requiredFormat : "BNO_V1";
1166 meta = fw.getMeta("FW-Format");
1167 if (!meta || std::strcmp(meta, expected_format) != 0) {
1168 status = SH2_ERR_BAD_PARAM;
1169 goto dfu_close;
1170 }
1171 }
1172
1173 if (options.requirePartNumber) {
1174 meta = fw.getMeta("SW-Part-Number");
1175 if (!meta) {
1176 status = SH2_ERR_BAD_PARAM;
1177 goto dfu_close;
1178 }
1179 if (options.requiredPartNumber) {
1180 if (std::strcmp(meta, options.requiredPartNumber) != 0) {
1181 status = SH2_ERR_BAD_PARAM;
1182 goto dfu_close;
1183 }
1184 } else if (!dfuIsKnownPartNumber(meta)) {
1185 status = SH2_ERR_BAD_PARAM;
1186 goto dfu_close;
1187 }
1188 }
1189
1190 app_len = fw.getAppLen();
1191 if (app_len < 1024) {
1192 status = SH2_ERR_BAD_PARAM;
1193 goto dfu_close;
1194 }
1195
1196 packet_len = fw.getPacketLen();
1197 if (options.packetLenOverride != 0) {
1198 packet_len = options.packetLenOverride;
1199 }
1200 if (packet_len == 0 || packet_len > DFU_MAX_PACKET_LEN)
1201 packet_len = DFU_MAX_PACKET_LEN;
1202
1203 if (io_.GetInterfaceType() == BNO085Interface::I2C) {
1204 // Datasheet section 1.4: over I2C the bootloader answers at 0x28 / 0x29, not 0x4A / 0x4B.
1205 status = dfuSelectBootloaderAddress(app_address, address_switched);
1206 if (status != SH2_OK)
1207 goto dfu_close;
1208 }
1209
1210 status = hal->open(hal);
1211 if (status != SH2_OK)
1212 goto dfu_close;
1213 hal_opened = true;
1214
1215 status = dfuSendAppSize(dfu_buff, app_len);
1216 if (status != SH2_OK)
1217 goto dfu_close;
1218 status = dfuSendPktSize(dfu_buff, packet_len);
1219 if (status != SH2_OK)
1220 goto dfu_close;
1221
1222 if (options.progress) {
1223 options.progress(DfuProgress{0, app_len});
1224 }
1225
1226 offset = 0;
1227 while (offset < app_len) {
1228 uint32_t to_send = app_len - offset;
1229 if (to_send > packet_len)
1230 to_send = packet_len;
1231 status = fw.getAppData(dfu_buff, offset, to_send);
1232 if (status != SH2_OK) {
1233 status = SH2_ERR;
1234 goto dfu_close;
1235 }
1236 status = dfuSendPkt(dfu_buff, dfu_buff, to_send);
1237 if (status != SH2_OK)
1238 goto dfu_close;
1239 offset += to_send;
1240 if (options.progress) {
1241 options.progress(DfuProgress{offset, app_len});
1242 }
1243 }
1244
1245dfu_close:
1246 if (fw_opened)
1247 fw.close();
1248 if (status == SH2_OK && hal_opened) {
1249 uint32_t now = hal->getTimeUs(hal), start = now;
1250 while ((now - start) < DFU_DELAY_POST_US)
1251 now = hal->getTimeUs(hal);
1252 }
1253 if (hal_opened)
1254 hal->close(hal);
1255 if (address_switched && !dfuRestoreAppAddress(app_address) && status == SH2_OK)
1256 status = SH2_ERR_IO;
1257
1258dfu_end:
1260 last_error_ = status;
1261 return status;
1262}
High-level C++ driver for the BNO08x 9-axis IMU family.
constexpr int BNO08X_ERR_SH2_SESSION_IN_USE
GetLastError() value when Begin() is refused because another BNO085 object already holds the SH-2 ses...
Definition bno08x.hpp:33
BNO085Interface
Identifies the host interface type that a CommInterface provides.
Definition bno08x_comm_interface.hpp:27
@ UARTRVC
UART in RVC mode (PS1=0, PS0=1). RVC frames only.
@ I2C
I2C bus (PS1=0, PS0=0). Supports SH-2 and DFU.
Unified driver for the BNO08x IMU – SH-2 mode, RVC mode, and DFU.
Definition bno08x.hpp:400
int DfuWithOptions(const HcBin_t &fw, const DfuOptions &options) noexcept
Perform DFU with explicit validation and transfer options.
Definition bno08x.ipp:1125
bool DisableSensor(BNO085Sensor sensor) noexcept
Disable reporting for a sensor.
Definition bno08x.ipp:119
bool SetBootPin(bool state) noexcept
Drive the BOOTN pin to enter/exit DFU bootloader mode.
Definition bno08x.ipp:642
bool BeginRvc() noexcept
Initialise RVC mode.
Definition bno08x.ipp:774
bool HasNewData(BNO085Sensor sensor) const
Check if new data is available for a sensor.
Definition bno08x.ipp:149
SensorEvent GetLatest(BNO085Sensor sensor) noexcept
Retrieve the most recent event for a sensor.
Definition bno08x.ipp:421
bool EnableSensor(BNO085Sensor sensor, uint32_t interval_ms, float sensitivity=0.0f) noexcept
Enable periodic reporting for a sensor.
Definition bno08x.ipp:100
void ServiceRvc() noexcept
Poll for RVC frames and dispatch callbacks.
Definition bno08x.ipp:803
void SetCallback(SensorCallback cb) noexcept
Register a callback for SH-2 sensor events.
Definition bno08x.ipp:137
void CloseRvc() noexcept
Stop RVC processing and close the transport.
Definition bno08x.ipp:814
int DfuFromMemory(const DfuMemoryImage &image, const DfuOptions &options={}) noexcept
Perform DFU from an in-memory firmware image.
Definition bno08x.ipp:1074
void Update() noexcept
Pump the SH-2 service loop.
Definition bno08x.ipp:436
int RunDfuFromMemory(const DfuMemoryImage &image, const DfuOptions &options={}, uint32_t enterResetLowMs=10, uint32_t enterSettleMs=50, uint32_t exitResetLowMs=2, uint32_t exitSettleMs=100) noexcept
Full class-aware DFU workflow: enter bootloader -> transfer firmware -> reboot to app.
Definition bno08x.ipp:1098
void Close() noexcept
Close the currently active session and release transport resources.
Definition bno08x.ipp:444
int Dfu(const HcBin_t &fw=firmware) noexcept
Perform a Device Firmware Update (DFU).
Definition bno08x.ipp:1067
bool Begin() noexcept
Initialise the sensor in SH-2 mode.
Definition bno08x.ipp:40
bool EnterBootloader(uint32_t resetLowMs=10, uint32_t settleMs=50) noexcept
Enter bootloader mode using BOOTN + reset pin sequence.
Definition bno08x.ipp:648
bool SetWakePin(bool state) noexcept
Drive the WAKE pin (SPI mode only).
Definition bno08x.ipp:722
bool ExitBootloaderAndReboot(uint32_t resetLowMs=2, uint32_t settleMs=100) noexcept
Exit bootloader mode and reboot into application firmware.
Definition bno08x.ipp:686
bool SelectInterface(BNO085Interface iface) noexcept
Select the host interface by driving PS0/PS1 pins.
Definition bno08x.ipp:736
bool HardwareReset(uint32_t lowMs=2) noexcept
Perform a hardware reset via the RSTN pin.
Definition bno08x.ipp:627
void SetRvcCallback(RvcCallback cb) noexcept
Register a callback for decoded RVC frames.
Definition bno08x.ipp:143
HcBin_t implementation for firmware stored in memory.
Definition MemoryFirmware.hpp:16
std::function< void(const RvcSensorValue &)> RvcCallback
Callback invoked when a decoded RVC frame is available.
Definition bno08x.hpp:299
BNO085Sensor
Identifiers for all SH-2 sensor reports supported by the BNO08x.
Definition bno08x.hpp:70
std::function< void(const SensorEvent &)> SensorCallback
Callback invoked when a new SH-2 sensor event is received.
Definition bno08x.hpp:296
@ HeartRateMonitor
Heart rate data.
@ MagneticFieldUncalibrated
Raw magnetic field (uT)
@ GyroUncalibrated
Raw angular velocity (rad/s)
@ PersonalActivityClassifier
Activity classification (walking, running, etc.)
@ StepDetector
Individual step detected.
@ ShakeDetector
Shake gesture detected.
@ PickupDetector
Pickup gesture detected.
@ ARVRStabilizedRV
AR/VR stabilised rotation vector.
@ CircleDetector
Circular motion detected.
@ IZroMotionRequest
Interactive ZRO motion intent/request.
@ RawOpticalFlow
Raw optical flow report.
@ GeomagneticRotationVector
Orientation using magnetometer for heading.
@ FlipDetector
Flip gesture detected.
@ StepCounter
Cumulative step count.
@ Gravity
Gravity vector (m/s^2)
@ SleepDetector
Sleep state detected.
@ WheelEncoder
Wheel encoder report.
@ RotationVector
Fused absolute orientation quaternion.
@ SignificantMotion
Significant motion detected.
@ AmbientLight
Ambient light level (lux)
@ Magnetometer
Calibrated magnetic field (uT)
@ TiltDetector
Tilt event detected.
@ RawMagnetometer
Raw magnetometer ADC counts.
@ TapDetector
Single or double tap event.
@ Pressure
Barometric pressure (hPa)
@ StabilityClassifier
Device stability state (on table, in hand, etc.)
@ Accelerometer
Calibrated acceleration vector (m/s^2)
@ LinearAcceleration
Acceleration minus gravity (m/s^2)
@ Gyroscope
Calibrated angular velocity (rad/s)
@ StabilityDetector
Stability change detected.
@ GyroIntegratedRV
High-rate gyro-integrated rotation vector.
@ GameRotationVector
Orientation without magnetometer heading.
@ RawAccelerometer
Raw accelerometer ADC counts.
@ ARVRStabilizedGameRV
AR/VR stabilised game rotation vector.
@ Humidity
Relative humidity (%)
@ DeadReckoningPose
Dead reckoning pose estimate.
@ Temperature
Temperature (deg C)
@ RawGyroscope
Raw gyroscope ADC counts.
@ Proximity
Proximity detector (cm)
@ PocketDetector
Device-in-pocket detected.
std::atomic< const void * > & Sh2SessionOwner() noexcept
Owner of the process-wide SH-2 session (nullptr when free).
Definition bno08x.hpp:42
constexpr bool kGpioSetReportsStatus
True when the port's GpioSet() reports a status (returns bool).
Definition bno08x_comm_interface.hpp:90
bool GpioSet(Port &port, CtrlPin pin, GpioSignal signal) noexcept
Call the port's GpioSet() and return its status.
Definition bno08x_comm_interface.hpp:102
Definition bno08x.hpp:35
CtrlPin
Identifies the hardware control pins of the BNO08x.
Definition bno08x_comm_interface.hpp:60
@ BOOTN
Bootloader entry (active-low on the physical pin)
@ WAKE
Wake from suspend, SPI mode only (active-low on the physical pin)
GpioSignal
Abstract signal level for control pins.
Definition bno08x_comm_interface.hpp:76
@ ACTIVE
Pin function is asserted.
@ INACTIVE
Pin function is deasserted.
Firmware image descriptor for class-aware memory DFU.
Definition bno08x.hpp:342
const uint8_t * data
Pointer to firmware bytes.
Definition bno08x.hpp:343
Controls DFU validation and transfer behavior.
Definition bno08x.hpp:329
Progress sample emitted during DFU transfers.
Definition bno08x.hpp:317
Definition HcBin.h:47
Raw RVC frame data in integer fixed-point format.
Definition bno08x.hpp:261
uint64_t timestamp_uS
Timestamp in microseconds (set by the driver)
Definition bno08x.hpp:271
Decoded RVC frame data in floating-point natural units.
Definition bno08x.hpp:280
Container for a single decoded SH-2 sensor report.
Definition bno08x.hpp:227
BNO085Sensor sensor
Which sensor produced this event.
Definition bno08x.hpp:228