運動不足解消！リアルジャンプでDinoゲームチャレンジ

# はじめに

Chromeブラウザでインターネットにつながらない場合に遊べるDinoゲームをご存知でしょうか。
他の名称でDinoランナー、T-REXゲームと呼ばれる場合もあります。
スペースキーのみで遊べます。

![dino.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/238dc9bb-a923-5323-2ab5-1f9382cd8d41.jpeg)

# ゲームに熱中し過ぎてキーボードが壊れる心配

ノートPCのスペースキーを何回も強く押し過ぎて、キーボードが壊れる心配があります。
UIcreator(自作キーボード)をつなげて遊んでみました。
I/Oポートへフットスイッチやゲーミングボタンを接続すれば耐久性の心配は無くなります。

![footpedal.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/342a27c9-1bb9-c85b-a400-ace3de37cbd9.jpeg)

![game_button.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/4c734faa-b8e6-8f40-b924-c29594e91249.jpeg)

# 運動不足解消法

M5StickCの腕時計マウンタを足首につけて実際にジャンプしてDinoゲームを遊ぶことで運動不足解消を狙います。

![M5StickC.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/eabea1fa-08c1-8d3c-f25f-f929e70894b6.jpeg)

M5StickCには加速度センサー(MPU6886)が搭載されています。また、ESP32のBluetooth機能を利用してBLE KeyboardとしてPCへ接続することができます。
Y軸方向の加速度を検知して、"スペースキー"を押したことをPCへ送信すればDinoゲームを遊ぶことができます。

![accelDirection.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/764a6317-55cb-536a-d5fe-cf231b470426.jpeg)

# 環境

Arduino IDEを使用してソースコードをビルドしてM5StickCへ書き込みを行います。
BLE Keyboard用のライブラリは手動でインストールする必要があります。
GitHubのページへアクセスしてZIPファイル形式でダウンロードしてArduino IDEのlibrariesディレクトリへ解凍します。

https://github.com/T-vK/ESP32-BLE-Keyboard

# ソースコード

加速度センサーは非常に敏感なので、細かな揺れでも値が変化します。
ジャンプして着地するまでは値が上下に大きく振れるので、設定した閾値を越えた場合に一定期間(ソースコードでは0.5秒間)は"スペースキー"を押したことを送信しない＝チャタリングを防止しています。

```c++:M5StickC_MPU6886_11.ino
// M5StickC MPU6886
// https://pages.switch-science.com/letsiot/vibration/
// Bluetooth keyboard
// https://programresource.net/2020/04/09/3244.html
//
#include <M5StickC.h>
#include <BleKeyboard.h>  // BLE keyboard
 
BleKeyboard bleKeyboard("M5StickC");

#define SAMPLE_PERIOD 20    // サンプリング間隔(ミリ秒)
#define SAMPLE_SIZE 150     // 20ms x 150 = 3秒

void setup() {
    Serial.begin(115200);
    M5.begin();
    bleKeyboard.begin();
    M5.Lcd.setRotation(3);
    M5.MPU6886.Init();  // MPU6886を初期設定する
}

float ax, ay, az[SAMPLE_SIZE];  // 加速度データを読み出す変数

#define X0 5  // 横軸の描画開始座標
#define MINZ 500  // 縦軸の最小値 600mG
#define MAXZ 1500  // 縦軸の最大値 1400mG

#define TIME 25  // 20ms x 25 = 0.5秒
int count;  // チャタリング防止用カウンタ　(JUMP検知でcount = 0、TIMEを経過するまでは次のJUMPを行わない)

void loop() {
    M5.Lcd.fillScreen(BLACK);  // 画面をクリア
    M5.Lcd.setCursor(0, 5);  // テキスト表示位置をクリア

    if (bleKeyboard.isConnected()) {
      M5.Lcd.println("Connected");
    }
    else {
      M5.Lcd.println("Disconnected");
    }  

    for (int i = 0; i < SAMPLE_SIZE; i++) {
//        M5.MPU6886.getAccelData(&ax,&ay,&az[i]);  // MPU6886から加速度を取得
        M5.MPU6886.getAccelData(&ax,&az[i],&ay);  // Z軸とY軸を入れ替え

        az[i] *= 1000;  // mGに変換
        ax *= 1000;
        ay *= 1000;
        
        if (i == 0) continue;
        int y0 = map((int)(az[i - 1]), MINZ, MAXZ, M5.Lcd.height(), 0);
        int y1 = map((int)(az[i]), MINZ, MAXZ, M5.Lcd.height(), 0);
        M5.Lcd.drawLine(i - 1 + X0, y0, i + X0, y1, GREEN);
        //Serial.printf("%7.2f,%7.2f,%7.2f\n", ax * M5.MPU6886.gRes,  ay * M5.MPU6886.gRes, az[i] * M5.MPU6886.gRes);

        if (az[i] > 1500) {
          Serial.printf("az: %7.2f\n", az[i]);
          if (count > TIME) {
            M5.Lcd.println("JUMP!");
            Serial.println("JUMP!");
            bleKeyboard.print(" ");  // スペースキー
            count = 0;  // カウンタリセット
          }
        }
        count++;

        delay(SAMPLE_PERIOD);
    }
}
```

