M5Stack FIRE に 9軸IMU を外付けセンサデータを取得してみた (ICM-20948)

[M5Stack](https://m5stack.com/) に外付けした 9軸IMU(Inertial measurement unit) から各種センサデータを取得した記録を共有します。M5Stack 単体で 9軸IMU を搭載していれば良かったのですが、執筆時点(2023年8月27日)では M5Stack シリーズに9軸IMUを搭載している機種は販売終了していました(過去に M5Stack Gray という機種が搭載していた)。本記事では M5Stack FIRE とは別に 9軸IMU を購入して外付けしました。

## 実現したかったこと

開発中の 9軸IMU のセンサ補正プログラムを動作確認するための実機環境を構築したいというモチベーションで M5Stack を使い始めました。あまり深くは考えずに始めたのですが、結果的には1日程度の時間でセンサデータの取得まで実現できたので選択肢として良かったと思います。

## 購入した製品

M5Stack のファームウェアを更新するためのパソコンを所持しているものとします。本記事では Windows ユーザを想定していますが、macOS や Linux にも対応しています。

- [M5Stack FIRE Development Kit](https://www.switch-science.com/products/9009) (本体)
- [Qwiic ICM-20948 搭載 9DoF IMU モジュール](https://www.switch-science.com/products/5854) (9軸IMU)
- [Qwiic-Grove アダプタケーブル (100 mm)](https://www.switch-science.com/products/5327) (本体と9軸IMUをつなぐケーブル)

上記はすべて[スイッチサイエンス社のオンラインショップ](https://www.switch-science.com/)で購入しました。

## 初期設定

M5Stack で開発するまでの設定を簡単にまとめます。ここに書いてあることが最新ではなかったり正しくない可能性もあるので常に公式ページも確認するようにしてください。

https://docs.m5stack.com/en/core/fire_v2.7

### USBドライバのインストール

[M5Stack FIRE](https://docs.m5stack.com/en/core/fire_v2.7) のページに有る「Driver Installation」からドライバ (CP210X/CP34X) をインストールします。チップのバージョンが分からなければ両方を入れても大丈夫そうです。

### ハードウェア接続

M5Stack FIRE では USB-C ケーブルを接続します。電源ボタンを1回押すと起動します。終了するには電源ボタンを2回押します。

### ファームウェアの書き換え

[M5Burner](https://docs.m5stack.com/en/quick_start/m5core/uiflow) というツールを使ってファームウェアを書き換えます。「Burning tool」から開発環境の OS に合ったツールをダウンロードしてインストールします。「UIFlow_Fire」で「Burn」を押すと書き換えが実行されます。WiFi への接続情報が必要です。

### 開発環境構築

ファームウェア書き換えが終わると実機に接続して開発することができるようになります。本記事では「UIFlow」を使って開発を行います。オンラインのエディタを使って開発できます。

https://flow.m5stack.com/

上記のページにアクセスすると UIFlow のバージョンを選択する画面が表示されます。M5Stack FIRE には左の UIFlow 1.0 を選択します。
「Api key」には本体の画面にファームウェア書き換え時に表示される API KEY を入力します。 Device は Fire を選択します。「OK」を押すと開発が開始できます。

## M5Stack のアプリケーション開発

UIFlow を使ってアプリケーション開発をしていきます。今回はIMUのセンサデータを取得するのがアプリケーションの目的です。

### M5Stack で Hello World

「グラフィック」から「テキスト」を選んで「""」に「"Hello World"」を入力して「Setup」ブロックの下に配置します。「Run」ボタンを押すと実機にテキストが表示されます。何も読まずにコードを書こうとしたらテキスト表示する方法を見つけるのに苦戦しました。
![uiflow_helloworld.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8421/0e052798-4686-fbd5-6b7c-72d96c69fae5.png)

### M5Stack と IMU のケーブル接続

[IMUのハードウェア仕様書](https://cdn.sparkfun.com/assets/c/7/8/f/8/15335_9DoF_Schematic.pdf) で 1.8 - 5.5[V] の入力電圧に対応していることを確認しました。M5Stack FIRE の外部ポートの出力 5.1[V] をそのまま IMU に突っ込んでも大丈夫そうです。

M5Stack FIRE と IMU をケーブルでハードウェア接続してみましょう。
M5Stack FIRE は [Grove](https://wiki.seeedstudio.com/Grove_System/) というモジュール規格に対応しています。Grove 対応の 9軸IMU も執筆時点では購入可能なものが見当たらなかったので [Qwiic](https://www.sparkfun.com/qwiic) 対応の IMU を使うことにしました。Grove-Qwiic のアダプタケーブルを使えば接続できました。めでたしめでたし、とはうまくいかないもので、Grove 側のコネクタについているツメが M5Stack FIRE に合わない状態でした。仕方がないのでツメをニッパーで削り取ってツメなしにして対応しました。M5Stack FIRE の Port A に接続しました。

![PXL_20230827_115724815.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8421/8fb5db5f-241a-5719-7e87-2618faa2c5d3.jpeg)

### 外付けモジュールを制御するには

本記事では [I2C(Inter-Integrated Circuit)](https://learn.sparkfun.com/tutorials/i2c/all) というプロトコルを使って IMU と通信します。先ほど接続して Port A が I2C に対応しています。M5Stack でも I2C をソフトウェアで扱う環境が用意されています。UIFlow では「ハードウェア」の「I2C Master」にあるブロックを使って I2C 対応ポートに接続した機器を制御します。

### IMU のセンサデータを取得する

いよいよ ICM-20948 からデータを取得してみましょう。デバイスの[データシート](https://invensense.tdk.com/wp-content/uploads/2021/10/DS-000189-ICM-20948-v1.5.pdf)に仕様の詳細が書いてあります。
大まかな実装の流れを書きます。

1. ポートと通信速度の設定(`portA` 10kHz): `i2c0` Master `portA` freq `100000`
1. ポートアドレスの設定(`0x69` で接続): `i2c0` Set slave addr `0x69`: 
1. スリープモードの解除(`PWR_MGMT_1`): `i2c0` のレジスタアドレス `0x06` の6ビット目を `0` にした値を書き出す
1. 加速度センサとジャイロセンサの有効化(`PWR_MGMT_2`): `i2c0` のレジスタアドレス `0x07` の 0-5 bit を `0` にした値を書き出す
1. 加速度センサ値の取得(`ACCEL_ZOUT_H`): `i2c0` のレジスタアドレス `0x31` から2バイト読み取る
1. ジャイロセンサ値の取得(`GYRO_XOUT_H`): `i2c0` のレジスタアドレス `0x33` から2バイト読み取る

加速度センサとジャイルセンサの値を取得できました。ボタンAを押したときにセンサの値を画面に表示する機能を実装しました。

![m5stack_fire_6dof.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8421/1d2bc5fe-b035-1e61-4240-6aeaf594bc51.png)

生成されたコード

https://gist.github.com/algas/c19acca7bbe7983e7b92953c8cf67173

UIFlow で Python コードを書くためには Python モードに変更する必要があります。UIFlow 上部のタブで「Blockly」ではなく「Python」を選択すると Python コードを記述することができる画面に切り替わります。

加速度センサとジャイルセンサの値を取得できましたが、地磁気センサだけはさらに外部接続になっているようで簡単には呼び出しできそうにありません。

### 地磁気センサデータ取得用のコードを書く

ICM-20948 のデータシートの情報だけでは地磁気センサのデータ取得方法を読み解くことができませんでした。解決方法を調べていくと IMU の提供元 [SparkFun](https://www.sparkfun.com/) が配布している Python ライブラリを発見しました。このライブラリを使えば地磁気センサのデータも取得できそうです。

https://github.com/sparkfun/Qwiic_9DoF_IMU_ICM20948_Py

上記のライブラリを元にして地磁気センサも含めた9軸すべての値を取得するコードを書きました。データ読み書きのコードの一部を M5Stack 用に変更してあります。ボタンAを押すと押した瞬間のセンサの値が画面に表示されます。

![PXL_20230828_122930081.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8421/786de859-9a81-1525-114f-4e052eba499f.jpeg)

https://gist.github.com/algas/c3cd20db98b3cbfca41124f988ee013b

## まとめ

M5Stack FIRE に 9軸IMU を外付けしてセンサデータを取得できました。ハードウェアを開封して環境構築するところから始めて地磁気センサのデータを取得するまでに1日程度の時間がかかりました。

## あとがき

Grove 対応の 10自由度IMU([GROVE - IMU 10DOF v2.0](https://www.switch-science.com/products/2855)) があったことに執筆中に気づいてしまいました。これを買ったら Grove 対応機器同士をつなぐだけだったのでもう少し楽だったかもしれません。

## 参考資料

https://qiita.com/t_kumazawa/items/2107e3363cab370be744

https://ss-sholaw-wmmc.hatenablog.com/entry/2020/12/12/235938