M5StickC PlusのHATにWire1を使いたい！

## はじめに
M5StickC Plusでは，Grove端子のi2cの他にHAT用のi2cも同時に使いたいシチュエーションがあります．
つまりi2cの通信を2つ同時に行いたいわけですが，これは`Wire`オブジェクトと`Wire1`オブジェクトを同時に使うことで簡単に実現できる…ように見えて地味にハマったので備忘録として残しておきます．


## 前提

- 諸々の開発環境は準備済みであること
- [1]にあるサンプルコードのように，`M5StickC.h`を利用しつつ`Wire`のみを使うコードは動作していること
- ライブラリのバージョンは以下の通り
  - m5stack/M5Unified@^0.1.9



## 問題

[2]のページによれば，以下のように`Wire`と`Wire1`をbeginし直せばどちらも使えるはず…とのことでした．

```cpp
// [2]から引用

void setup() {
  M5.begin();

  Wire.begin(32, 33);
  Wire1.begin(0, 26);
}
```

これに基づいて書いてみたコードがこちら．

```cpp
// これでは動きませんでした

#include <M5Unified.h>
#include <Wire.h>

void setup() {

  M5.begin();
  Wire1.begin(0, 26, 100000UL);

  Serial.begin(115200);
}


void loop() {
  Serial.println("start-transmission");
  Wire1.beginTransmission(0x38);

  Serial.print("start-write: ");
  bool rw = Wire1.write(0x02);
  Serial.println(rw);
  
  uint8_t re = Wire1.endTransmission();
  Serial.print("end-transmission: ");
  Serial.println(re);

  Wire1.requestFrom(0x38, 3);
  bool w1 = Wire1.available();
  Serial.print("available: ");
  Serial.println(w1);
  Serial.println("");

  if(w1){
    Serial.println("OK");
  }
  M5.update();
  delay(100);
}
```

実際のコードから関係ない処理は全部消して，見やすくなるように書き直しています．`Serial.println("OK");`が呼び出されればHATとの通信ができているものとして考えてください．このコードだと，`Wire1.endTransmission()`の戻り値が`ESP_FAIL`を意味する2になってコケていました．

`M5Unified.h`を使っていることくらいしか違いがなく𝕏で嘆いていたところ，有識者の方々から色んなコメントが届きました…ありがたや…

## 解決策

鍵になったのはらびやんさんから教えていただいた`Wire1.end()`でした


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ひとまずこれでM5.begin後に たなかさんの助言どおり WireとWire1を再割当てすると良いと思います。<br>あとは…もしかすると、一度Wire1.endしてからでないとWire1.beginできないかも。。</p>&mdash; らびやん (@lovyan03) <a href="https://twitter.com/lovyan03/status/1711607258008977637?ref_src=twsrc%5Etfw">October 10, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

このアドバイスをもとに以下のように修正したところ，無事HAT用の端子へWire1を割り当てて通信することができました！！一回`end()`呼ばないといけないとは…自力では辿り着けなかった気がします．感謝🙏🙏（`delay(100);`はなんとなくおきもちで入れているのでなくても良いかもしれません）

```cpp
// これなら動く

#include <M5Unified.h>
#include <Wire.h>

void setup() {

  //----------------------ここから-------------------------
  auto cfg = M5.config();
  cfg.pmic_button = false;  // これで電源ボタン状態取得を行わなくなる
  cfg.internal_imu = false; // これで内蔵IMUを使わなくなる
  cfg.internal_rtc = false; // これで内蔵RTCを使わなくなる
  M5.begin(cfg);

  Wire1.end();
  delay(100);
  //----------------------ここまで追加----------------------

  Wire1.begin(0, 26, 100000UL);

  Serial.begin(115200);
}


void loop() {
  Serial.println("start-transmission");
  Wire1.beginTransmission(0x38);

  Serial.print("start-write: ");
  bool rw = Wire1.write(0x02);
  Serial.println(rw);
  
  uint8_t re = Wire1.endTransmission();
  Serial.print("end-transmission: ");
  Serial.println(re);

  Wire1.requestFrom(0x38, 3);
  bool w1 = Wire1.available();
  Serial.print("available: ");
  Serial.println(w1);
  Serial.println("");

  if(w1){
    Serial.println("OK");
  }
  M5.update();
  delay(100);
}
```

## おわりに
いかがでしたか？groveポートとHATを同時に使いたいシチュエーション，それなりにあると思います．どなたかのお役に立てたら幸いです．各コメント全文は[3]から辿れます．

それでは．


## 参考
[[1] Joystick hat 公式ドキュメント](https://docs.m5stack.com/en/hat/hat-joystick?id=communication-protocol)

https://docs.m5stack.com/en/hat/hat-joystick?id=communication-protocol

[[2] M5StickC非公式日本語リファレンス](https://lang-ship.com/reference/unofficial/M5StickC/Peripherals/I2C/#2grovei2c)

https://lang-ship.com/reference/unofficial/M5StickC/Peripherals/I2C/#2grovei2c

[[3] 元ツイのスレッド](https://x.com/eternalfriend17/status/1711503408245735687?s=20)

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">M5StickC Plusくん、Groveのi2cとHATのi2cは同時に叩けないんかい！！！！！！詰んだ！！！今からESP-NOWまじで導入するか…？</p>&mdash; 孔明 (@eternalfriend17) <a href="https://twitter.com/eternalfriend17/status/1711503408245735687?ref_src=twsrc%5Etfw">October 9, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>