※ 2023年5月2日追記: M5StickC Plus用のコード

以下の環境で動作確認しました。

- Arduino IDE 2.1.0
- ボードマネージャ M5Stack 2.0.6
- ライブラリマネージャ M5StickCPlus 0.0.8

```c++:
// M5StickC Plus MPU6886
// https://pages.switch-science.com/letsiot/vibration/
// Bluetooth keyboard
// https://programresource.net/2020/04/09/3244.html
//
#include <M5StickCPlus.h>
#include <BleKeyboard.h>  // BLE keyboard
 
BleKeyboard bleKeyboard("M5StickCPlus");

#define SAMPLE_PERIOD 20    // サンプリング間隔(ミリ秒)
#define SAMPLE_SIZE 150     // 20ms x 150 = 3秒

void setup() {
    Serial.begin(115200);
    M5.begin();
    bleKeyboard.begin();
    M5.Lcd.setRotation(3);
    M5.Imu.Init();  // MPU6886を初期設定する
}

float ax, ay, az[SAMPLE_SIZE];  // 加速度データを読み出す変数

#define X0 5  // 横軸の描画開始座標
#define MINZ 500  // 縦軸の最小値 600mG
#define MAXZ 1500  // 縦軸の最大値 1400mG

#define TIME 25  // 20ms x 25 = 0.5秒
int count;  // チャタリング防止用カウンタ　(JUMP検知でcount = 0、TIMEを経過するまでは次のJUMPを行わない)

void loop() {
    M5.Lcd.fillScreen(BLACK);  // 画面をクリア
    M5.Lcd.setCursor(0, 5);  // テキスト表示位置をクリア

    if (bleKeyboard.isConnected()) {
      M5.Lcd.println("Connected");
    }
    else {
      M5.Lcd.println("Disconnected");
    }  

    for (int i = 0; i < SAMPLE_SIZE; i++) {
//        M5.MPU6886.getAccelData(&ax,&ay,&az[i]);  // MPU6886から加速度を取得
        M5.Imu.getAccelData(&ax,&az[i],&ay);  // Z軸とY軸を入れ替え

        az[i] *= 1000;  // mGに変換
        ax *= 1000;
        ay *= 1000;
        
        if (i == 0) continue;
        int y0 = map((int)(az[i - 1]), MINZ, MAXZ, M5.Lcd.height(), 0);
        int y1 = map((int)(az[i]), MINZ, MAXZ, M5.Lcd.height(), 0);
        M5.Lcd.drawLine(i - 1 + X0, y0, i + X0, y1, GREEN);
        //Serial.printf("%7.2f,%7.2f,%7.2f\n", ax * M5.MPU6886.gRes,  ay * M5.MPU6886.gRes, az[i] * M5.MPU6886.gRes);

        if (az[i] > 1500) {
          Serial.printf("az: %7.2f\n", az[i]);
          if (count > TIME) {
            M5.Lcd.println("JUMP!");
            Serial.println("JUMP!");
            bleKeyboard.print(" ");  // スペースキー
            count = 0;  // カウンタリセット
          }
        }
        count++;

        delay(SAMPLE_PERIOD);
    }
}
```

GitHubでもソースコードを公開しています。

https://github.com/kitazaki/M5StickC_MPU6886_BLE_Keyboard

# 実際に遊んでみた

https://youtu.be/72pX-VdhHoU

### 参考にしたページ

M5StickC MPU6886

https://pages.switch-science.com/letsiot/vibration/

Bluetooth keyboard

https://programresource.net/2020/04/09/3244.html