BNO055をESP32やTeensyで使う



![SS 192.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/a41cd967-f549-8677-1efa-2e99bf69518d.png) 秋月BNO055は安住の地かもしれない...ESP32に限っては

## 前回記事
https://qiita.com/Ninagawa_Izumi/items/fb48fa06ef29e83ec517

の続きです。

## やること
9軸センサの**BNO055**とESP32devkitCの組み合わせで動かします。
前回記事のBMX055と名前が似ていますが、BNO055の方はセンサーフュージョンも内臓していてマイコンへの処理負担も少なく、とても便利です。

いま秋月で買おうという場合、店内のセンサー売り場でBMX055の近くを探してもBNO055はありません。やっぱり品切れかとガッカリする前にぜひ**店外のワゴン**をチェックしてみてください。それでもなければ店員さんに。(※2022年5月上旬現在の情報です)

## 準備物・実施環境
- ESP32devkitC
- 秋月の9軸センサ AE-BNO055-BO
- Arduino IDE
- Adafruit_BNO055 ライブラリ

## 接続
| AE-BNO055-BO | ESP32devkitC |
|-----------:|:------------|
| VCC      | 5V| 
| GND     | GND| 
| SDA       | SDA(Pin21)|
| SCL       | SCL(Pin22)|

ESP32DevkitCのSDA,SCLのデフォルトピンが上記になります。
デフォルトピン以外での動作もできると思いますが手元ではまだ確認していません。

## ジャンパピン
| AE-BNO055 |ジャンパ内容||
|-----------:|-----|:------------|
| JP1|アドレス設定|オープン(0x28)| 
| JP2,JP3|モード|オープン(I2C)| 
| JP4,JP5|モード|ショート(I2C)|

今回は出荷時の設定そのままで使用できます。
BXM055に比べてジャンパがさらに小さいです。
目視だとJP4,JP5がオープンのように見えますが、ジャンパの間に凸があり、しっかりショートしてます。（写真の左下にジャンパがあるのが見えるでしょうか。必要に応じてここをはんだでブリッジします。）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/84d6548b-cadf-5ecc-5a6a-0e099cfb0ac7.png" width=400>


