M5Stack Core2内蔵のジャイロ加速度計「MPU6886」からジャイロデータと加速度データを入力し、加速度データをSDカードにCSV形式で保存します。開発環境は「PlatformIO」を使用します。

ジャイロ加速度計入力アプリの作成

ジャイロ加速度計入力アプリ「MPU6886test」を作成します。

  • 68行目の「M5.update」でボタンの状態を更新します。これを呼び出さないとボタンの入力ができません。
  • 71行目でジャイロデータを入力し、72行目で加速度データを入力します。
  • 56行目でSDカードのファイルをオープンし、103行目で計測し加速度データをSD カードに保存します
  • 112ー142行目まではM5Stack Core2の下側にあるボタンが押されたときに動作します。140行目では右側ののボタンが押されたときに、「LOG STOPPED」と表示されます。 同時にSDカードのファイルがクローズされます

main.cpp

#include <M5Core2.h>
#include <map>

float accX = 0.0F; // Define variables for storing inertial sensor data
float accY = 0.0F;
float accZ = 0.0F;

float gyroX = 0.0F;
float gyroY = 0.0F;
float gyroZ = 0.0F;

float pitch = 0.0F;
float roll = 0.0F;
float yaw = 0.0F;

float temp = 0.0F;

// RTC
RTC_DateTypeDef RTC_DateStruct; // Data
RTC_TimeTypeDef RTC_TimeStruct; // Time
File output_file;
bool isLogging = true;
int num = 0;
// Activities
int activity = 0; // 0: 'updown', 1: 'leftright', 2: 'circle'
std::map<int, std::string> activityName;

String zeroPadding(int num, int cnt)
{
  char tmp[256];
  char prm[5] = {'%', '0', (char)(cnt + 48), 'd', '\0'};
  sprintf(tmp, prm, num);
  return tmp;
}

/* After M5Core2 is started or reset
the program in the setUp () function will be run, and this part will only be run once. */
void setup()
{
  // Activity Name
  activityName[0] = " updown ";
  activityName[1] = " leftright ";
  activityName[2] = " circle";
 

  M5.begin();                        // Init M5Core.
  M5.IMU.Init();                     // Init IMU sensor.
  M5.Lcd.fillScreen(BLACK);          // Set the screen background color to black.
  M5.Lcd.setTextColor(GREEN, BLACK); // Sets the foreground color and background color of the displayed text.
  M5.Lcd.setTextSize(2);             // Set the font size.
  M5.Rtc.GetDate(&RTC_DateStruct);
  M5.Rtc.GetTime(&RTC_TimeStruct);
  String datetime = zeroPadding(RTC_DateStruct.Year, 4) + zeroPadding(RTC_DateStruct.Month, 2) + zeroPadding(RTC_DateStruct.Date, 2) + "-" + zeroPadding(RTC_TimeStruct.Hours, 2) + zeroPadding(RTC_TimeStruct.Minutes, 2) + zeroPadding(RTC_TimeStruct.Seconds, 2);

  String filepath = "/imu_data_" + datetime + ".csv";
  output_file = SD.open(filepath.c_str(), FILE_WRITE);
  if (!output_file)
  {
    M5.Lcd.println("ERROR: OPEN FILE");
    while (1)
      ;
  }
  output_file.println("num,accX,accY,accZ"); // Header  num = 0;
}

