M5Stackでスクワットをカウントして、BLEで回数を送信する

M5Stackでスクワットをして、その動きに連動してゲームができるようなプロダクトを作りたい。

まず、M5stackでスクワットをカウントするスクリプトを作成したので共有する。
このスクリプトは先輩が作成したものを参照して、BLE通信で回数を送信するスクリプトを作成した。
↓先輩のスクリプト

https://qiita.com/KechanaGohan/items/c40f35889b6b533f4632

# 使い方
M5stackを起動後、ポケットに入れます。
スクワットが終わったらM5stackの画面に記載の回数を確認する。

# 機能
・スクワットのアルゴリズムを元にカウントを行う
・BLE通信で外部機器にカウントした数値を送信
（後日関連づけるため）

# スクワットのアルゴリズム
・前後の角度で45°を基準として１回とする。
（改善の余地あり）
たとえば、40°（→45°）→50°で1回スクワットカウントを増やす。

# 既存のスクワットスクリプトの課題点
・スクワットを素早くしてみるとカウントされていない時があった。
下記の２つの変数をシリアルプロッターで比較してみた。
　①getAccelDataで加速度のY軸とZ軸から求めた角度（赤色のグラフ）
　②getAhrsDataで取得した角度（緑色のグラフ）
 　→①の角度の数値と比較して、正しい角度情報を取得しているか
![スクリーンショット 2024-01-14 21.43.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624849/d04e24a1-68ea-bcfa-7ee6-bc06a12aec0c.png)
①と②で値の動きがズレていた。
そのため、①の値を採用することにした。

可能性として、「M5stackの処理時間」や「delayの時間」が原因だと推測をしたが違っていた。
そこで、getAhrsDataのライブラリーの中身を確認したところ、
※人が違和感を感じるのは M5Stackの画面処理ぐらいなので、処理速度はそこまで考えなくて良い。（チラつきなど）
MahonyAHRSupdateIMU関数の処理時間が原因だと結論になった。
![スクリーンショット 2024-01-14 21.27.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624849/216bd6f0-dd47-4a20-bd80-7d9aad882598.png)
IMUセンサーの仕様把握は以下の２つのサイトを参照しました。

https://lang-ship.com/blog/work/m5stickc-imu-ahrs-2/

https://qiita.com/MuAuan/items/30dd39af2b429147be51

# 改善点
・センサーで得られる数値の見直し
　①センサーの値を0°~90°に制限した
 　（constrain関数を用いて値の調整）
　②角度情報のリアルタイム性を向上
 　（加速度センサー情報をgetAhrsDataからgetAccelDataで取得）
　③


```ruby:Squat_count.ino

#include <M5Stack.h>
#include "BLESerial.h"

BLESerial bleSerial;
//TFT_eSpriteクラスをインスタンス化、spriteを使用し画面のちらつき防止
TFT_eSprite sprite = TFT_eSprite(&M5.Lcd);
//スプライトを使わない場合、LcdライブラリーのfillScreenを使わなければ良い（処理に時間がかかるためチラつく）

int Squat_count = 0;
bool SquatRotated = false;
float accX, accY, accZ;      // 加速度格納用
float gyroX, gyroY, gyroZ;  // 角速度格納用
float pitch, roll, yaw;     // 姿勢角格納用

void setup() {
  M5.begin();
  M5.IMU.Init();
  M5.Lcd.setRotation(1);
  bleSerial.begin("ESP32");
  
  // スプライトの作成
  sprite.setColorDepth(8);
  sprite.setTextSize(3);
  sprite.createSprite(M5.Lcd.width(), M5.Lcd.height());
  
}

void loop() {
  M5.update();
  M5.IMU.getAccelData(&accX, &accY, &accZ);
  M5.IMU.getGyroData(&gyroX, &gyroY, &gyroZ); // 角速度データ取得
  M5.IMU.getAhrsData(&pitch, &roll, &yaw);    // 姿勢角データ取得

//「リアルタイム」の角度の計算(rollでは測定に時間がかかるため)
  float x_angle = atan2(accX, accZ) * 180.0 / PI; // X-Z加速度から角度に換算
  float y_angle = atan2(accY, accZ) * 180.0 / PI; // Y-Z加速度から角度に換算

//上限(90度)と下限（0度）を設ける
  x_angle = constrain(x_angle, 0, 90);
  y_angle = constrain(y_angle, 0, 90);


//アルゴリズム（y_angelを採用）修正点：現状45度をいったり来たりすればカウントされる
//たとえば44度→46度、46度→44度で１回カウントされている
//案：カウントされた後の最大角度を記録して比較する？
  if (SquatRotated == false) {
    if (y_angle < 45) {
      Squat_count ++;
      SquatRotated = true;
    }
  } else {
    if (y_angle > 45) {
      SquatRotated = false;
//音を出したい
    }
  }
//各速度センサーで前後の揺れを感知するアルゴリズムを追加したい。
//揺れを感知したらカウントしない？

  bleSerial.println(Squat_count);
//y_angleとrollの比較
  Serial.println("x_angle, y_angle ,roll"); // 姿勢角
  Serial.printf("%7.2f, %7.2f, %7.2f\n", x_angle, y_angle ,roll);   // 姿勢角

//検証用
//  Serial.println("gyroX, gyroY, gyroZ"); // 姿勢角
//  Serial.printf("%7.2f, %7.2f, %7.2f\n", gyroX, gyroY, gyroZ);   // 姿勢角
//  Serial.println(y_angle);

//M5画面操作
  sprite.fillScreen(BLACK);

  sprite.setCursor(10, 10); //文字表示の左上位置を設定
  sprite.println("Squat count:" + String(Squat_count));
  sprite.setCursor(10, 175); //文字表示の左上位置を設定
  sprite.setTextColor(WHITE); //文字色の指定

  delay(50);
// スプライトを画面表示する
  sprite.pushSprite(0, 0); 

}

```

# 一言
今まで関数を利用して呼び出すようなスクリプトを作成していたが、
「どのような処理」が裏で発生しているのかを調べる癖をつけられた。
ライブラリーは便利だが、応用が効くように中身の確認をすることをしていきたい。
（深みハマると時間がかかるのである程度のところまでで。。）
ゲーム側のスクリプトも作成したので、見てみてください！

https://qiita.com/hato_pippo/items/4cca9964fe471393287c