## 動作確認
下記のサイトが参考になります。まずはプロセシングでうさぎを回しましょう。
- [ホームメードガービッジ：モーションセンサ BNO055 モジュールの動作確認 ](https://homemadegarbage.com/bno055-01)

上記の補足としまして、現在はAdafruit_BNO055ライブラリは、ArduinoIDEのライブラリ管理より「BNO055」を検索してインストールすることができます。

#### 動かない時！
接触不良は大いにあり得ます。接点に対してなかなかセンシティブなようです。
ブレッドボードにしっかり刺さっていても内部的に接触不良を起こしている場合や、ジャンパワイヤ自体が内部で切れている場合なども十分あり得ますので、
動かない場合には
①接続の確認 ②結線ワイヤーやブレッドボードの交換
の順で確認してみると良いかもしれません。

## ESP32のスケッチ

Adafruit_BNO055 ライブラリのサンプルスケッチを少し改造し、割り込み処理にて一通りのデータをシリアルに出力します。
スケッチではまず、センサフュージョンによるロール軸、ピッチ軸、ヨー軸の推定値だけが出るようにしてあります。


```c++:ESP32系

#include <Wire.h>
#include <Adafruit_BNO055.h>
#include <Ticker.h>
Ticker bno055ticker; //タイマー割り込み用のインスタンス
#define BNO055interval 10 //何ms間隔でデータを取得するか

//Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x28, &Wire); //ICSの名前, デフォルトアドレス, 謎
Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x28);

void setup(void)
{
  pinMode(21, INPUT_PULLUP); //SDA 21番ピンのプルアップ(念のため)
  pinMode(22, INPUT_PULLUP); //SDA 22番ピンのプルアップ(念のため)

  Serial.begin(115200);
  Serial.println("Orientation Sensor Raw Data Test"); Serial.println("");

  if (!bno.begin()) // センサの初期化
  {
    Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
    while (1);
  }

  delay(1000);

  /* Display the current temperature */
  int8_t temp = bno.getTemp();
  Serial.print("Current Temperature: ");
  Serial.print(temp);
  Serial.println(" C");
  Serial.println("");

  bno.setExtCrystalUse(false);

  Serial.println("Calibration status values: 0=uncalibrated, 3=fully calibrated");
  bno055ticker.attach_ms(BNO055interval, get_bno055_data);
}

void get_bno055_data(void)
{
  // Possible vector values can be:
  // - VECTOR_ACCELEROMETER - m/s^2
  // - VECTOR_MAGNETOMETER  - uT
  // - VECTOR_GYROSCOPE     - rad/s
  // - VECTOR_EULER         - degrees
  // - VECTOR_LINEARACCEL   - m/s^2
  // - VECTOR_GRAVITY       - m/s^2
  
  /*
  // キャリブレーションのステータスの取得と表示
  uint8_t system, gyro, accel, mag = 0;
  bno.getCalibration(&system, &gyro, &accel, &mag);
  Serial.print("CALIB Sys:");
  Serial.print(system, DEC);
  Serial.print(", Gy");
  Serial.print(gyro, DEC);
  Serial.print(", Ac");
  Serial.print(accel, DEC);
  Serial.print(", Mg");
  Serial.print(mag, DEC);
  */
  
  /*
  // ジャイロセンサ値の取得と表示
  imu::Vector<3> gyroscope = bno.getVector(Adafruit_BNO055::VECTOR_GYROSCOPE);
  Serial.print(" 　Gy_xyz:");
  Serial.print(gyroscope.x());
  Serial.print(", ");
  Serial.print(gyroscope.y());
  Serial.print(", ");
  Serial.print(gyroscope.z());
  */
  
  /*
  // 加速度センサ値の取得と表示
  imu::Vector<3> accelermetor = bno.getVector(Adafruit_BNO055::VECTOR_ACCELEROMETER);
  Serial.print(" 　Ac_xyz:");
  Serial.print(accelermetor.x());
  Serial.print(", ");
  Serial.print(accelermetor.y());
  Serial.print(", ");
  Serial.print(accelermetor.z());
  */
  
  /*
  // 磁力センサ値の取得と表示
  imu::Vector<3> magnetmetor = bno.getVector(Adafruit_BNO055::VECTOR_MAGNETOMETER);
  Serial.print(" 　Mg_xyz:");
  Serial.print(magnetmetor .x());
  Serial.print(", ");
  Serial.print(magnetmetor .y());
  Serial.print(", ");
  Serial.print(magnetmetor .z());
  */

  // センサフュージョンによる方向推定値の取得と表示
  imu::Vector<3> euler = bno.getVector(Adafruit_BNO055::VECTOR_EULER);
  Serial.print(" 　DIR_xyz:");
  Serial.print(euler.x());
  Serial.print(", ");
  Serial.print(euler.y());
  Serial.print(", ");
  Serial.print(euler.z());

  /*
    // センサフュージョンの方向推定値のクオータニオン
    imu::Quaternion quat = bno.getQuat();
    Serial.print("qW: ");
    Serial.print(quat.w(), 4);
    Serial.print(" qX: ");
    Serial.print(quat.x(), 4);
    Serial.print(" qY: ");
    Serial.print(quat.y(), 4);
    Serial.print(" qZ: ");
    Serial.print(quat.z(), 4);
    Serial.print("\t\t");
  */

  Serial.println();
}

void loop(void)
{
}
```
## 実行
上記スケッチの実行後にArduinoIDEのシリアルプロッタを開くと、センサの動きに合わせて値が変化する様子が確認できます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/84c18218-aacb-1418-0a49-55d261b530b6.png" width="400">

## 詳細な情報の表示
上記スケッチの「／＊」「＊／」のペアを消すことで、必要なデータを表示させることができます。
今回のスケッチで表示できるのは下記です。

- キャリブレーション度（システム、ジャイロ、加速度、磁力）
- ジャイロセンサのx,y,z値
- 加速度センサのx,y,z値
- 磁力センサのx,y,z値
- センサフュージョンによるロール軸、ピッチ軸、ヨー軸の推定値
- センサフュージョンのクオータニオン値のw,x,y,z

キャリブレーション度は3がMaxになります。
起動後にぐるぐる回すと磁力のキャリブレーション度が3になるのが確認できると思います。

## Teensyのスケッチ
TeensyではタイマーとしてIntervalTimer.hが使えます。
タイマー割り込みでセンサーフュージョンの結果の値を表示してみます。

```c++:Teensy系
/* ライブラリ導入 */
#include <Adafruit_BNO055.h>            // 9軸センサBNO055用のライブラリ
#include <IntervalTimer.h>
#include <SPI.h>

/* BNO055用変数 */
Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x28, &Wire);
IntervalTimer bnoTimer;

void get_bno055_data(void)
{
    // センサフュージョンによる方向推定値の取得と表示
    imu::Vector<3> euler = bno.getVector(Adafruit_BNO055::VECTOR_EULER);
    Serial.print("  DIR_xyz:");
    Serial.print(euler.x());
    Serial.print(", ");
    Serial.print(euler.y());
    Serial.print(", ");
    Serial.print(euler.z());
    Serial.println();
}

void setup()
{
    Serial.begin(115200); // シリアルモニター表示
    delay(100);

    if (!bno.begin()) // センサの初期化
    {
        Serial.print("No BNO055 detected ... Check your wiring or I2C ADDR.");
        while (1);
    }
    bnoTimer.begin(get_bno055_data, 10000); // 0.01 seconds
}

void loop()
{
}
```
# Teensyで使うときの注意点

Teensy4.0などの場合、通信が安定しない場合があります。
SDAとSCAをプルアップすることで安定することが確認できました。（Teensyの内部プルアップの設定でも安定できる可能性があるかもしれませんが方法がわかりません。）
サンプリングレート100Hz、プルアップ抵抗1kΩで試しました。
ESP32の場合には外部プルアップなしでも使えました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/1a5f1e8d-44ad-dad8-a155-2b2efc6128ab.png" width=400>

ただし、通信速度はかなりあやしいです。SPIと並行で使っているのですが、何が悪いのか、MPU6050と比べてかなり通信速度が遅いです。1回の通信に5msぐらいかかっているような感じです。センサーのデータだけを取得したい場合は問題ありませんが、他の高速処理と組み合わせて使う場合には注意が必要です。

# おわりに

ほそぼそと６軸、９軸のセンサーを使う記事を書いてきましたが、ようやく使い勝手の良い９軸センサに辿り着きました。
6050のDMPもかなり精度が良かったですし、9250も使いこなせればかなり便利そうです。
今回のBNO055もドキュメントをよく読めばさらに効率的に使うことができそうです。

記事についてご指摘やアドバイスがあればぜひコメント欄などで教えていただければ幸いです。

#### 前回記事
https://qiita.com/Ninagawa_Izumi/items/fb48fa06ef29e83ec517

#### 多軸センサーシリーズ最初の記事
https://qiita.com/Ninagawa_Izumi/items/8d9e023547c20bc171a0