M5Stack CoreS3でM5Stack用MAX3421E搭載 USBモジュールを動かす

# 背景

USB MIDI I/F を M5Stack CoreS3 に接続してアレコレやりたくて M5Stack用MAX3421E搭載 USBモジュール なるものを購入。

https://www.switch-science.com/products/9566?srsltid=AfmBOopDL58J3gRAMc0rt45V1HSeogUPpu3if7qqqN-_QT6yNyF58yFW

S3で使用できるかよく分からなかったが、「DIPスイッチにより異なるホストを調整可能」と書いてあるので何とかなるだろうと思い購入。

案の定そのままでは動作しなかった。

M5Stack Basic では USB Host Shield Library (https://github.com/felis/USB_Host_Shield_2.0) が使用可能だったため何の問題もなかったが、M5Stack CoreS3ではビルドできずダメだった。

# 調査

USB_Host_Shield_2.0のソースコードを調べてみたところ、M5Stack Basic では if defined(ESP32) のところに引っかかって正しいピンアサインになるようだ。

```avrpins.h
MAKE_PIN(P19, 19); // MISO
MAKE_PIN(P23, 23); // MOSI
MAKE_PIN(P18, 18); // SCK
MAKE_PIN(P5, 5); // SS
MAKE_PIN(P17, 17); // INT
```

M5Stack用MAX3421E搭載 USBモジュールのPIN MAPは以下にある通り
![274a0f12-4659-4ab9-9e7a-21a30f35c3a2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29493/b1dc84a6-8e3d-df5d-5aa4-43a441a1d2da.png)※スイッチサイエンスページから

INTだけはMAPに出てこないが他はPIN MAP通り。

M5Stack CoreS3は PIN MAPより MOSI 37, MISO 35, SCK 36 にする必要があるが、avrpins.h にはそのような定義値はないようなので追加する。

まず、M5Stack CoreS3でビルドするとき定義されているdefine値を調べた。

ビルドオプションに -DARDUINO_M5STACK_CORES3 と指定されているようなので
if defined(ARDUINO_M5STACK_CORES3)
が使えるとわかった。気をつけないといけないのが、ビルドオプションに-DESP32も指定されている点。順序を間違えるとavrpins.hの中でif defined(ESP32)のところに入ってしまい、PINがM5Stack BASICと変わらないという罠に一度だけハマった。

```avrpins.h
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
~
~
#elif defined(ARDUINO_M5STACK_CORES3)

#elif defined(ESP32)
~
PIN設定
~
#endif
```

こんな感じ。

# 対策
ということで、#elif defined(ESP32)の手前に追加した部分だけ抜粋したソースコードを以下に。

```avrpins.h
#elif defined(ARDUINO_M5STACK_CORES3)

// Workaround strict-aliasing warnings
#ifdef pgm_read_word
#undef pgm_read_word
#endif
#ifdef pgm_read_dword
#undef pgm_read_dword
#endif
#ifdef  pgm_read_float
#undef pgm_read_float
#endif
#ifdef  pgm_read_ptr
#undef pgm_read_ptr
#endif

#define pgm_read_word(addr) ({ \
  typeof(addr) _addr = (addr); \
  *(const unsigned short *)(_addr); \
})
#define pgm_read_dword(addr) ({ \
  typeof(addr) _addr = (addr); \
  *(const unsigned long *)(_addr); \
})
#define pgm_read_float(addr) ({ \
  typeof(addr) _addr = (addr); \
  *(const float *)(_addr); \
})
#define pgm_read_ptr(addr) ({ \
  typeof(addr) _addr = (addr); \
  *(void * const *)(_addr); \
})

// Pinout for ESP32 dev module

MAKE_PIN(P0, 0);
MAKE_PIN(P43, 43); // TX0
MAKE_PIN(P17, 17); // TX1
MAKE_PIN(P44, 44); // RX0
MAKE_PIN(P21, 21); // SDA
MAKE_PIN(P22, 22); // SCL
MAKE_PIN(P35, 35); // MISO
MAKE_PIN(P37, 37); // MOSI
MAKE_PIN(P36, 36); // SCK
MAKE_PIN(P1, 1); // SS
MAKE_PIN(P14, 14); // INT

#elif defined(ESP32)
```

```UsbCore.h
#elif defined(ARDUINO_M5STACK_CORES3)
typedef MAX3421e<P1, P14> MAX3421E; // ESP32S3 boards
#elif defined(ESP32)
```

```usbhost.h
#elif defined(ARDUINO_M5STACK_CORES3)
typedef SPi< P36, P37, P35, P1 > spi;
#elif defined(ESP32)
```

DIPスイッチは変更なし(全てON)。

これでM5Stack用MAX3421E搭載 USBモジュールをM5Stack CoreS3で使えるようになった。USB Host Shield 2.0のサンプルが動作するようになったので、usbh_midi.h を使いMIDI I/Fを接続してイベントのやり取りができるようになった。
おそらく、M5Stack Core2も同様にやれば動くのだと思う。

※冒頭に「DIPスイッチにより異なるホストを調整可能」と書かれていたと書いたが結局何も変更しなかった。

なお、ちょうど下記PRで似たような変更を実施した形跡があるので参考に。

https://github.com/felis/USB_Host_Shield_2.0/commit/e1a9d4b78b0ee2c120112431f7ccd71b25691340

### 2024/11/12 追記
出来たと思っていたが、USB Host Shield 2.0のWiimote系サンプルでBluetooth dongleが認識せず。Core2でも同様。M5Stack Basicだと動作するので上記対応だと何かまだ問題があるようだ。

### 2024/11/14 追記
M5Stack CoreS3用バッテリーボトムを付けるとUSB機器を認識しなくなることがわかった。また一度取り付けて認識しなくなると、リセットはおろか焼き直しても駄目であることもわかった。Pin Mapを見る限り競合しているPinはないように見えるのだが…真因までは特定できていない。M5BurnerでUIFlow2.0を焼いて、また焼きなおすと直った。焼くときはリセット長押して焼きモードにすること。
またArduino IDEを開きながらM5Burnerで焼くときにはシリアルモニタを閉じることを忘れずに。

https://www.switch-science.com/products/9421?srsltid=AfmBOop3R6U_V4caXOGMfOhTJpQuHSAZeKM7j1lDaTJ0LIx-hmgUFUCa

### 2024/11/14 追記その2
さらにわかったこと
焼いた後にバッテリーボトム付け替えはしてはいけないようだ。
USB Module, バッテリーボトムを接続した状態で M5Burner で CoreS3 UserDemo を焼き、その後目的のスケッチを焼き直すことで バッテリーボトムを付けたままUSB機器は認識されるようになった。

### 2024/11/14 追記その3
さらにわかったこと
やはりバッテリーボトムは付けてはいけない。追記その2一時期はできたがすぐにできなくなった。液晶画面も消えたりするので相性が悪いのか、やめた方が良い。
もしバッテリーが欲しかったらUSB Moduleにバッテリーを直接挿せば良い。