void loop()
{
  M5.update();
  
  // Stores the triaxial gyroscope data of the inertial sensor to the relevant variable
  M5.IMU.getGyroData(&gyroX, &gyroY, &gyroZ);
  M5.IMU.getAccelData(&accX, &accY, &accZ); // Stores the triaxial accelerometer.
  M5.IMU.getAhrsData(&pitch, &roll, &yaw);  // Stores the inertial sensor attitude.
  M5.IMU.getTempData(&temp);                // Stores the inertial sensor temperature to temp.

  /* The M5Core screen is 320x240 pixels, starting at the top left corner of the screen (0,0).
  gyroscope output related  gyroscope output related. */
  M5.Lcd.setCursor(0, 20);               // Move the cursor position to (x,y).
  M5.Lcd.printf("gyroX,  gyroY, gyroZ"); // Screen printingformatted string.
  M5.Lcd.setCursor(0, 42);
  M5.Lcd.printf("%6.2f %6.2f%6.2f o/s", gyroX, gyroY, gyroZ);

  // Accelerometer output is related
  M5.Lcd.setCursor(0, 70);
  M5.Lcd.printf("accX,   accY,  accZ");
  M5.Lcd.setCursor(0, 92);
  M5.Lcd.printf("%5.2f  %5.2f  %5.2f G", accX, accY, accZ);

  // Pose output is related
  M5.Lcd.setCursor(0, 120);
  M5.Lcd.printf("pitch,  roll,  yaw");
  M5.Lcd.setCursor(0, 142);
  M5.Lcd.printf("%5.2f  %5.2f  %5.2f deg", pitch, roll, yaw);

  // Inertial sensor temperature output related
  M5.Lcd.setCursor(0, 175);
  M5.Lcd.printf("Temperature : %.2f C", temp);
  if (isLogging)
  {
    if (output_file.size() < (1024 * 1024 * 1024))
    {

      output_file.printf("%d,%.7e,%.7e,%.7e\n", num, accX, accY, accZ);
       output_file.flush();
      num++;
    }
    else
    {
      output_file.close();
    }
  }
  // Button A, change activities
  if (M5.BtnA.wasPressed())
  {
    if (activity < activityName.size() - 1)
    {
      activity++;
    }
    else
    {
      activity = 0;
    }
    M5.Lcd.setCursor(0, 221);
    M5.Lcd.printf("%s", activityName[activity].c_str());
  }

  // Button B
  if (M5.BtnB.wasPressed())
  {
  }

  // Button C, stop logging
  if (M5.BtnC.wasPressed())
  {
    if (isLogging)
    {
      isLogging = false;
      output_file.close();
      M5.Lcd.setCursor(180, 221);
      M5.Lcd.printf("LOG STOPPED");
    }
  }
  delay(1000); // Delay 10ms.
}

ジャイロ加速度計入力アプリの実行

作成したジャイロ加速度計入力アプリ「MPU6886test」を実行します。次のように画面に入力したデータが表示されます。画面下右側のボタンを押すと、ログが終了して「LOG STOPPED」が表示されます。

SDカードに作成したCSVファイル「imu_data_xxxxxx.csv」を次に示します。パソコン上でエクスプローラを使って表示しました。

保存したCSVファイルをExcelで解析すると次のようなグラフが表示されます。①は上下に移動させたデータ、➁は左右に移動させたデータ、③は円を描くように移動させた場合のデータでそれぞれ特徴が出ています。

グラフを使った加速度モニター

グラフを使った加速度モニターは、「M5Stackの振動センサで3軸加速度モニター(グラフ表示)」を使用させていただきました。

グラフ表示に使用するLovyanGFXインストールするために、メニュー「ツール」→「ライブラリを管理」を選択します。

表示されたライブラリマネージャで「LovyanGFX」を検索して、「LovyanGFX」をインストールします。

加速度モニター表示形式

MPU6886Test.ino

#define M5STACK_MPU6886 // <M5Core2.h>をincludeする前に、IMUモジュールを#defineしておく
#include <M5Core2.h>

#define LGFX_AUTODETECT // 自動認識(D-duino-32 XS, PyBadgeはパネルID読取れないため自動認識の対象から外れているそうです)
#define LGFX_USE_V1     // v1.0.0を有効に(v0からの移行期間の特別措置とのこと。書かない場合は旧v0系で動作)
#include <LovyanGFX.hpp>          // lovyanGFXのヘッダを準備
#include <LGFX_AUTODETECT.hpp>    // クラス"LGFX"を準備
static LGFX lcd;                  // LGFXのインスタンスを作成(クラスLGFXを使ってlcdコマンドでいろいろできるようにする)
static LGFX_Sprite canvas(&lcd);  // スプライトを使う場合はLGFX_Spriteのインスタンスを作成

// グローバル変数宣言
float accX, accY, accZ;     // 加速度格納用
float x, y, z = 0;          // 現在値格納用
float x10, y10, z10 = 0;    // 0補正値格納用
float ax_data1[6] = {0};    // 加速度最大値格納用
float plot_x[250] = {150};  // グラフプロットデータ格納用
float plot_y[250] = {150};
float plot_z[250] = {150};
int axis = 0;               // x軸座標カウント用
int range_select = 1;       // y軸レンジ選択用
float y_range = 250;        // y軸レンジ換算値用
int sampling = 0;           // サンプリング回数カウント用

