M5Stick CPlus 1.1 導入 ～運動用タイマーを作る～

# 課題設定

HIIT (High Intensity Interval Training) という運動方法がある。
これは、「20秒間全力で運動をする → 10秒間休憩をする」を繰り返すものである。
これを行うため、この時間を計りたい。
できれば、パソコンではなく持ち運べる小型のデバイスがよい。
スマートフォンで
[インターバルタイマー : Hinokoto](https://www.hinokoto.com/interval-timer/)
を使用することはできるが、以下の問題が生じた。

* スマートフォンを音を出す設定にしないといけない。戻し忘れるリスクもある。
* スマートフォンの画面がすぐオフになる。設定を変えるのもオフにならないようタップするのも面倒。

やはり専用のデバイスを作るべきか？
基板から設計する？100均のタイマーかなんかを改造する？

# そこで M5Stick CPlus

M5Stick CPlus は以下の特徴があり、今回の課題の解決に有効である。

* 小型であり、持ち運びやすい
* ボタン・画面・ブザーという基本的なUIが標準装備されている
* 電池も内蔵されており、外部の電源などを接続せずに単体で使える

# 本体の入手

自分は[秋月電子通商](https://akizukidenshi.com/catalog/) (M-16011) で購入したが、現在は品切れ中のようである。

![M5Stick CPlus](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/8529e562-27ae-2a9e-5fb2-931b8afa6862.jpeg)

現在は、たとえば以下のページで購入できそうである。(未確認)

* [K016-H M5Stack Technology Co., Ltd. | Maker/DIY, Educational | DigiKey](https://www.digikey.jp/en/products/detail/m5stack-technology-co-ltd/K016-H/15771301)
  * [M5STICKC PLUS W/ WATCH ACCESSORY K016-H M5Stack製｜電子部品・半導体通販のマルツ](https://www.marutsu.co.jp/pc/i/41564231/)
* [Official M5Stack M5StickC PLUS ESP32-PICO Mini IoT Development Kit BLE and WiFi Bigger Screen IoT Controller - AliExpress](https://www.aliexpress.com/item/1005003421432169.html)

また、新版 (M5StickC Plus2) が出ているが、今回のプログラムの動作は未確認である。

[M5Stack Official M5StickC PLUS2 ESP32 Mini IoT Development Kit - AliExpress](https://www.aliexpress.com/item/1005006331521752.html)

# プログラミング環境の構築

[M5StickC Plusの使い方、初期設定、サンプルプログラム、M5StickCとの違い等を詳しく紹介 | ロジカラブログ](https://logikara.blog/m5stickc_plus/)

に沿って、PlatformIO をセットアップした。

`platformio.ini` から余計な部分を取り除き、エラー対策としてエラーが出たヘッダの名前を `lib_deps` に追加すると、以下のようになった。
(参考：[Platform IO compile error on every new project ( Wire.h / SPI.h ) · Issue #307 · adafruit/Adafruit-GFX-Library · GitHub](https://github.com/adafruit/Adafruit-GFX-Library/issues/307))

```ini
[env:m5stick-c]
platform = espressif32
board = m5stick-c
framework = arduino
lib_deps = m5stack/M5StickCPlus@^0.1.0
    Wire
    SPI
    FS
```

# プログラミング

C++でプログラムを書いていく。
Arduino のように `setup` 関数と `loop` 関数を作るといいようである。
`M5StickCPlus.h` を `#include` することで、ライブラリが利用可能になるようである。
デフォルトで作られる `lib`、`test`、`include` ディレクトリは、利用しないなら削除してよさそうである。

ライブラリの情報は、以下のサイトが参考になる。
[M5StickC非公式日本語リファレンス](https://lang-ship.com/reference/unofficial/M5StickC/)
上部メニューの「Class」に、本体内臓のデバイス (ボタン・ディスプレイなど) を扱うライブラリの情報がまとまっている。

さらに、Arduino のAPIも使える可能性がある。
[Arduino 日本語リファレンス](http://www.musashinodenpa.com/arduino/ref/)
今回は `millis()` を用いた時間測定を行った。

## 使用したAPI

### 初期化と状態の更新

```c++
M5.begin();
```

初期化を行う。
`setup` 関数の最初に呼び出すとよさそうである。

```c++
M5.update();
```

状態の更新を行う。
`loop` 関数の最初に呼び出すとよさそうである。

### 電源管理

[電源管理(AXP192) - M5StickC非公式日本語リファレンス](https://lang-ship.com/reference/unofficial/M5StickC/Class/AXP192/)

```c++
M5.Axp.GetBatVoltage()
```

バッテリーの電圧をボルト単位の浮動小数点数で取得する。

> バッテリーは4.2Vまで充電して、3.0V以下になったらシャットダウンします。

とのことなので、この範囲を仮定し、%単位に変換して画面に表示するようにした。
(自分で実装しておかないと、バッテリーの残量を知るすべはなさそうである)

実際に使ってみると、30%以下になると残量がどんどん減っていってすぐに電源が切れてしまい、実用にはならなそうであった。
そのため、今回は実装していないが、30～40%以下程度で充電を推奨する表示を出したほうがよさそうである。

### ブザー

```c++
M5.Beep.tone(周波数, 長さ);
```

周波数(Hz)と長さ(ms)を指定してブザーを鳴らす。
[M5StickC Plusの使い方、初期設定、サンプルプログラム、M5StickCとの違い等を詳しく紹介 | ロジカラブログ](https://logikara.blog/m5stickc_plus/)
に掲載されているサンプルプログラムに使い方がある。

### ボタン

[ボタン管理(Button) - M5StickC非公式日本語リファレンス](https://lang-ship.com/reference/unofficial/M5StickC/Class/Button/)

```c++
M5.BtnA.wasReleased()
M5.BtnB.wasReleased()
```

対応するボタンが離されたときに一度 1 を返し、それ以外のときは 0 を返す。
`BtnA` は正面の「M5」と書かれたボタンに、`BtnB` は「M5」ボタンを正面下にして置いたとき右側のボタンに対応する。

### 画面

[画面管理(M5Display) - M5StickC非公式日本語リファレンス](https://lang-ship.com/reference/unofficial/M5StickC/Class/M5Display/)
[M5Displayクラスの使い方 - M5StickC非公式日本語リファレンス](https://lang-ship.com/reference/unofficial/M5StickC/Tips/M5Display/)
[M5StickCのDisplay周り解析 | Lang-ship](https://lang-ship.com/blog/work/m5stickc-display/)

```c++
M5.Lcd.begin();
```

初期化を行う。

```c++
M5.Lcd.setRotation(1);
```

画面の向きを0～3の整数で設定する。
「1」は「M5」のボタンが右側になる向きを表す。

```c++
M5.Lcd.fillScreen(色);
```

画面全体を指定した色で塗りつぶす。
色は、`TFT_BLACK`、`TFT_WHITE`、`TFT_GREEN`、`TFT_ORANGE` などがあらかじめ定義されている。
定義済みの色の情報は
[M5StickCのDisplay周り解析 | Lang-ship](https://lang-ship.com/blog/work/m5stickc-display/#toc8)
にある。

```c++
M5.Lcd.setTextSize(倍率);
```

テキストを描画する際の大きさを、選択されているフォントの標準の大きさの何倍にするかの整数で設定する。

```c++
M5.Lcd.setTextColor(色);
```

テキストを描画する際の色を設定する。

```c++
M5.Lcd.drawString(テキスト, x座標, y座標);
```

位置(整数)と内容を指定してテキストを描画する。
テキストは、文字列リテラルは使用できたが、`char` の配列は使用できないようだった。
`char` の配列は、`String()` で囲むことで描画できた。

# 成果物

[GitHub - mikecat/hiit_timer: Timer for HIIT, using M5StickC Plus](https://github.com/mikecat/hiit_timer)

## 操作方法

### 電源オン/オフ

電源ボタン (「M5」ボタンを正面下にして置いたとき、左側のボタン) を長押しすると、電源を入れたり切ったりできる。(M5Stick CPlus の標準機能)
電源を入れるには、2秒程度長押しする。
電源を切るには、4秒程度長押しする。(ドキュメントには6秒とあるが、試してみると4秒ほどで切れた)

### タイトル画面

![タイトル画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/22be2eda-f3c1-4726-e5db-6678da83662f.jpeg)

タイトルと電池残量を表示する。
Aボタン (M5ボタン) を押すと、HIITモードのカウントを開始する。
Bボタン (右側のボタン) を押すと、60秒モードのカウントを開始する。

### カウント画面

![カウント画面 (緑)](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/6fbc4219-e0a7-a3d0-38f7-918531fb2520.jpeg)
![カウント画面 (オレンジ)](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/03c5e97b-f8f1-c94b-ce60-2d4d54402d6b.jpeg)

緑の背景で運動時間を、オレンジの背景で休憩時間を表す。
残りの秒数 (切り上げ) を数字で表示する。
一定間隔でブザーを短く鳴らし、時間の経過を知らせる。
設定された時間が経過すると、ブザーを長く鳴らし、背景が切り替わる。
Aボタンを押すと、タイトル画面に戻る。

HIITモードでは、20秒の運動時間と10秒の休憩時間を交互に計り、5秒おきにブザーを短く鳴らす。
60秒モードでは、60秒の運動時間を1回計ってタイトル画面に戻る。10秒おきにブザーを短く鳴らす。

# 結論

M5Stick CPlus を用いることで、持ち運びやすく使いやすいインターバルタイマーを作ることができた。
設定時間を使うものだけ固定する (埋め込む) ことで、操作はスタート・ストップだけというシンプルな仕様になった。