M5Stack CoreS3 にボタン入力をつけよう

## M5Stack CoreS3
今年(2023)発売された [M5Stack CoreS3](https://shop.m5stack.com/products/m5stack-cores3-esp32s3-lotdevelopment-kit)、第3世代の Core デバイス ESP32S3 搭載の M5Stack です。
カメラ(GC0308)内蔵、PSRAM の容量増加等、従来の Core2 を凌ぐパワフルな製品なのですが... **Coreでいう物理ボタン、 Core2 でいうタッチボタン、いわゆる M5.BtnA/B/C が存在しないのです。** (2023 年 11 月時点)

[写真:左上 Core2 左下 CoreS3 右 Core(Faces装着)]
![core123.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159667/9addb209-43bf-8dd4-3d26-8015c5d52cb6.png)

Core2 同様なタッチスクリーンなので、それに即した独自コードを書けばよいのですが、従来の Core / Core2 のボタン操作を前提としたコードの移植や対応の際に悩む事になります。

#### 備考
なお [CoreS3 DevelopKit](https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/products/core/CoreS3/CoreS3%20Development%20Kit-compressed%20(1).pdf) によるとカメラとマイクの部分がタッチ識別可能なようですが、2023年 11月時点では M5Unified では当該区域をボタンとして使用できません。

## M5Unified のすすめ
M5Unified、皆さんご存知ですか? 
数多く出ている M5Stack 系列機器を透過的に扱えるように統合されたライブラリです。
個々の機器用のライブラリでは、似たような関数の引数の微妙な順番や意味の違い、ピン他の外部接続設定等、考慮しないといけない点が多くあります。また単一ソースで多機器対応しようとすると #if/#endif を駆使して組み上げなければならず中々に難儀です。

そこで **M5Unified** です! 

https://github.com/m5stack/M5Unified

詳しくは以下のページ等を参照してください。

https://speakerdeck.com/lovyan03/m5unifiednoshao-jie

https://docs.m5stack.com/ja/quick_start/m5unified/helloworld

https://lang-ship.com/blog/work/m5unified-1/

### M5.BtnX
M5Unified では M5.BtnA/B/C/PWR 経由でボタン状態を取得できます。Core/Core2 では以下のコードで A ボタンが押下されるとシリアルにメッセージが出力されます。
```sample_core_1_2.cpp
#include <M5Unified.h>
void setup()
{
  M5.begin();
}
void loop()
{
  M5.update();
  if(M5.BtnA.wasPressed())
  {
    M5_LOGI("Button A was pressed.");
  }
}
```
もし CoreS3 にもボタン A/B/C が存在して、同様にアクセスすることで状態を取得できたら、上のようなコードを全ての Core で動作するようにできるのでは? というのが製作動機の一つです。

## gob_unifiedButton による CoreS3 へのボタンの追加
### GitHub
https://github.com/GOB52/gob_unifiedButton

### 導入
自身の環境に合わせて適宜行ってください。
* git clone や Zip ダウンロードからの展開
* PlatformIO
```platformio.ini
lib_deps = gob/gob_unifiedButton
```
* ArduinoIDE ライブラリマネージャからのインストール

### 使用例
先ほどの sample_1_2.cpp を CoreS3 対応にすると以下のようになります。
もちろん従来の Core/Core2 でも動作します。

```sample_core_1_2_3.cpp
#include <M5Unified.h>
#include <gob_unifiedButton.hpp>

goblib::UnifiedButton unifiedButton;

void setup()
{
  M5.begin();
  unifiedButton.begin(&M5.Display);
}
void loop()
{
  M5.update();
  unifiedButton.update(); // M5.update() の後に呼ぶ事
  
  // ボタン判定部は以前のままで動作する!
  if(M5.BtnA.wasPressed())
  {
    M5_LOGI("Button A was pressed.");
  }
  unifiedButton.draw(); // ボタンを画面へ描画
}
```
setup で begin、 loop で update/draw が基本的な流れとなります。**ボタンの update() は M5.update() の後に呼び出すようにしてください。**
描画は begin 時に指定された LovyanGFX* に対して行われます。オフスクリーンバッファ等別途用意している場合は、そちらを指定すると良いでしょう。

### ボタン外観
ボタンはデフォルトでは画面下端に表示されます。

![gob_unifiedButton.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159667/a936e179-988d-15f2-c352-763f8b1b8054.png)

Core2 と違い画面外にあるタッチボタンではないので、ボタン以外の要素の描画範囲が狭くなってしまいます。
透明ボタン設定で使用する事で、画面全てを他の描画に使うことが可能です。
```transparency.cpp
  // 画面全体透明ボタン指定による begin
  unifiedButton.begin(&M5.Display, goblib::UnifiedButton::transparent_all);
```
上記例では画面を縦に3分割した透明ボタンとして機能します。
また独自形状のボタンを作成することも可能です。詳細は当該リポジトリの examples/customButton を参照してください。

### 活用例
https://github.com/GOB52/M5Stack_FlipBookSD

拙作の M5Stack 上での動画再生アプリ、 M5Stack_FlipBookSD にて使用しています。
単一ソースで Core/Core2/CoreS3 で動作するものを作ることができています。 


### 内部挙動
* M5Unified の Touch_Class によって実装されている M5.Touch 経由でタッチスクリーンにアクセスしています。
* Core2 のタッチボタンと同様のロジックで状態を取得決定し、それを各ボタンへ引き渡してボタン状態を設定しています。
* CoreS3 以外の機器の場合は何もしません。
* ボタン描画は M5GFX の LGFX_Button によって行っています。

### 将来
M5Unified での CoreS3 仮想ボタン対応が進行中ですが、それが実装された暁には当ライブラリの使命は終わることになるでしょう。
その際は画面内タッチボタンによるインターフェイス作成の一例として参考にしていただければ幸いです。