HF-BNO08x  0.1.0-dev
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 last_error_ = 0;
53 prepareHalWrapper();
54
55 int status = sh2_open(halWrapper_.asHal(), asyncCallback, this);
56 if (status != SH2_OK) {
57 last_error_ = status;
58 io_.Close();
59 return false;
60 }
61 status = sh2_reinitialize();
62 if (status != SH2_OK) {
63 last_error_ = status;
64 sh2_close();
65 io_.Close();
66 return false;
67 }
68 status = sh2_setSensorCallback(sensorCallback, this);
69 if (status != SH2_OK) {
70 last_error_ = status;
71 sh2_close();
72 io_.Close();
73 return false;
74 }
75
76 new_flag_.fill(false);
77 last_interval_.fill(0);
78 last_sensitivity_.fill(0);
80 return true;
81}
82
90template <typename CommType>
91bool BNO085<CommType>::EnableSensor(BNO085Sensor sensor, uint32_t interval_ms,
92 float sensitivity) noexcept {
93 if (state_ != BNO085DriverState::Sh2Active) {
94 last_error_ = SH2_ERR_OP_IN_PROGRESS;
95 return false;
96 }
97 auto id = static_cast<uint8_t>(sensor);
98 if (id >= last_interval_.size())
99 return false;
100 uint32_t interval_us = interval_ms * 1000;
101 if (!configure(sensor, interval_us, sensitivity, 0))
102 return false;
103 last_interval_[id] = interval_us;
104 last_sensitivity_[id] = sensitivity;
105 return true;
106}
107
109template <typename CommType>
111 if (state_ != BNO085DriverState::Sh2Active) {
112 last_error_ = SH2_ERR_OP_IN_PROGRESS;
113 return false;
114 }
115 auto id = static_cast<uint8_t>(sensor);
116 if (id >= last_interval_.size())
117 return false;
118 if (!configure(sensor, 0, 0, 0))
119 return false;
120 last_interval_[id] = 0;
121 last_sensitivity_[id] = 0.0f;
122 new_flag_[id] = false;
123 return true;
124}
125
127template <typename CommType>
129 callback_ = cb;
130}
131
133template <typename CommType>
135 rvc_cb_ = cb;
136}
137
139template <typename CommType>
141 if (state_ != BNO085DriverState::Sh2Active)
142 return false;
143 auto id = static_cast<uint8_t>(sensor);
144 if (id >= new_flag_.size())
145 return false;
146 return new_flag_[id];
147}
148
152template <typename CommType>
154 const sh2_SensorValue_t& val) const noexcept {
155 SensorEvent out{};
156 out.sensor = sensor;
157 out.timestamp = val.timestamp;
158 out.detected = false;
159 const uint8_t accuracy = val.status & 0x03;
160
161 switch (sensor) {
163 out.raw.x = val.un.rawAccelerometer.x;
164 out.raw.y = val.un.rawAccelerometer.y;
165 out.raw.z = val.un.rawAccelerometer.z;
166 out.raw.sensorTimeUs = val.un.rawAccelerometer.timestamp;
167 break;
169 out.vector.x = val.un.accelerometer.x;
170 out.vector.y = val.un.accelerometer.y;
171 out.vector.z = val.un.accelerometer.z;
172 out.vector.accuracy = accuracy;
173 break;
175 out.vector.x = val.un.linearAcceleration.x;
176 out.vector.y = val.un.linearAcceleration.y;
177 out.vector.z = val.un.linearAcceleration.z;
178 out.vector.accuracy = accuracy;
179 break;
181 out.vector.x = val.un.gravity.x;
182 out.vector.y = val.un.gravity.y;
183 out.vector.z = val.un.gravity.z;
184 out.vector.accuracy = accuracy;
185 break;
187 out.raw.x = val.un.rawGyroscope.x;
188 out.raw.y = val.un.rawGyroscope.y;
189 out.raw.z = val.un.rawGyroscope.z;
190 out.raw.temperature = val.un.rawGyroscope.temperature;
191 out.raw.sensorTimeUs = val.un.rawGyroscope.timestamp;
192 break;
194 out.vector.x = val.un.gyroscope.x;
195 out.vector.y = val.un.gyroscope.y;
196 out.vector.z = val.un.gyroscope.z;
197 out.vector.accuracy = accuracy;
198 break;
200 out.vector.x = val.un.gyroscopeUncal.x;
201 out.vector.y = val.un.gyroscopeUncal.y;
202 out.vector.z = val.un.gyroscopeUncal.z;
203 out.vector.accuracy = accuracy;
204 out.bias.x = val.un.gyroscopeUncal.biasX;
205 out.bias.y = val.un.gyroscopeUncal.biasY;
206 out.bias.z = val.un.gyroscopeUncal.biasZ;
207 break;
209 out.raw.x = val.un.rawMagnetometer.x;
210 out.raw.y = val.un.rawMagnetometer.y;
211 out.raw.z = val.un.rawMagnetometer.z;
212 out.raw.sensorTimeUs = val.un.rawMagnetometer.timestamp;
213 break;
215 out.vector.x = val.un.magneticField.x;
216 out.vector.y = val.un.magneticField.y;
217 out.vector.z = val.un.magneticField.z;
218 out.vector.accuracy = accuracy;
219 break;
221 out.vector.x = val.un.magneticFieldUncal.x;
222 out.vector.y = val.un.magneticFieldUncal.y;
223 out.vector.z = val.un.magneticFieldUncal.z;
224 out.vector.accuracy = accuracy;
225 out.bias.x = val.un.magneticFieldUncal.biasX;
226 out.bias.y = val.un.magneticFieldUncal.biasY;
227 out.bias.z = val.un.magneticFieldUncal.biasZ;
228 break;
230 out.rotation.w = val.un.rotationVector.real;
231 out.rotation.x = val.un.rotationVector.i;
232 out.rotation.y = val.un.rotationVector.j;
233 out.rotation.z = val.un.rotationVector.k;
234 out.rotation.accuracy = accuracy;
235 break;
237 out.rotation.w = val.un.gameRotationVector.real;
238 out.rotation.x = val.un.gameRotationVector.i;
239 out.rotation.y = val.un.gameRotationVector.j;
240 out.rotation.z = val.un.gameRotationVector.k;
241 out.rotation.accuracy = accuracy;
242 break;
244 out.rotation.w = val.un.geoMagRotationVector.real;
245 out.rotation.x = val.un.geoMagRotationVector.i;
246 out.rotation.y = val.un.geoMagRotationVector.j;
247 out.rotation.z = val.un.geoMagRotationVector.k;
248 out.rotation.accuracy = accuracy;
249 break;
251 out.rotation.w = val.un.arvrStabilizedRV.real;
252 out.rotation.x = val.un.arvrStabilizedRV.i;
253 out.rotation.y = val.un.arvrStabilizedRV.j;
254 out.rotation.z = val.un.arvrStabilizedRV.k;
255 out.rotation.accuracy = accuracy;
256 break;
258 out.rotation.w = val.un.arvrStabilizedGRV.real;
259 out.rotation.x = val.un.arvrStabilizedGRV.i;
260 out.rotation.y = val.un.arvrStabilizedGRV.j;
261 out.rotation.z = val.un.arvrStabilizedGRV.k;
262 out.rotation.accuracy = accuracy;
263 break;
265 out.rotation.w = val.un.gyroIntegratedRV.real;
266 out.rotation.x = val.un.gyroIntegratedRV.i;
267 out.rotation.y = val.un.gyroIntegratedRV.j;
268 out.rotation.z = val.un.gyroIntegratedRV.k;
269 out.rotation.accuracy = accuracy;
270 out.angularVelocity.x = val.un.gyroIntegratedRV.angVelX;
271 out.angularVelocity.y = val.un.gyroIntegratedRV.angVelY;
272 out.angularVelocity.z = val.un.gyroIntegratedRV.angVelZ;
273 break;
275 out.scalar = val.un.pressure.value;
276 break;
278 out.scalar = val.un.ambientLight.value;
279 break;
281 out.scalar = val.un.humidity.value;
282 break;
284 out.scalar = val.un.proximity.value;
285 break;
287 out.scalar = val.un.temperature.value;
288 break;
290 out.latencyUs = val.un.stepDetector.latency;
291 out.detected = true;
292 out.eventFlags = 1;
293 break;
295 out.latencyUs = val.un.stepCounter.latency;
296 out.stepCount = val.un.stepCounter.steps;
297 break;
299 out.eventFlags = val.un.sigMotion.motion;
300 out.detected = out.eventFlags != 0;
301 break;
303 out.classification = val.un.stabilityClassifier.classification;
304 break;
306 out.eventFlags = val.un.tapDetector.flags;
307 out.tap.doubleTap = (val.un.tapDetector.flags & TAPDET_DOUBLE);
308 if (val.un.tapDetector.flags & TAPDET_X)
309 out.tap.direction = (val.un.tapDetector.flags & TAPDET_X_POS) ? 0 : 1;
310 else if (val.un.tapDetector.flags & TAPDET_Y)
311 out.tap.direction = (val.un.tapDetector.flags & TAPDET_Y_POS) ? 2 : 3;
312 else if (val.un.tapDetector.flags & TAPDET_Z)
313 out.tap.direction = (val.un.tapDetector.flags & TAPDET_Z_POS) ? 4 : 5;
314 else
315 out.tap.direction = 0;
316 out.detected = val.un.tapDetector.flags & (TAPDET_X | TAPDET_Y | TAPDET_Z);
317 break;
319 out.eventFlags = val.un.shakeDetector.shake;
320 out.detected = out.eventFlags != 0;
321 break;
323 out.eventFlags = val.un.flipDetector.flip;
324 out.detected = out.eventFlags != 0;
325 break;
327 out.eventFlags = val.un.pickupDetector.pickup;
328 out.detected = out.eventFlags != 0;
329 break;
331 out.eventFlags = val.un.stabilityDetector.stability;
332 out.detected = out.eventFlags != 0;
333 break;
335 out.activity.page = val.un.personalActivityClassifier.page;
336 out.activity.lastPage = val.un.personalActivityClassifier.lastPage;
337 out.activity.mostLikelyState = val.un.personalActivityClassifier.mostLikelyState;
338 for (std::size_t i = 0; i < out.activity.confidence.size(); ++i) {
339 out.activity.confidence[i] = val.un.personalActivityClassifier.confidence[i];
340 }
341 out.classification = out.activity.mostLikelyState;
342 break;
344 out.sleepState = val.un.sleepDetector.sleepState;
345 out.detected = out.sleepState != 0;
346 break;
348 out.eventFlags = val.un.tiltDetector.tilt;
349 out.detected = out.eventFlags != 0;
350 break;
352 out.eventFlags = val.un.pocketDetector.pocket;
353 out.detected = out.eventFlags != 0;
354 break;
356 out.eventFlags = val.un.circleDetector.circle;
357 out.detected = out.eventFlags != 0;
358 break;
360 out.scalar = static_cast<float>(val.un.heartRateMonitor.heartRate);
361 break;
363 out.motionIntent = static_cast<uint8_t>(val.un.izroRequest.intent);
364 out.motionRequest = static_cast<uint8_t>(val.un.izroRequest.request);
365 break;
367 out.rawOpticalFlow.timestamp = val.un.rawOptFlow.timestamp;
368 out.rawOpticalFlow.dt = val.un.rawOptFlow.dt;
369 out.rawOpticalFlow.dx = val.un.rawOptFlow.dx;
370 out.rawOpticalFlow.dy = val.un.rawOptFlow.dy;
371 out.rawOpticalFlow.iq = val.un.rawOptFlow.iq;
372 out.rawOpticalFlow.resX = val.un.rawOptFlow.resX;
373 out.rawOpticalFlow.resY = val.un.rawOptFlow.resY;
374 out.rawOpticalFlow.shutter = val.un.rawOptFlow.shutter;
375 out.rawOpticalFlow.frameMax = val.un.rawOptFlow.frameMax;
376 out.rawOpticalFlow.frameAvg = val.un.rawOptFlow.frameAvg;
377 out.rawOpticalFlow.frameMin = val.un.rawOptFlow.frameMin;
378 out.rawOpticalFlow.laserOn = val.un.rawOptFlow.laserOn;
379 break;
381 out.deadReckoningPose.timestamp = val.un.deadReckoningPose.timestamp;
382 out.deadReckoningPose.linearPosition.x = val.un.deadReckoningPose.linPosX;
383 out.deadReckoningPose.linearPosition.y = val.un.deadReckoningPose.linPosY;
384 out.deadReckoningPose.linearPosition.z = val.un.deadReckoningPose.linPosZ;
385 out.deadReckoningPose.rotation.w = val.un.deadReckoningPose.real;
386 out.deadReckoningPose.rotation.x = val.un.deadReckoningPose.i;
387 out.deadReckoningPose.rotation.y = val.un.deadReckoningPose.j;
388 out.deadReckoningPose.rotation.z = val.un.deadReckoningPose.k;
389 out.deadReckoningPose.linearVelocity.x = val.un.deadReckoningPose.linVelX;
390 out.deadReckoningPose.linearVelocity.y = val.un.deadReckoningPose.linVelY;
391 out.deadReckoningPose.linearVelocity.z = val.un.deadReckoningPose.linVelZ;
392 out.deadReckoningPose.angularVelocity.x = val.un.deadReckoningPose.angVelX;
393 out.deadReckoningPose.angularVelocity.y = val.un.deadReckoningPose.angVelY;
394 out.deadReckoningPose.angularVelocity.z = val.un.deadReckoningPose.angVelZ;
395 break;
397 out.wheelEncoder.timestamp = val.un.wheelEncoder.timestamp;
398 out.wheelEncoder.wheelIndex = val.un.wheelEncoder.wheelIndex;
399 out.wheelEncoder.dataType = val.un.wheelEncoder.dataType;
400 out.wheelEncoder.data = val.un.wheelEncoder.data;
401 break;
402 default:
403 break;
404 }
405 return out;
406}
407
411template <typename CommType>
413 SensorEvent out{};
414 out.sensor = sensor;
415 if (state_ != BNO085DriverState::Sh2Active)
416 return out;
417 const auto id = static_cast<uint8_t>(sensor);
418 if (id >= latest_.size())
419 return out;
420 out = toSensorEvent(sensor, latest_[id]);
421 new_flag_[id] = false;
422 return out;
423}
424
426template <typename CommType>
428 if (state_ == BNO085DriverState::Sh2Active) {
429 sh2_service();
430 }
431}
432
434template <typename CommType>
435void BNO085<CommType>::Close() noexcept {
436 if (state_ == BNO085DriverState::DfuInProgress) {
437 last_error_ = SH2_ERR_OP_IN_PROGRESS;
438 return;
439 }
440 if (state_ == BNO085DriverState::RvcActive) {
441 io_.Close();
443 rvc_frame_len_ = 0;
444 return;
445 }
446 if (state_ != BNO085DriverState::Sh2Active)
447 return;
448 sh2_close();
449 io_.Close();
451 new_flag_.fill(false);
452 last_interval_.fill(0);
453 last_sensitivity_.fill(0.0f);
454}
455
456// ---- SH-2 HAL callback bridges (CRTP CommInterface -> C sh2_Hal_t) ---------
457
459template <typename CommType>
460int BNO085<CommType>::halOpen(sh2_Hal_t* self) {
461 auto* t = reinterpret_cast<CommHal*>(self);
462 return t->comm->Open() ? SH2_OK : SH2_ERR;
463}
464
466template <typename CommType>
467void BNO085<CommType>::halClose(sh2_Hal_t* self) {
468 reinterpret_cast<CommHal*>(self)->comm->Close();
469}
470
472template <typename CommType>
473int BNO085<CommType>::halRead(sh2_Hal_t* self, uint8_t* buf, unsigned len, uint32_t* t) {
474 auto* th = reinterpret_cast<CommHal*>(self);
475 int ret = th->comm->Read(buf, len);
476 *t = th->comm->GetTimeUs();
477 return ret;
478}
479
481template <typename CommType>
482int BNO085<CommType>::halWrite(sh2_Hal_t* self, uint8_t* buf, unsigned len) {
483 return reinterpret_cast<CommHal*>(self)->comm->Write(buf, len);
484}
485
487template <typename CommType>
488uint32_t BNO085<CommType>::halGetTimeUs(sh2_Hal_t* self) {
489 return reinterpret_cast<CommHal*>(self)->comm->GetTimeUs();
490}
491
492// ---- SH-2 event callback trampolines --------------------------------------
493
495template <typename CommType>
496void BNO085<CommType>::sensorCallback(void* cookie, sh2_SensorEvent_t* event) {
497 static_cast<BNO085<CommType>*>(cookie)->handleSensorEvent(event);
498}
499
501template <typename CommType>
502void BNO085<CommType>::asyncCallback(void* cookie, sh2_AsyncEvent_t* event) {
503 static_cast<BNO085<CommType>*>(cookie)->handleAsyncEvent(event);
504}
505
512template <typename CommType>
513void BNO085<CommType>::handleSensorEvent(const sh2_SensorEvent_t* event) noexcept {
514 sh2_SensorValue_t value;
515 int decode_status = sh2_decodeSensorEvent(&value, event);
516 if (decode_status != SH2_OK) {
517 last_error_ = decode_status;
518 return;
519 }
520 uint8_t id = value.sensorId;
521 if (id >= latest_.size())
522 return;
523 latest_[id] = value;
524 new_flag_[id] = true;
525 if (callback_) {
526 callback_(toSensorEvent(static_cast<BNO085Sensor>(id), value));
527 }
528}
529
536template <typename CommType>
537void BNO085<CommType>::handleAsyncEvent(const sh2_AsyncEvent_t* event) noexcept {
538 if (event->eventId == SH2_RESET) {
539 for (uint8_t id = 0; id < last_interval_.size(); ++id) {
540 if (last_interval_[id])
541 configure(static_cast<BNO085Sensor>(id), last_interval_[id], last_sensitivity_[id], 0);
542 }
543 }
544}
545
555template <typename CommType>
556bool BNO085<CommType>::configure(BNO085Sensor sensor, uint32_t interval_us, float sensitivity,
557 uint32_t batch_us) noexcept {
558 sh2_SensorConfig_t cfg{};
559 cfg.reportInterval_us = interval_us;
560 cfg.batchInterval_us = batch_us;
561 cfg.sensorSpecific = 0;
562 const float clamped_sensitivity = std::clamp(sensitivity, 0.0f, 65535.0f);
563 cfg.changeSensitivity = static_cast<uint16_t>(clamped_sensitivity + 0.5f);
564 cfg.changeSensitivityEnabled = clamped_sensitivity > 0.0f;
565 int status = sh2_setSensorConfig(static_cast<sh2_SensorId_t>(sensor), &cfg);
566 if (status != SH2_OK) {
567 last_error_ = status;
568 return false;
569 }
570 return true;
571}
572
573// ============================================================================
576// ============================================================================
577
584template <typename CommType>
585void BNO085<CommType>::HardwareReset(uint32_t lowMs) noexcept {
586 using bno08x::CtrlPin;
587 io_.GpioSetActive(CtrlPin::RSTN); // Assert reset
588 io_.Delay(lowMs);
589 io_.GpioSetInactive(CtrlPin::RSTN); // Release reset
590 io_.Delay(50); // Wait for sensor boot
591}
592
594template <typename CommType>
595void BNO085<CommType>::SetBootPin(bool state) noexcept {
596 using bno08x::CtrlPin;
597 using bno08x::GpioSignal;
598 io_.GpioSet(CtrlPin::BOOTN, state ? GpioSignal::ACTIVE : GpioSignal::INACTIVE);
599}
600
602template <typename CommType>
603bool BNO085<CommType>::EnterBootloader(uint32_t resetLowMs, uint32_t settleMs) noexcept {
604 if (io_.GetInterfaceType() == BNO085Interface::UARTRVC) {
605 last_error_ = SH2_ERR_BAD_PARAM;
606 return false;
607 }
608 if (state_ == BNO085DriverState::DfuInProgress) {
609 last_error_ = SH2_ERR_OP_IN_PROGRESS;
610 return false;
611 }
612 if (state_ != BNO085DriverState::Closed) {
613 Close();
614 }
615 if (state_ != BNO085DriverState::Closed) {
616 last_error_ = SH2_ERR_OP_IN_PROGRESS;
617 return false;
618 }
619
620 io_.GpioSetActive(bno08x::CtrlPin::BOOTN); // Assert BOOTN (enter bootloader)
621 HardwareReset(resetLowMs);
622 io_.GpioSetInactive(bno08x::CtrlPin::BOOTN); // Release BOOTN after reset
623 if (settleMs) {
624 io_.Delay(settleMs);
625 }
626 last_error_ = SH2_OK;
627 return true;
628}
629
631template <typename CommType>
632bool BNO085<CommType>::ExitBootloaderAndReboot(uint32_t resetLowMs, uint32_t settleMs) noexcept {
633 if (io_.GetInterfaceType() == BNO085Interface::UARTRVC) {
634 last_error_ = SH2_ERR_BAD_PARAM;
635 return false;
636 }
637 if (state_ == BNO085DriverState::DfuInProgress) {
638 last_error_ = SH2_ERR_OP_IN_PROGRESS;
639 return false;
640 }
641 if (state_ != BNO085DriverState::Closed) {
642 Close();
643 }
644 if (state_ != BNO085DriverState::Closed) {
645 last_error_ = SH2_ERR_OP_IN_PROGRESS;
646 return false;
647 }
648
649 io_.GpioSetInactive(bno08x::CtrlPin::BOOTN); // Ensure normal application boot path
650 HardwareReset(resetLowMs);
651 if (settleMs) {
652 io_.Delay(settleMs);
653 }
654 last_error_ = SH2_OK;
655 return true;
656}
657
659template <typename CommType>
660void BNO085<CommType>::SetWakePin(bool state) noexcept {
661 using bno08x::CtrlPin;
662 using bno08x::GpioSignal;
663 io_.GpioSet(CtrlPin::WAKE, state ? GpioSignal::ACTIVE : GpioSignal::INACTIVE);
664}
665
672template <typename CommType>
674 using bno08x::CtrlPin;
675 using bno08x::GpioSignal;
676 switch (iface) {
678 io_.GpioSetInactive(CtrlPin::PS1);
679 io_.GpioSetInactive(CtrlPin::PS0);
680 break;
682 io_.GpioSetActive(CtrlPin::PS1);
683 io_.GpioSetInactive(CtrlPin::PS0);
684 break;
686 io_.GpioSetInactive(CtrlPin::PS1);
687 io_.GpioSetActive(CtrlPin::PS0);
688 break;
690 io_.GpioSetActive(CtrlPin::PS1);
691 io_.GpioSetActive(CtrlPin::PS0);
692 break;
693 }
694}
695
696// ============================================================================
715// ============================================================================
716
722template <typename CommType>
724 if (state_ == BNO085DriverState::RvcActive)
725 return true;
726 if (state_ != BNO085DriverState::Closed) {
727 last_error_ = SH2_ERR_OP_IN_PROGRESS;
728 return false;
729 }
730 if (io_.GetInterfaceType() != BNO085Interface::UARTRVC) {
731 last_error_ = SH2_ERR_BAD_PARAM;
732 return false;
733 }
734 if (!io_.Open()) {
735 last_error_ = SH2_ERR;
736 return false;
737 }
738 last_error_ = SH2_OK;
739 rvc_frame_len_ = 0;
741 return true;
742}
743
751template <typename CommType>
753 if (state_ != BNO085DriverState::RvcActive)
754 return;
755 uint8_t c;
756 while (io_.Read(&c, 1) == 1) {
757 rvcProcessByte(c);
758 }
759}
760
762template <typename CommType>
764 if (state_ == BNO085DriverState::RvcActive) {
765 io_.Close();
767 rvc_frame_len_ = 0;
768 }
769}
770
781template <typename CommType>
782void BNO085<CommType>::rvcProcessByte(uint8_t c) noexcept {
783 if (rvc_frame_len_ == RVC_FRAME_LEN_) {
784 std::memmove(rvc_frame_, rvc_frame_ + 1, RVC_FRAME_LEN_ - 1);
785 rvc_frame_[RVC_FRAME_LEN_ - 1] = c;
786 } else {
787 rvc_frame_[rvc_frame_len_++] = c;
788 }
789
790 if (rvc_frame_len_ == RVC_FRAME_LEN_ && rvc_frame_[0] == 0xAA && rvc_frame_[1] == 0xAA) {
791 uint8_t check = 0;
792 for (int i = 2; i < RVC_FRAME_LEN_ - 1; ++i)
793 check += rvc_frame_[i];
794
795 if (check == rvc_frame_[RVC_FRAME_LEN_ - 1]) {
796 RvcSensorEvent event{};
797 event.timestamp_uS = io_.GetTimeUs();
798 event.index = rvc_frame_[2];
799 event.yaw = static_cast<int16_t>((rvc_frame_[4] << 8) | rvc_frame_[3]);
800 event.pitch = static_cast<int16_t>((rvc_frame_[6] << 8) | rvc_frame_[5]);
801 event.roll = static_cast<int16_t>((rvc_frame_[8] << 8) | rvc_frame_[7]);
802 event.acc_x = static_cast<int16_t>((rvc_frame_[10] << 8) | rvc_frame_[9]);
803 event.acc_y = static_cast<int16_t>((rvc_frame_[12] << 8) | rvc_frame_[11]);
804 event.acc_z = static_cast<int16_t>((rvc_frame_[14] << 8) | rvc_frame_[13]);
805 event.mi = rvc_frame_[15];
806 event.mr = rvc_frame_[16];
807
808 if (rvc_cb_) {
809 RvcSensorValue val;
810 decodeRvc(&val, &event);
811 rvc_cb_(val);
812 }
813 rvc_frame_len_ = 0;
814 }
815 }
816}
817
824template <typename CommType>
825void BNO085<CommType>::decodeRvc(RvcSensorValue* out, const RvcSensorEvent* in) noexcept {
826 out->index = in->index;
827 out->yaw_deg = 0.01f * in->yaw;
828 out->pitch_deg = 0.01f * in->pitch;
829 out->roll_deg = 0.01f * in->roll;
830 out->acc_x_g = 0.001f * in->acc_x;
831 out->acc_y_g = 0.001f * in->acc_y;
832 out->acc_z_g = 0.001f * in->acc_z;
833 out->mi = in->mi;
834 out->mr = in->mr;
835 out->timestamp_uS = in->timestamp_uS;
836}
837
838// ============================================================================
851// ============================================================================
852
855static constexpr uint8_t DFU_ACK = 's';
856static constexpr uint32_t DFU_MAX_PACKET_LEN = 64;
857static constexpr uint32_t DFU_MAX_ATTEMPTS = 5;
858static constexpr uint32_t DFU_DELAY_POST_US = 10000;
859static constexpr uint32_t DFU_SEND_TIMEOUT_US = 100000;
861
862template <typename CommType>
863bool BNO085<CommType>::dfuIsKnownPartNumber(const char* part) noexcept {
864 if (!part)
865 return false;
866 return std::strcmp(part, "1000-3608") == 0 || std::strcmp(part, "1000-3676") == 0 ||
867 std::strcmp(part, "1000-4148") == 0 || std::strcmp(part, "1000-4563") == 0;
868}
869
871template <typename CommType>
872void BNO085<CommType>::dfuWrite32be(uint8_t* buf, uint32_t value) noexcept {
873 *buf++ = (value >> 24) & 0xFF;
874 *buf++ = (value >> 16) & 0xFF;
875 *buf++ = (value >> 8) & 0xFF;
876 *buf++ = (value >> 0) & 0xFF;
877}
878
880template <typename CommType>
881void BNO085<CommType>::dfuAppendCrc(uint8_t* packet, uint8_t len) noexcept {
882 uint16_t crc = 0xFFFF;
883 for (int n = 0; n < len; n++) {
884 uint16_t x = static_cast<uint16_t>(packet[n]) << 8;
885 for (int i = 0; i < 8; i++) {
886 if ((crc ^ x) & 0x8000)
887 crc = (crc << 1) ^ 0x1021;
888 else
889 crc = crc << 1;
890 x <<= 1;
891 }
892 }
893 packet[len] = (crc >> 8) & 0xFF;
894 packet[len + 1] = crc & 0xFF;
895}
896
905template <typename CommType>
906int BNO085<CommType>::dfuSend(uint8_t* dfu_buff, uint8_t* p_data, uint32_t len) noexcept {
907 unsigned int retries = 0;
908 int status = SH2_OK;
909 uint8_t ack = 0;
910 bool got_ack = false;
911 uint32_t t;
912 sh2_Hal_t* hal = halWrapper_.asHal();
913
914 while (!got_ack && (retries < DFU_MAX_ATTEMPTS)) {
915 uint32_t now = hal->getTimeUs(hal);
916 uint32_t start = now;
917 status = 0;
918 while ((status == 0) && ((now - start) < DFU_SEND_TIMEOUT_US)) {
919 status = hal->write(hal, p_data, len);
920 now = hal->getTimeUs(hal);
921 }
922 if (status == 0)
923 status = SH2_ERR_TIMEOUT;
924 if (status > 0) {
925 status = 0;
926 while ((status == 0) && ((now - start) < DFU_SEND_TIMEOUT_US)) {
927 status = hal->read(hal, &ack, 1, &t);
928 now = hal->getTimeUs(hal);
929 }
930 if (status == 0)
931 status = SH2_ERR_TIMEOUT;
932 }
933 if (status > 0) {
934 if (ack == DFU_ACK) {
935 got_ack = true;
936 status = SH2_OK;
937 } else {
938 got_ack = false;
939 status = SH2_ERR_HUB;
940 }
941 }
942 if (!got_ack)
943 retries++;
944 }
945 return (status >= 0) ? SH2_OK : status;
946}
947
949template <typename CommType>
950int BNO085<CommType>::dfuSendAppSize(uint8_t* dfu_buff, uint32_t app_size) noexcept {
951 dfuWrite32be(dfu_buff, app_size);
952 dfuAppendCrc(dfu_buff, 4);
953 return dfuSend(dfu_buff, dfu_buff, 6);
954}
955
957template <typename CommType>
958int BNO085<CommType>::dfuSendPktSize(uint8_t* dfu_buff, uint8_t packet_len) noexcept {
959 dfu_buff[0] = packet_len;
960 dfuAppendCrc(dfu_buff, 1);
961 return dfuSend(dfu_buff, dfu_buff, 3);
962}
963
965template <typename CommType>
966int BNO085<CommType>::dfuSendPkt(uint8_t* dfu_buff, uint8_t* p_data, uint32_t len) noexcept {
967 std::memcpy(dfu_buff, p_data, len);
968 dfuAppendCrc(dfu_buff, len);
969 return dfuSend(dfu_buff, dfu_buff, len + 2);
970}
971
973template <typename CommType>
974int BNO085<CommType>::Dfu(const HcBin_t& fw) noexcept {
975 DfuOptions options{};
976 return DfuWithOptions(fw, options);
977}
978
980template <typename CommType>
982 const DfuOptions& options) noexcept {
983 if (!image.data || image.length == 0) {
984 last_error_ = SH2_ERR_BAD_PARAM;
985 return SH2_ERR_BAD_PARAM;
986 }
987 MemoryFirmware memfw(image.data, image.length, image.format ? image.format : "BNO_V1",
988 image.partNumber ? image.partNumber : "unknown", image.preferredPacketLen);
989 return DfuWithOptions(memfw.hcbin(), options);
990}
991
993template <typename CommType>
994int BNO085<CommType>::DfuFromMemory(const uint8_t* data, uint32_t len, const char* partNumber,
995 const DfuOptions& options) noexcept {
996 DfuMemoryImage image{};
997 image.data = data;
998 image.length = len;
999 image.partNumber = partNumber ? partNumber : "unknown";
1000 return DfuFromMemory(image, options);
1001}
1002
1004template <typename CommType>
1006 uint32_t enterResetLowMs, uint32_t enterSettleMs,
1007 uint32_t exitResetLowMs, uint32_t exitSettleMs) noexcept {
1008 if (!EnterBootloader(enterResetLowMs, enterSettleMs))
1009 return last_error_;
1010
1011 int status = DfuFromMemory(image, options);
1012 const int dfu_status = status;
1013 if (!ExitBootloaderAndReboot(exitResetLowMs, exitSettleMs) && status == SH2_OK) {
1014 // Reboot failure becomes the workflow result only if DFU transfer succeeded.
1015 status = last_error_;
1016 }
1017
1018 if (dfu_status != SH2_OK) {
1019 return dfu_status;
1020 }
1021 return status;
1022}
1023
1031template <typename CommType>
1032int BNO085<CommType>::DfuWithOptions(const HcBin_t& fw, const DfuOptions& options) noexcept {
1033 if (io_.GetInterfaceType() == BNO085Interface::UARTRVC) {
1034 last_error_ = SH2_ERR_BAD_PARAM;
1035 return SH2_ERR_BAD_PARAM;
1036 }
1038 last_error_ = SH2_ERR_OP_IN_PROGRESS;
1039 return SH2_ERR_OP_IN_PROGRESS;
1040 }
1041 if (state_ == BNO085DriverState::Sh2Active) {
1042 // DFU uses the same transport HAL and must not race an active SH-2 session.
1043 Close();
1044 }
1045 if (state_ != BNO085DriverState::Closed) {
1046 last_error_ = SH2_ERR_OP_IN_PROGRESS;
1047 return SH2_ERR_OP_IN_PROGRESS;
1048 }
1050 prepareHalWrapper();
1051
1052 int rc, status = SH2_OK;
1053 uint32_t app_len = 0;
1054 uint8_t packet_len = 0;
1055 uint32_t offset = 0;
1056 const char* meta = nullptr;
1057 bool fw_opened = false;
1058 bool hal_opened = false;
1059 uint8_t dfu_buff[DFU_MAX_PACKET_LEN + 2];
1060 sh2_Hal_t* hal = halWrapper_.asHal();
1061
1062 rc = fw.open();
1063 if (rc != 0) {
1064 status = SH2_ERR;
1065 goto dfu_end;
1066 }
1067 fw_opened = true;
1068
1069 if (options.requireFormatMatch) {
1070 const char* expected_format = options.requiredFormat ? options.requiredFormat : "BNO_V1";
1071 meta = fw.getMeta("FW-Format");
1072 if (!meta || std::strcmp(meta, expected_format) != 0) {
1073 status = SH2_ERR_BAD_PARAM;
1074 goto dfu_close;
1075 }
1076 }
1077
1078 if (options.requirePartNumber) {
1079 meta = fw.getMeta("SW-Part-Number");
1080 if (!meta) {
1081 status = SH2_ERR_BAD_PARAM;
1082 goto dfu_close;
1083 }
1084 if (options.requiredPartNumber) {
1085 if (std::strcmp(meta, options.requiredPartNumber) != 0) {
1086 status = SH2_ERR_BAD_PARAM;
1087 goto dfu_close;
1088 }
1089 } else if (!dfuIsKnownPartNumber(meta)) {
1090 status = SH2_ERR_BAD_PARAM;
1091 goto dfu_close;
1092 }
1093 }
1094
1095 app_len = fw.getAppLen();
1096 if (app_len < 1024) {
1097 status = SH2_ERR_BAD_PARAM;
1098 goto dfu_close;
1099 }
1100
1101 packet_len = fw.getPacketLen();
1102 if (options.packetLenOverride != 0) {
1103 packet_len = options.packetLenOverride;
1104 }
1105 if (packet_len == 0 || packet_len > DFU_MAX_PACKET_LEN)
1106 packet_len = DFU_MAX_PACKET_LEN;
1107
1108 status = hal->open(hal);
1109 if (status != SH2_OK)
1110 goto dfu_close;
1111 hal_opened = true;
1112
1113 status = dfuSendAppSize(dfu_buff, app_len);
1114 if (status != SH2_OK)
1115 goto dfu_close;
1116 status = dfuSendPktSize(dfu_buff, packet_len);
1117 if (status != SH2_OK)
1118 goto dfu_close;
1119
1120 if (options.progress) {
1121 options.progress(DfuProgress{0, app_len});
1122 }
1123
1124 offset = 0;
1125 while (offset < app_len) {
1126 uint32_t to_send = app_len - offset;
1127 if (to_send > packet_len)
1128 to_send = packet_len;
1129 status = fw.getAppData(dfu_buff, offset, to_send);
1130 if (status != SH2_OK) {
1131 status = SH2_ERR;
1132 goto dfu_close;
1133 }
1134 status = dfuSendPkt(dfu_buff, dfu_buff, to_send);
1135 if (status != SH2_OK)
1136 goto dfu_close;
1137 offset += to_send;
1138 if (options.progress) {
1139 options.progress(DfuProgress{offset, app_len});
1140 }
1141 }
1142
1143dfu_close:
1144 if (fw_opened)
1145 fw.close();
1146 if (status == SH2_OK && hal_opened) {
1147 uint32_t now = hal->getTimeUs(hal), start = now;
1148 while ((now - start) < DFU_DELAY_POST_US)
1149 now = hal->getTimeUs(hal);
1150 }
1151 if (hal_opened)
1152 hal->close(hal);
1153
1154dfu_end:
1156 last_error_ = status;
1157 return status;
1158}
High-level C++ driver for the BNO08x 9-axis IMU family.
BNO085Interface
Identifies the host interface type that a CommInterface provides.
Definition bno08x_comm_interface.hpp:25
@ UARTRVC
UART in RVC mode (PS1=1, PS0=0). RVC frames only.
@ SPI
SPI bus (PS1=1, PS0=1). Supports SH-2 and DFU.
@ UART
UART in SH-2 mode (PS1=0, PS0=1). Supports SH-2 and DFU.
@ 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:375
int DfuWithOptions(const HcBin_t &fw, const DfuOptions &options) noexcept
Perform DFU with explicit validation and transfer options.
Definition bno08x.ipp:1032
bool DisableSensor(BNO085Sensor sensor) noexcept
Disable reporting for a sensor.
Definition bno08x.ipp:110
bool BeginRvc() noexcept
Initialise RVC mode.
Definition bno08x.ipp:723
void HardwareReset(uint32_t lowMs=2) noexcept
Perform a hardware reset via the RSTN pin.
Definition bno08x.ipp:585
bool HasNewData(BNO085Sensor sensor) const
Check if new data is available for a sensor.
Definition bno08x.ipp:140
SensorEvent GetLatest(BNO085Sensor sensor) noexcept
Retrieve the most recent event for a sensor.
Definition bno08x.ipp:412
bool EnableSensor(BNO085Sensor sensor, uint32_t interval_ms, float sensitivity=0.0f) noexcept
Enable periodic reporting for a sensor.
Definition bno08x.ipp:91
void ServiceRvc() noexcept
Poll for RVC frames and dispatch callbacks.
Definition bno08x.ipp:752
void SetCallback(SensorCallback cb) noexcept
Register a callback for SH-2 sensor events.
Definition bno08x.ipp:128
void CloseRvc() noexcept
Stop RVC processing and close the transport.
Definition bno08x.ipp:763
void SelectInterface(BNO085Interface iface) noexcept
Select the host interface by driving PS0/PS1 pins.
Definition bno08x.ipp:673
void SetWakePin(bool state) noexcept
Drive the WAKE pin (SPI mode only).
Definition bno08x.ipp:660
int DfuFromMemory(const DfuMemoryImage &image, const DfuOptions &options={}) noexcept
Perform DFU from an in-memory firmware image.
Definition bno08x.ipp:981
void Update() noexcept
Pump the SH-2 service loop.
Definition bno08x.ipp:427
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:1005
void Close() noexcept
Close the currently active session and release transport resources.
Definition bno08x.ipp:435
int Dfu(const HcBin_t &fw=firmware) noexcept
Perform a Device Firmware Update (DFU).
Definition bno08x.ipp:974
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:603
void SetBootPin(bool state) noexcept
Drive the BOOTN pin to enter/exit DFU bootloader mode.
Definition bno08x.ipp:595
bool ExitBootloaderAndReboot(uint32_t resetLowMs=2, uint32_t settleMs=100) noexcept
Exit bootloader mode and reboot into application firmware.
Definition bno08x.ipp:632
void SetRvcCallback(RvcCallback cb) noexcept
Register a callback for decoded RVC frames.
Definition bno08x.ipp:134
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:273
BNO085Sensor
Identifiers for all SH-2 sensor reports supported by the BNO08x.
Definition bno08x.hpp:44
std::function< void(const SensorEvent &)> SensorCallback
Callback invoked when a new SH-2 sensor event is received.
Definition bno08x.hpp:270
@ 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.
CtrlPin
Identifies the hardware control pins of the BNO08x.
Definition bno08x_comm_interface.hpp:58
@ BOOTN
Bootloader entry (active-low on the physical pin)
GpioSignal
Abstract signal level for control pins.
Definition bno08x_comm_interface.hpp:74
Firmware image descriptor for class-aware memory DFU.
Definition bno08x.hpp:321
const uint8_t * data
Pointer to firmware bytes.
Definition bno08x.hpp:322
Controls DFU validation and transfer behavior.
Definition bno08x.hpp:308
Progress sample emitted during DFU transfers.
Definition bno08x.hpp:296
Definition HcBin.h:47
Raw RVC frame data in integer fixed-point format.
Definition bno08x.hpp:235
uint64_t timestamp_uS
Timestamp in microseconds (set by the driver)
Definition bno08x.hpp:245
Decoded RVC frame data in floating-point natural units.
Definition bno08x.hpp:254
Container for a single decoded SH-2 sensor report.
Definition bno08x.hpp:201
BNO085Sensor sensor
Which sensor produced this event.
Definition bno08x.hpp:202