Перейти к контенту
← Назад

Инерциальный модуль BNO055

BNO055

// Copyright (c) 2026 ЦПМК по информатике
// Licensed under the MIT License.
// https://robot.mipt.ru/

#include <Wire.h>

#define BNO_ADDR 0x29         // I2C адрес BNO055 (0x28 или 0x29)
#define BNO_CHIP_ID_REG 0x00  // Регистр идентификации (должен вернуть 0xA0)

// --- Структура для хранения данных ---
struct BNOData {
  float euler[3];   // [0]-Курс(H), [1]-Крен(R), [2]-Тангаж(P). Эйлеровы углы в градусах.
  float quat[4];    // [0]-W, [1]-X, [2]-Y, [3]-Z. Кватернионы Нормированные значения.
  float linAcc[3];  // Линейное ускорение без гравитации (X, Y, Z) в m/s^2.
  float grav[3];    // Вектор силы тяжести (X, Y, Z) в m/s^2.
  float acc[3];     // Общее ускорение (X, Y, Z) в m/s^2.
  float gyro[3];    // Угловая скорость (X, Y, Z) в Dps (град/сек).
  float mag[3];     // Напряженность магн. поля (X, Y, Z) в uT (микротесла).
  int8_t temp;      // Температура чипа в градусах Цельсия.
  uint8_t cal[4];   // Статус калибровки [0]-Система, [1]-Гироскоп, [2]-Акселерометр, [3]-Магнитометр (0..3).
};

// Функция записи в регистр
void writeReg(uint8_t reg, uint8_t val) {
  Wire.beginTransmission(BNO_ADDR);
  Wire.write(reg);
  Wire.write(val);
  Wire.endTransmission();
  delay(30);
}

// Функция чтения нескольких регистров подряд
void readRegs(uint8_t reg, uint8_t *buf, uint8_t len) {
  Wire.beginTransmission(BNO_ADDR);
  Wire.write(reg);
  Wire.endTransmission(false);
  Wire.requestFrom((uint8_t)BNO_ADDR, (uint8_t)len);
  for (uint8_t i = 0; i < len; i++) {
    buf[i] = Wire.read();
  }
}

// --- Функция чтения всех данных ---
void readBNOData(BNOData &data) {

  uint8_t buffer[8];  // Буфер для приема данных (макс 8 байт для кватернионов)

  // Регистр 0x08: Акселерометр (X,Y,Z). 1 LSB = 1/100 m/s^2
  readRegs(0x08, buffer, 6);
  for (int i = 0; i < 3; i++) data.acc[i] = (int16_t)((buffer[i * 2 + 1] << 8) | buffer[i * 2]) / 100.0;

  // Регистр 0x0E: Магнитометр (X,Y,Z). 1 LSB = 1/16 uT
  readRegs(0x0E, buffer, 6);
  for (int i = 0; i < 3; i++) data.mag[i] = (int16_t)((buffer[i * 2 + 1] << 8) | buffer[i * 2]) / 16.0;

  // Регистр 0x14: Гироскоп (X,Y,Z). 1 LSB = 1/16 Dps
  readRegs(0x14, buffer, 6);
  for (int i = 0; i < 3; i++) data.gyro[i] = (int16_t)((buffer[i * 2 + 1] << 8) | buffer[i * 2]) / 16.0;

  // Регистр 0x1A: Эйлеровы углы (H,R,P). 1 LSB = 1/16 Degree
  readRegs(0x1A, buffer, 6);
  for (int i = 0; i < 3; i++) data.euler[i] = (int16_t)((buffer[i * 2 + 1] << 8) | buffer[i * 2]) / 16.0;

  // Регистр 0x20: Кватернионы (W,X,Y,Z). 1 LSB = 1/2^14 (16384)
  readRegs(0x20, buffer, 8);
  for (int i = 0; i < 4; i++) data.quat[i] = (int16_t)((buffer[i * 2 + 1] << 8) | buffer[i * 2]) / 16384.0;

  // Регистр 0x28: Линейное ускорение (X,Y,Z). 1 LSB = 1/100 m/s^2
  readRegs(0x28, buffer, 6);
  for (int i = 0; i < 3; i++) data.linAcc[i] = (int16_t)((buffer[i * 2 + 1] << 8) | buffer[i * 2]) / 100.0;

  // Регистр 0x2E: Гравитация (X,Y,Z). 1 LSB = 1/100 m/s^2
  readRegs(0x2E, buffer, 6);
  for (int i = 0; i < 3; i++) data.grav[i] = (int16_t)((buffer[i * 2 + 1] << 8) | buffer[i * 2]) / 100.0;

  // Регистр 0x34: Температура. 1 LSB = 1°C
  readRegs(0x34, buffer, 1);
  data.temp = (int8_t)buffer[0];

  // Регистр 0x35: Статус калибровки (битовое поле)
  readRegs(0x35, buffer, 1);
  data.cal[0] = (buffer[0] >> 6) & 0x03;  // Система
  data.cal[1] = (buffer[0] >> 4) & 0x03;  // Гироскоп
  data.cal[2] = (buffer[0] >> 2) & 0x03;  // Акселерометр
  data.cal[3] = buffer[0] & 0x03;         // Магнитометр
}

