1mod registers;
2use byteorder::{ByteOrder, LittleEndian};
3use i2cdev::core::I2CDevice;
4use i2cdev::linux::LinuxI2CDevice;
5use log::{debug, error, warn};
6pub use registers::OperationMode;
7use registers::{
8 AccelRegisters, ChipRegisters, Constants, EulerRegisters, GravityRegisters, GyroRegisters,
9 LinearAccelRegisters, MagRegisters, QuaternionRegisters, RegisterPage, StatusRegisters,
10};
11use std::sync::mpsc;
12use std::sync::{Arc, RwLock};
13use std::thread;
14use std::time::Duration;
15
16#[derive(Debug)]
17pub enum Error {
18 I2c(i2cdev::linux::LinuxI2CError),
19 InvalidChipId,
20 CalibrationFailed,
21 ReadError,
22 WriteError,
23}
24
25impl std::fmt::Display for Error {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 match self {
28 Error::I2c(err) => write!(f, "I2C error: {}", err),
29 Error::InvalidChipId => write!(f, "Invalid chip ID"),
30 Error::CalibrationFailed => write!(f, "Calibration failed"),
31 Error::ReadError => write!(f, "Read error"),
32 Error::WriteError => write!(f, "Write error"),
33 }
34 }
35}
36
37impl std::error::Error for Error {
38 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
39 match self {
40 Error::I2c(err) => Some(err),
41 _ => None,
42 }
43 }
44}
45
46impl From<i2cdev::linux::LinuxI2CError> for Error {
47 fn from(err: i2cdev::linux::LinuxI2CError) -> Self {
48 Error::I2c(err)
49 }
50}
51
52#[derive(Debug, Clone, Copy)]
53pub struct Quaternion {
54 pub w: f32,
55 pub x: f32,
56 pub y: f32,
57 pub z: f32,
58}
59
60#[derive(Debug, Clone, Copy)]
61pub struct EulerAngles {
62 pub roll: f32, pub pitch: f32, pub yaw: f32, }
66
67#[derive(Debug, Clone, Copy)]
68pub struct Vector3 {
69 pub x: f32,
70 pub y: f32,
71 pub z: f32,
72}
73
74#[derive(Debug, Clone, Copy)]
75pub struct BnoData {
76 pub quaternion: Quaternion,
77 pub euler: EulerAngles,
78 pub accelerometer: Vector3,
79 pub gyroscope: Vector3,
80 pub magnetometer: Vector3,
81 pub linear_acceleration: Vector3,
82 pub gravity: Vector3,
83 pub temperature: i8,
84 pub calibration_status: u8,
85}
86
87impl Default for BnoData {
88 fn default() -> Self {
89 BnoData {
90 quaternion: Quaternion {
91 w: 0.0,
92 x: 0.0,
93 y: 0.0,
94 z: 0.0,
95 },
96 euler: EulerAngles {
97 roll: 0.0,
98 pitch: 0.0,
99 yaw: 0.0,
100 },
101 accelerometer: Vector3 {
102 x: 0.0,
103 y: 0.0,
104 z: 0.0,
105 },
106 gyroscope: Vector3 {
107 x: 0.0,
108 y: 0.0,
109 z: 0.0,
110 },
111 magnetometer: Vector3 {
112 x: 0.0,
113 y: 0.0,
114 z: 0.0,
115 },
116 linear_acceleration: Vector3 {
117 x: 0.0,
118 y: 0.0,
119 z: 0.0,
120 },
121 gravity: Vector3 {
122 x: 0.0,
123 y: 0.0,
124 z: 0.0,
125 },
126 temperature: 0,
127 calibration_status: 0,
128 }
129 }
130}
131
132pub struct Bno055 {
133 i2c: LinuxI2CDevice,
134}
135
136impl Bno055 {
137 pub fn new(i2c_bus: &str) -> Result<Self, Error> {
142 let i2c = LinuxI2CDevice::new(i2c_bus, Constants::DefaultI2cAddr as u16)?;
143 let mut bno = Bno055 { i2c };
144
145 bno.set_page(RegisterPage::Page0)?;
147
148 bno.verify_chip_id()?;
150
151 bno.reset()?;
153
154 bno.set_mode(OperationMode::Ndof)?;
156
157 Ok(bno)
158 }
159
160 fn set_page(&mut self, page: RegisterPage) -> Result<(), Error> {
161 self.i2c
162 .smbus_write_byte_data(ChipRegisters::PageId as u8, page as u8)
163 .map_err(Error::I2c)
164 }
165
166 fn verify_chip_id(&mut self) -> Result<(), Error> {
167 self.set_page(RegisterPage::Page0)?;
168 let chip_id = self.i2c.smbus_read_byte_data(ChipRegisters::ChipId as u8)?;
169 if Constants::ChipId as u8 != chip_id {
170 error!("Invalid chip ID. Expected 0xA0, got {:#x}", chip_id);
171 return Err(Error::InvalidChipId);
172 }
173 Ok(())
174 }
175
176 pub fn reset(&mut self) -> Result<(), Error> {
177 self.set_page(RegisterPage::Page0)?;
178 self.i2c
179 .smbus_write_byte_data(StatusRegisters::SysTrigger as u8, 0x20)?;
180 thread::sleep(Duration::from_millis(650));
181 Ok(())
182 }
183
184 pub fn get_quaternion(&mut self) -> Result<Quaternion, Error> {
187 self.set_page(RegisterPage::Page0)?;
188 let mut buf = [0u8; 8];
189
190 for (i, byte) in buf.iter_mut().enumerate() {
192 *byte = self
193 .i2c
194 .smbus_read_byte_data((QuaternionRegisters::WLsb as u8) + i as u8)?;
195 }
196
197 let scale = 1.0 / ((1 << 14) as f32);
198 Ok(Quaternion {
199 w: (LittleEndian::read_i16(&buf[0..2]) as f32) * scale,
200 x: (LittleEndian::read_i16(&buf[2..4]) as f32) * scale,
201 y: (LittleEndian::read_i16(&buf[4..6]) as f32) * scale,
202 z: (LittleEndian::read_i16(&buf[6..8]) as f32) * scale,
203 })
204 }
205
206 pub fn get_euler_angles(&mut self) -> Result<EulerAngles, Error> {
209 self.set_page(RegisterPage::Page0)?;
210 let mut buf = [0u8; 6];
211
212 for (i, byte) in buf.iter_mut().enumerate() {
214 *byte = self
215 .i2c
216 .smbus_read_byte_data((EulerRegisters::HLsb as u8) + i as u8)?;
217 }
218
219 let scale = 1.0 / 16.0;
221 Ok(EulerAngles {
222 yaw: (LittleEndian::read_i16(&buf[0..2]) as f32) * scale,
223 roll: (LittleEndian::read_i16(&buf[2..4]) as f32) * scale,
224 pitch: (LittleEndian::read_i16(&buf[4..6]) as f32) * scale,
225 })
226 }
227
228 pub fn get_linear_acceleration(&mut self) -> Result<Vector3, Error> {
231 self.set_page(RegisterPage::Page0)?;
232 let mut buf = [0u8; 6];
233
234 for (i, byte) in buf.iter_mut().enumerate() {
236 *byte = self
237 .i2c
238 .smbus_read_byte_data((LinearAccelRegisters::XLsb as u8) + i as u8)?;
239 }
240
241 let scale = 1.0 / 100.0;
243 Ok(Vector3 {
244 x: (LittleEndian::read_i16(&buf[0..2]) as f32) * scale,
245 y: (LittleEndian::read_i16(&buf[2..4]) as f32) * scale,
246 z: (LittleEndian::read_i16(&buf[4..6]) as f32) * scale,
247 })
248 }
249
250 pub fn get_gravity_vector(&mut self) -> Result<Vector3, Error> {
253 self.set_page(RegisterPage::Page0)?;
254 let mut buf = [0u8; 6];
255
256 for (i, byte) in buf.iter_mut().enumerate() {
258 *byte = self
259 .i2c
260 .smbus_read_byte_data((GravityRegisters::XLsb as u8) + i as u8)?;
261 }
262
263 let scale = 1.0 / 100.0;
264 Ok(Vector3 {
265 x: (LittleEndian::read_i16(&buf[0..2]) as f32) * scale,
266 y: (LittleEndian::read_i16(&buf[2..4]) as f32) * scale,
267 z: (LittleEndian::read_i16(&buf[4..6]) as f32) * scale,
268 })
269 }
270
271 pub fn set_mode(&mut self, mode: OperationMode) -> Result<(), Error> {
276 self.set_page(RegisterPage::Page0)?;
277 self.i2c
278 .smbus_write_byte_data(StatusRegisters::OprMode as u8, mode as u8)?;
279 thread::sleep(Duration::from_millis(20));
281 Ok(())
282 }
283
284 pub fn get_accelerometer(&mut self) -> Result<Vector3, Error> {
287 self.set_page(RegisterPage::Page0)?;
288 let mut buf = [0u8; 6];
289
290 for (i, byte) in buf.iter_mut().enumerate() {
292 *byte = self
293 .i2c
294 .smbus_read_byte_data((AccelRegisters::XLsb as u8) + i as u8)?;
295 }
296
297 let scale = 1.0 / 100.0;
299 Ok(Vector3 {
300 x: (LittleEndian::read_i16(&buf[0..2]) as f32) * scale,
301 y: (LittleEndian::read_i16(&buf[2..4]) as f32) * scale,
302 z: (LittleEndian::read_i16(&buf[4..6]) as f32) * scale,
303 })
304 }
305
306 pub fn get_magnetometer(&mut self) -> Result<Vector3, Error> {
309 self.set_page(RegisterPage::Page0)?;
310 let mut buf = [0u8; 6];
311
312 for (i, byte) in buf.iter_mut().enumerate() {
314 *byte = self
315 .i2c
316 .smbus_read_byte_data((MagRegisters::XLsb as u8) + i as u8)?;
317 }
318
319 let scale = 1.0 / 16.0;
321 Ok(Vector3 {
322 x: (LittleEndian::read_i16(&buf[0..2]) as f32) * scale,
323 y: (LittleEndian::read_i16(&buf[2..4]) as f32) * scale,
324 z: (LittleEndian::read_i16(&buf[4..6]) as f32) * scale,
325 })
326 }
327
328 pub fn get_gyroscope(&mut self) -> Result<Vector3, Error> {
331 self.set_page(RegisterPage::Page0)?;
332 let mut buf = [0u8; 6];
333
334 for (i, byte) in buf.iter_mut().enumerate() {
336 *byte = self
337 .i2c
338 .smbus_read_byte_data((GyroRegisters::XLsb as u8) + i as u8)?;
339 }
340
341 let scale = 1.0 / 16.0;
343 Ok(Vector3 {
344 x: (LittleEndian::read_i16(&buf[0..2]) as f32) * scale,
345 y: (LittleEndian::read_i16(&buf[2..4]) as f32) * scale,
346 z: (LittleEndian::read_i16(&buf[4..6]) as f32) * scale,
347 })
348 }
349
350 pub fn get_temperature(&mut self) -> Result<i8, Error> {
353 self.set_page(RegisterPage::Page0)?;
354 let temp = self
355 .i2c
356 .smbus_read_byte_data(StatusRegisters::Temperature as u8)? as i8;
357 Ok(temp)
358 }
359
360 pub fn get_calibration_status(&mut self) -> Result<u8, Error> {
366 self.set_page(RegisterPage::Page0)?;
367 let status = self
368 .i2c
369 .smbus_read_byte_data(StatusRegisters::CalibStat as u8)?;
370 Ok(status)
371 }
372}
373
374pub struct Bno055Reader {
375 data: Arc<RwLock<BnoData>>,
376 command_tx: mpsc::Sender<ImuCommand>,
377}
378
379impl Bno055Reader {
380 pub fn new(i2c_bus: &str) -> Result<Self, Error> {
381 let data = Arc::new(RwLock::new(BnoData::default()));
382 let (command_tx, command_rx) = mpsc::channel();
383
384 let imu = Bno055::new(i2c_bus)?;
387
388 Self::start_reading_thread_with_imu(imu, Arc::clone(&data), command_rx);
390
391 Ok(Bno055Reader { data, command_tx })
392 }
393
394 fn start_reading_thread_with_imu(
395 mut imu: Bno055,
396 data: Arc<RwLock<BnoData>>,
397 command_rx: mpsc::Receiver<ImuCommand>,
398 ) {
399 thread::spawn(move || {
400 debug!("BNO055 reading thread started");
401 loop {
402 if let Ok(command) = command_rx.try_recv() {
404 match command {
405 ImuCommand::SetMode(mode) => {
406 if let Err(e) = imu.set_mode(mode) {
407 error!("Failed to set mode: {}", e);
408 }
409 }
410 ImuCommand::Reset => {
411 if let Err(e) = imu.reset() {
412 error!("Failed to reset: {}", e);
413 }
414 }
415 ImuCommand::Stop => break,
416 }
417 }
418
419 let mut data_holder = BnoData::default();
421
422 if let Ok(quat) = imu.get_quaternion() {
423 data_holder.quaternion = quat;
424 } else {
425 warn!("Failed to get quaternion");
426 }
427
428 if let Ok(euler) = imu.get_euler_angles() {
429 data_holder.euler = euler;
430 } else {
431 warn!("Failed to get euler angles");
432 }
433
434 if let Ok(accel) = imu.get_accelerometer() {
435 data_holder.accelerometer = accel;
436 } else {
437 warn!("Failed to get accelerometer");
438 }
439
440 if let Ok(gyro) = imu.get_gyroscope() {
441 data_holder.gyroscope = gyro;
442 } else {
443 warn!("Failed to get gyroscope");
444 }
445
446 if let Ok(mag) = imu.get_magnetometer() {
447 data_holder.magnetometer = mag;
448 } else {
449 warn!("Failed to get magnetometer");
450 }
451
452 if let Ok(linear_accel) = imu.get_linear_acceleration() {
453 data_holder.linear_acceleration = linear_accel;
454 } else {
455 warn!("Failed to get linear acceleration");
456 }
457
458 if let Ok(gravity) = imu.get_gravity_vector() {
459 data_holder.gravity = gravity;
460 } else {
461 warn!("Failed to get gravity vector");
462 }
463
464 if let Ok(temp) = imu.get_temperature() {
465 data_holder.temperature = temp;
466 } else {
467 warn!("Failed to get temperature");
468 }
469
470 if let Ok(status) = imu.get_calibration_status() {
471 data_holder.calibration_status = status;
472 } else {
473 warn!("Failed to get calibration status");
474 }
475
476 if let Ok(mut imu_data) = data.write() {
478 *imu_data = data_holder;
479 }
480
481 thread::sleep(Duration::from_millis(10));
483 }
484 debug!("BNO055 reading thread exiting");
485 });
486 }
487
488 pub fn set_mode(&self, mode: OperationMode) -> Result<(), Error> {
489 self.command_tx
490 .send(ImuCommand::SetMode(mode))
491 .map_err(|_| Error::WriteError)
492 }
493
494 pub fn reset(&self) -> Result<(), Error> {
495 self.command_tx
496 .send(ImuCommand::Reset)
497 .map_err(|_| Error::WriteError)
498 }
499
500 pub fn stop(&self) -> Result<(), Error> {
501 self.command_tx
502 .send(ImuCommand::Stop)
503 .map_err(|_| Error::WriteError)
504 }
505
506 pub fn get_data(&self) -> Result<BnoData, Error> {
507 self.data
508 .read()
509 .map(|data| *data)
510 .map_err(|_| Error::ReadError)
511 }
512}
513
514impl Drop for Bno055Reader {
515 fn drop(&mut self) {
516 let _ = self.stop();
517 }
518}
519
520#[derive(Debug)]
521pub enum ImuCommand {
522 SetMode(OperationMode),
523 Reset,
524 Stop,
525}