/**************************** Y軸項目表示 ****************************/
void rangeItem(float y_range) {
  canvas.setTextColor(WHITE, BLACK);  // 文字色
  canvas.setFont(&fonts::Font2);      // フォント設定
  if (y_range == 5) {                 // y_range(換算値)が5なら整数表示
    canvas.setCursor(5, 93);    canvas.printf("%7d", 10);
    canvas.setCursor(5, 193);   canvas.printf("%7d", -10);
  } else {                            // y_range(換算値)が5でなければ浮動小数表示
    canvas.setCursor(5, 93);    canvas.printf("%7.1f", 50 / y_range);
    canvas.setCursor(5, 193);   canvas.printf("%7.1f", -50 / y_range);
  }
}
/*************************** 0補正値セット ***************************/
void zeroReset() {
  M5.IMU.getAccelData(&accX,&accY,&accZ); // 加速度データ取得
  x10 = accX;         //取得値を補正値としてセット
  y10 = accY;
  z10 = accZ;
}
// 初期設定 ----------------------------------------------------------
void setup(){
  M5.begin();               // 本体初期化
  lcd.init();               // LCD初期化
  canvas.setColorDepth(8);  // カラーモード設定
  canvas.createSprite(lcd.width(), lcd.height()); // canvasサイズ(メモリ描画領域)設定(画面サイズに設定)

  M5.IMU.Init();            // 6軸センサ初期化
  M5.IMU.SetAccelFsr(M5.IMU.AFS_2G);  // 加速度センサースケール初期値 ±2G(2,4,8,16) ※GRAYは「setAccelFsr」(先頭のsが小文字)

  canvas.fillScreen(BLACK);           // 背景色
  canvas.setTextColor(WHITE , BLACK); // 文字色
  canvas.setTextSize(1);
  canvas.setFont(&fonts::Font4);      // フォント設定
  canvas.setCursor(20, 0);  canvas.print("3-AXIS MONITOR (G)"); // 座標、タイトル表示
  
  canvas.drawRect(60, 90, 250, 120, WHITE); // グラフ枠表示
  canvas.drawRect(59, 90, 252, 120, WHITE);
  canvas.drawRect(58, 90, 254, 120, WHITE);

  canvas.setFont(&fonts::Font2);            //  フォント設定
  canvas.setCursor(40, 144);   canvas.print("0"); // Y軸0表示
  
  // 設定ボタン項目表示
  canvas.drawRect(20, 217, 70, 23, DARKGREY);     // ボタン枠
  canvas.drawRect(127, 217, 70, 23, DARKGREY);
  canvas.drawRect(234, 217, 70, 23, DARKGREY);

  canvas.setTextColor(BLACK, DARKGREY);
  canvas.setCursor(24, 221);    canvas.print("  0 SET  ");  // 0セットボタン
  canvas.setCursor(130, 221);   canvas.print("  RESET  ");  // 最大値0リセットボタン
  canvas.setCursor(237, 221);   canvas.print("  RANGE  ");  // Y軸レンジ選択ボタン

  zeroReset();        // 0リセット
  rangeItem(y_range); // y軸レンジ項目表示呼出し
}
// メイン処理 --------------------------------------------------------
void loop() {
  float ax_data0[6];  // 加速度測定値格納用
  M5.update();        // ボタン状態更新
  M5.IMU.getAccelData(&accX,&accY,&accZ); // 加速度データ取得

  // 0補正
  if (M5.BtnA.wasReleased()) { // ボタンAが押されたら0補正値セット
    delay(1000);  // 本体安定待ち
    zeroReset();  // 0リセット
  }
  x = accX - x10; // 補正後の数値を表示値としてセット
  y = accY - y10;
  z = accZ - z10;

  // 最大値リセット
  if (M5.BtnB.wasReleased()) {  // ボタンBが押されたら0リセット
    delay(1000);                // 本体安定待ち
    for (int i = 0; i < 6; i++) {
      ax_data1[i] = 0;          // 最大値配列0リセット
    }
  }
  // y軸レンジ変更
  if (M5.BtnC.wasReleased()) {  // ボタンCが押されたらy軸レンジ変更
    range_select++;             // y軸レンジ選択+1
    if (range_select == 7) {    // y軸レンジ選択値が7なら
      range_select = 1;         // 1にセット
    }
    // 加速度センサスケール選択 ※GRAYは「setAccelFsr」(先頭のsが小文字)
    switch (range_select) { // y軸レンジ、加速度センサスケール選択
      case 1: y_range = 250;                // ±0.2G
        M5.IMU.SetAccelFsr(M5.IMU.AFS_2G);  // ±2G
        break;
      case 2: y_range = 100;                // ±0.5G
        M5.IMU.SetAccelFsr(M5.IMU.AFS_2G);  // ±2G
        break;
      case 3: y_range = 50;                 // ±1.0G
        M5.IMU.SetAccelFsr(M5.IMU.AFS_2G);  // ±2G
        break;
      case 4: y_range = 25;                 // ±2.0G
        M5.IMU.SetAccelFsr(M5.IMU.AFS_4G);  // ±4G
        break;
      case 5: y_range = 10;                 // ±5.0G
        M5.IMU.SetAccelFsr(M5.IMU.AFS_8G);  // ±8G
        break;
      case 6: y_range = 5;                  // ±10G
        M5.IMU.SetAccelFsr(M5.IMU.AFS_16G); // ±16G
        break;
    }
    rangeItem(y_range); // y軸レンジ項目表示呼出し
  }
  // 最大値の取得
  if (x >= 0) {                       // 0以上なら
    ax_data0[0] = x;                  // +加速度を格納
    if (ax_data0[0] > ax_data1[0]) {  // 前回の測定値より大きければ
      ax_data1[0] = ax_data0[0];      // 測定値を更新
    }
  } else {                            // 0より小さければ
    ax_data0[1] = x;                  // -加速度を格納
    if (ax_data0[1] < ax_data1[1]) {  // 前回の測定値より小さければ
      ax_data1[1] = ax_data0[1];      // 測定値を更新
    }
  }
  if (y >= 0) {
    ax_data0[2] = y;
    if (ax_data0[2] > ax_data1[2]) {  // 前回の測定値より大きければ
      ax_data1[2] = ax_data0[2];      // 測定値を更新
    }
  } else {
    ax_data0[3] = y;
    if (ax_data0[3] < ax_data1[3]) {  // 前回の測定値より小さければ
      ax_data1[3] = ax_data0[3];      // 測定値を更新
    }
  }
  if (z >= 0) {
    ax_data0[4] = z;
    if (ax_data0[4] > ax_data1[4]) {  // 前回の測定値より大きければ
      ax_data1[4] = ax_data0[4];      // 測定値を更新
    }
  } else {
    ax_data0[5] = z;
    if (ax_data0[5] < ax_data1[5]) {  // 前回の測定値より小さければ
      ax_data1[5] = ax_data0[5];      // 測定値を更新
    }
  }
  if (sampling == 5) {
    // 加速度表示(現在値 最小〜最大値)
    canvas.setFont(&fonts::Font4);  // フォント設定
    canvas.setTextColor(ORANGE , BLACK);  // X軸測定値
    canvas.setCursor(30, 24);   canvas.printf("X:%7.3f ", x);             // 現在値
    canvas.setCursor(140, 24);  canvas.printf("( %5.2f", ax_data1[1]);    // 最小値
    canvas.setCursor(220, 24);  canvas.printf("~ %5.2f )", ax_data1[0]);  // 最大値
    canvas.setTextColor(CYAN , BLACK);    // Y軸測定値
    canvas.setCursor(30, 44);   canvas.printf("Y:%7.3f ", y);
    canvas.setCursor(140, 44);  canvas.printf("( %5.2f", ax_data1[3]);
    canvas.setCursor(220, 44);  canvas.printf("~ %5.2f )", ax_data1[2]);
    canvas.setTextColor(GREEN , BLACK);   // Z軸測定値
    canvas.setCursor(30, 64);   canvas.printf("Z:%7.3f ", z);
    canvas.setCursor(140, 64);  canvas.printf("( %5.2f", ax_data1[5]);
    canvas.setCursor(220, 64);  canvas.printf("~ %5.2f )", ax_data1[4]);
    
    // バッテリー残量(MAX約4.2V、限界電圧3V)パーセント換算表示 ※GRAYは使用不可180〜188行までコメントアウトする。
    float battery;    // バッテリー残量表示用
    battery = (M5.Axp.GetBatVoltage() - 3) * 90;      // バッテリー残量取得、換算
    if (battery > 100) {                              // 換算値が100以上なら
      battery = 100;                                  // 100にする
    }
    canvas.setCursor(287, 0); canvas.setFont(&fonts::Font2);  // 座標、フォント 2(16px)
    canvas.setTextColor(DARKGREY, BLACK);             // 文字色
    canvas.printf("%3.0f%%", battery);                // バッテリー残量表示

    sampling = 0;  // サンプリング回数0リセット
  }
  sampling++;      // サンプリング回数+1

  // グラフ目盛表示
  canvas.fillRect(61, 91, 248, 118, BLACK);     // 枠内リセット
  canvas.drawFastHLine(55, 100, 260, DARKGREY); // 横線
  canvas.drawFastHLine(55, 150, 260, WHITE);
  canvas.drawFastHLine(55, 200, 260, DARKGREY);
  canvas.drawFastVLine(110, 90, 120, DARKGREY); // 縦線
  canvas.drawFastVLine(160, 90, 120, DARKGREY);
  canvas.drawFastVLine(210, 90, 120, DARKGREY);
  canvas.drawFastVLine(260, 90, 120, DARKGREY);

  // 表示データスクロール
  if (axis < 248) {               // グラフx軸カウントmaxでなければ
    axis++;                       // グラフx軸カウント+1
  } else {                        // グラフx軸カウントmaxなら
    for (int i = 0; i < 249; i++) {   // 表示データ全てを
      plot_x[i] = plot_x[i + 1];  // 前配列へシフト
      plot_y[i] = plot_y[i + 1];  // 前配列へシフト
      plot_z[i] = plot_z[i + 1];  // 前配列へシフト
    }
  }
  // 加速度データ換算
  plot_x[axis] = 150 - (x * y_range); // 換算データを表示データ配列へ
  plot_y[axis] = 150 - (y * y_range);
  plot_z[axis] = 150 - (z * y_range);

  // 折れ線グラフ表示
  for (int i = 1; i <= axis; i++) { // x軸max250まで繰返し
    // X軸
    if ((91 < plot_x[i]) && (plot_x[i] < 208)) {  // 表示データが91より大きくて208以下なら
      canvas.drawLine(i+59, plot_x[i-1], i + 60, plot_x[i], ORANGE);  // 波形描画
    } else if (plot_x[i] <= 91) {
      plot_x[i] = 91;
      canvas.drawLine(i+59, plot_x[i-1], i + 60, plot_x[i], ORANGE);  // 波形描画(max)
    } else if (plot_x[i] >= 208) {
      plot_x[i] = 208;
      canvas.drawLine(i+59, plot_x[i-1], i + 60, plot_x[i], ORANGE);  // 波形描画(min)
    }
    // Y軸
    if ((91 < plot_y[i]) && (plot_y[i] < 208)) {  // 表示データが91より大きくて208以下なら
      canvas.drawLine(i+59, plot_y[i-1], i + 60, plot_y[i], CYAN);    // 波形描画
    } else if (plot_y[i] <= 91) {
      plot_y[i] = 91;
      canvas.drawLine(i+59, plot_y[i-1], i + 60, plot_y[i], CYAN);  // 波形描画(max)
    } else if (plot_y[i] >= 208) {
      plot_y[i] = 208;
      canvas.drawLine(i+59, plot_y[i-1], i + 60, plot_y[i], CYAN);  // 波形描画(min)
    }
    // Z軸
    if ((91 < plot_z[i]) && (plot_z[i] < 208)) {  // 表示データが91より大きくて208以下なら
      canvas.drawLine(i+59, plot_z[i-1], i + 60, plot_z[i], GREEN);   // 波形描画
    } else if (plot_z[i] <= 91) {
      plot_z[i] = 91;
      canvas.drawLine(i+59, plot_z[i-1], i + 60, plot_z[i], GREEN);  // 波形描画(max)
    } else if (plot_z[i] >= 208) {
      plot_z[i] = 208;
      canvas.drawLine(i+59, plot_z[i-1], i + 60, plot_z[i], GREEN);  // 波形描画(min)
    }
  }
  canvas.pushSprite(0, 0);  // メモリ内に描画したcanvasを座標を指定して表示する
}

グラフを使った加速度モニターを実行したときの画面を次に示します。