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を座標を指定して表示する
}
グラフを使った加速度モニターを実行したときの画面を次に示します。