void printOutput(BNOData &data) {  // Функция для вывода в Serial всех измеряемых значений
  Serial.println("--- BNO055 FULL REPORT ---");
  Serial.print("Euler: ");
  Serial.print(data.euler[0]);
  Serial.print(" ");
  Serial.print(data.euler[1]);
  Serial.print(" ");
  Serial.println(data.euler[2]);
  Serial.print("Quat:  ");
  Serial.print(data.quat[0]);
  Serial.print(" ");
  Serial.print(data.quat[1]);
  Serial.print(" ");
  Serial.print(data.quat[2]);
  Serial.print(" ");
  Serial.println(data.quat[3]);
  Serial.print("LinAcc:");
  Serial.print(data.linAcc[0]);
  Serial.print(" ");
  Serial.print(data.linAcc[1]);
  Serial.print(" ");
  Serial.println(data.linAcc[2]);
  Serial.print("Grav:  ");
  Serial.print(data.grav[0]);
  Serial.print(" ");
  Serial.print(data.grav[1]);
  Serial.print(" ");
  Serial.println(data.grav[2]);
  Serial.print("TotalAcc:");
  Serial.print(data.acc[0]);
  Serial.print(" ");
  Serial.print(data.acc[1]);
  Serial.print(" ");
  Serial.println(data.acc[2]);
  Serial.print("Gyro:  ");
  Serial.print(data.gyro[0]);
  Serial.print(" ");
  Serial.print(data.gyro[1]);
  Serial.print(" ");
  Serial.println(data.gyro[2]);
  Serial.print("Mag:   ");
  Serial.print(data.mag[0]);
  Serial.print(" ");
  Serial.print(data.mag[1]);
  Serial.print(" ");
  Serial.println(data.mag[2]);
  Serial.print("Temp:  ");
  Serial.print(data.temp);
  Serial.println(" C");
  Serial.print("Calib: [S:");
  Serial.print(data.cal[0]);
  Serial.print(" G:");
  Serial.print(data.cal[1]);
  Serial.print(" A:");
  Serial.print(data.cal[2]);
  Serial.print(" M:");
  Serial.print(data.cal[3]);
  Serial.println("]");
  Serial.println();
}


void plotEuler(const BNOData &data) {  // Функция для вывода в Serial Plotter углов Эйлера
  // Формат для Serial Plotter: "Название1:Значение1,Название2:Значение2..."

  Serial.print("Heading:");  // [0]-Курс(H)
  Serial.print(data.euler[0]);
  Serial.print(",");

  Serial.print("Roll:");  // [1]-Крен(R)
  Serial.print(data.euler[1]);
  Serial.print(",");

  Serial.print("Pitch:");         // [2]-Тангаж(P)
  Serial.println(data.euler[2]);  // В конце обязательно println
}


void setup() {
  Serial.begin(115200);
  Wire.begin();

  // Проверка связи с датчиком, остановит программу если BNO055 не найден
  uint8_t id;
  readRegs(BNO_CHIP_ID_REG, &id, 1);
  if (id != 0xA0) {
    Serial.println("Ошибка: BNO055 не найден!");
    while (1);
  }

  // Настройка датчика
  writeReg(0x3D, 0x00);  // OPR_MODE: CONFIGMODE (нужен для настройки)
  writeReg(0x3F, 0x20);  // SYS_TRIGGER: Сброс системы
  delay(700);            // Время на перезагрузку

  writeReg(0x3B, 0x00);  // UNIT_SEL: m/s2, Dps, Degrees, Celsius (выбор единиц измерения) 
  writeReg(0x3D, 0x0C);  // OPR_MODE: NDOF (Sensor Fusion включен)
  delay(100);
}

void loop() {
  BNOData currentData;

  readBNOData(currentData);  // Вызов функции чтения

  plotEuler(currentData);  // Функция для вывода в Serial Plotter углов Эйлера
//  printOutput(currentData);  // Вывод в Serial всех данных

  delay(100);
}