Pa.HUB(M5Stack用Port A I2C拡張ハブユニット)を使う

***

この記事は[TDU CPSLab Advent Calendar 2023 - Adventar]( https://adventar.org/calendars/8632 )の3日目の記事です。

***

## はじめに

Pa.HUBについて書かれている記事が少なかったため、サクッと利用できるように作成しました。
M5Stack(Basic, Core2など)を使っていると、複数のセンサを同時に利用したいといった場面が訪れるかもしれません。
しかし、M5StackはPort.A(I2C)が一つしかありません(Fire, S3はPort.B(GPIO), Port.C(UART)がありますがI2Cのセンサを接続することができません)。
では、複数のセンサを利用するためにはどうすればよいでしょうか?
->Pa.HUB(M5Stack用Port A I2C拡張ハブユニット)を使うことで解決します。

## Pa.HUBとは?

Pa.HUBはひとつのI2C HY2.0-4Pインターフェースを最大6チャンネルのI2Cに拡張/接続できる拡張ハブとなっています。これをPort.Aに接続することで最大6つのセンサを利用することができます。

![Pa.HUB](https://www.switch-science.com/cdn/shop/products/c9a111f2-ba26-4292-9433-4497c156f2cc_540x540.jpg?v=1699715086)

https://www.switch-science.com/products/7588

https://docs.m5stack.com/en/unit/pahub2

## 実際に使ってみる

Pa.HUBを利用してセンサから値を取得して見ましょう。
今回は、Accel & Gyroセンサを3つ用いてそれぞれから値を取得します。
開発環境はPlatformIOを利用します(PlatformIOの環境構築やプロジェクトの作成は省略します)。

### 機材

M5Stack Core2
Pa.HUB
MPU6886 × 3

### ライブラリ

必要なライブラリをダウンロードしてください。
Pa.HUB
https://github.com/closedcube/ClosedCube_TCA9548A_Arduino
MPU6886
https://github.com/m5stack/M5-ProductExampleCodes/tree/master/Unit/IMU_Unit
https://github.com/adafruit/Adafruit_BMP280_Library
https://github.com/adafruit/Adafruit_BusIO
https://github.com/adafruit/Adafruit_Sensor

## コード

```cpp:main.cpp
#include <Arduino.h>
#include <M5Core2.h>
#include <Wire.h>
#include <ClosedCube_TCA9548A.h>
#include <IMU_6886.h>

//Pa.HUBのアドレス:0x70~0x77
#define PaHub_I2C_ADDRESS 0x70

//sprite -> M5.Lcdで代用しても可
TFT_eSprite sprite(&M5.Lcd);

//Pa.HUB
ClosedCube::Wired::TCA9548A tca9548a;

//MPU6886
IMU_6886 mpu6886;

struct IMUData{
  float accX = 0.0F;
  float accY = 0.0F;
  float accZ = 0.0F;
  float gyroX = 0.0F;
  float gyroY = 0.0F;
  float gyroZ = 0.0F;
};
IMUData data[3];

void setup(){
  M5.begin();
  sprite.createSprite(M5.Lcd.width(), M5.Lcd.height());
  Wire.begin();

  sprite.setTextColor(WHITE);
  sprite.setTextSize(2);

  //PaHubのアドレスを指定0x70~0x77
  tca9548a.address(PaHub_I2C_ADDRESS);
  
  //各チャンネルのセンサの初期化
  for(int i = 0; i < 3; i++){
    tca9548a.selectChannel(i);
    mpu6886.Init(32, 33);
  }
}

void loop(){
  sprite.fillScreen(BLACK);
  sprite.setCursor(0, 0);
  
  //各チャンネルからセンサの値を取得する
  for(int i = 0; i < 3; i++){
    tca9548a.selectChannel(i);
    mpu6886.getAccelData(&data[i].accX, &data[i].accY, &data[i].accZ);
    mpu6886.getGyroData(&data[i].gyroX, &data[i].gyroY, &data[i].gyroZ);

    sprite.printf("Channel: %d\n", i);
    sprite.printf("%5.2f %5.2f %5.2f\n%6.2f %6.2f %6.2f\n",
                      data[i].accX, data[i].accY, data[i].accZ,
                      data[i].gyroX, data[i].gyroY,data[i].gyroZ);
  }
  sprite.pushSprite(0, 0);

  delay(100);
}
```

### メソッド
抑えておくべきメソッドを以下の二つです
1. address( 0x70 ~ 0x77 ) -> Pa.HUBのI2Cアドレスの設定
2. selectChannel(0 ~ 5) -> チャンネルの指定

## 実行結果

各IMUが別々の方向に重力がかかるように向きを調整します。MPU6886では重力がかかる向きは加速度が1.0になります。

![IMG_0416.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617580/dd106630-1d19-181e-3f63-776546835017.jpeg)

CH0のIMUはZ軸の正の方向に重力が、CH1のIMUはZ軸の負の方向に重力が、CH2のX軸の方向に重力がかかるようにしています。
そして、LcdではCH0ではaccZ≒1.0、CH1ではaccZ≒-1.0、CH2ではaccX≒-1.0となっていることから、各センサからデータを取得できました。

## I2Cアドレスの変更
Pa.HUBのI2Cアドレスはハンダ付けをすることによって変更することができます。

<img width=400 src="https://static-cdn.m5stack.com/resource/docs/static/assets/img/product_pics/unit/pahub/pahub_p5.webp">

<img width=400 src="https://static-cdn.m5stack.com/resource/docs/static/assets/img/product_pics/unit/pahub/pahub_p4.webp
">

何もハンダ付けしていない状態は(A2 A1 A0)は(L L L)となるのでI2Cアドレスは0x70となります。
A1をハンダ付けすると(A2 A1 A0)は(L H L)となるのでI2Cアドレスは0x72となります。

### なぜアドレスを変更できる?

センサの中にはI2Cアドレスが同じものが存在することがあります。
例えば、ENVⅲユニット( https://www.switch-science.com/products/7254 )に含まれている気圧センサ(QMP6988)のI2Cアドレスは0x70となっています。
同じアドレスがあってはセンサを特定することができなくなってしまいます。
なので、I2Cアドレスを変更することができるようになっているのです。

## まとめ
Pa.HUBを利用して複数のセンサを取得することができました。
今回は同じセンサを複数接続しましたが、同じセンサでなくても接続することは可能なので、是非Pa.HUBを利用してみてください!!

### 追記
Pa.HUBは認識されているけど、そこからセンサが上手く認識されない場合
PlatformIOのPIO HOME → Librariesから
ClosedCube_TCA9548AとClosedCube_I2C_Driverをインストールしてください

ライブラリにM5Stackが入っていないので
IMU_6886.cppの2行目のM5StackをM5Core2に変更してください