M5StickCでできること 〜加速度センサのデータをSDカードに書き込む

前回の調査(こちら)で、M5StickCとSDカードリーダーをつなぎ、M5StickCからSDカードを認識させることができました。

今回は、センサで採取したデータを、実際にSDカードに書き込んでみようと思います。

以前、M5StickC内蔵の加速度センサ(MPU6886)で、データ採取できる時間間隔を調査しました(記事は こちら)。このセンサでは、1秒間に160回程度、データを採取することができます。
今回は、この加速度データを、SDカードに書き込んでみます。



まず最初に、以下のようなスケッチを準備しました。
以前の記事でつくったスケッチ(加速度センサでデータ採取できる時間間隔を調査したもの)と基本的には同じで、ボタンを押すたびにデータ採取を開始・終了するようにしただけのものです。データ採取中は1秒ごとに、LCD画面に1秒間のデータ採取回数が表示されます。

#include <M5StickC.h>

unsigned long currentTime;
unsigned long prevTime=0;
unsigned long cnt=0;
unsigned long flag=0;

float accX = 0.0f;
float accY = 0.0f;
float accZ = 0.0f;
float prevX = 0.0f;
float prevY = 0.0f;
float prevZ = 0.0f;

void setup() {
  M5.begin();
  M5.Imu.Init();
  M5.Lcd.setRotation(3);
}

void loop() {
  M5.update();

  if(flag==1) {
    M5.IMU.getAccelData(&accX, &accY, &accZ);
    if(accX!=prevX || accY!=prevY || accZ!=prevZ) {
      cnt++;
      prevX=accX;
      prevY=accY;
      prevZ=accZ;
    }
  
    currentTime=millis();
    if(currentTime-prevTime>=1000) {
      M5.Lcd.fillScreen(BLACK);
      M5.Lcd.setCursor(0, 0, 4);
      M5.Lcd.println(cnt);
      cnt=0;
      prevTime=currentTime;
    }
  }

  if(M5.BtnA.wasPressed()) {
    flag = (flag+1)%2;
    if(flag==0) M5.Lcd.println("STOP");
  }
}

以前の調査の時と同様に、1秒間に160〜170回データを採取できました。

次に、SDカードにデータを書き込むための処理を追加しました。
ボタンAを押すと、ファイル “test1.csv” をWRITEモードで開きます。ファイルを開いている間は、センサで採取したデータをファイルにどんどん書き込んでいきます。もう一度ボタンAを押すと、ファイルを閉じてデータ採取を終了します。

#include <M5StickC.h>
#include "SD.h"

SPIClass SPI2;

unsigned long currentTime;
unsigned long prevTime=0;
unsigned long cnt=0;
unsigned long flag=0;

float accX = 0.0f;
float accY = 0.0f;
float accZ = 0.0f;
float prevX = 0.0f;
float prevY = 0.0f;
float prevZ = 0.0f;

File file;

void setup() {
  M5.begin();
  M5.Imu.Init();
  M5.Lcd.setRotation(3);

  SPI2.begin(0, 36, 26, -1); // SPI初期化
  if(!SD.begin(-1, SPI2)) { // SDカード初期化
    M5.Lcd.println("Card Mount Failed");
    return;
  }
}

void loop() {
  M5.update();

  if(flag==1) {
    M5.IMU.getAccelData(&accX, &accY, &accZ);
    if(accX!=prevX || accY!=prevY || accZ!=prevZ) {
      file.println((String)millis() + "," + (String)accX + "," + (String)accY + "," + (String)accZ);
      cnt++;
      prevX=accX;
      prevY=accY;
      prevZ=accZ;
    }
  
    currentTime=millis();
    if(currentTime-prevTime>=1000) {
      M5.Lcd.fillScreen(BLACK);
      M5.Lcd.setCursor(0, 0, 4);
      M5.Lcd.println(cnt);
      cnt=0;
      prevTime=currentTime;
    }
  }

  if(M5.BtnA.wasPressed()) {
    flag = (flag+1)%2;
    if(flag==1) {
      file = SD.open("/test1.csv", FILE_WRITE);
    }
    if(flag==0) {
      file.close();
      M5.Lcd.println("STOP");
    }
  }
}

結果はデータ書き込み処理を追加する前とほぼ変わっておらず、1秒間に160回程度、データを採取できました。

SDカードで作成されたデータをパソコンに取り込み、Excelで開いてみました。
A列がデータ採取時刻です(ms単位)。おおむね6〜7ms間隔でデータを採取できていることが分かります。
また、このスケッチでは、ファイルをWRITEモードで開いていますので、データ採取処理を複数回実行すると、前回のデータは上書きされて消えてしまいます。

次に、スケッチを以下のように変更してみました。
データ採取し、ファイルに書き込むたびに、ファイル “test2.csv” のオープン・クローズを実施しています。データをどんどん追記していく必要があるので、ファイルはAPPENDモードで開いています。

#include <M5StickC.h>
#include "SD.h"

SPIClass SPI2;

unsigned long currentTime;
unsigned long prevTime=0;
unsigned long cnt=0;
unsigned long flag=0;

float accX = 0.0f;
float accY = 0.0f;
float accZ = 0.0f;
float prevX = 0.0f;
float prevY = 0.0f;
float prevZ = 0.0f;

File file;

void setup() {
  M5.begin();
  M5.Imu.Init();
  M5.Lcd.setRotation(3);

  SPI2.begin(0, 36, 26, -1); // SPI初期化
  if(!SD.begin(-1, SPI2)) { // SDカード初期化
    M5.Lcd.println("Card Mount Failed");
    return;
  }
}

void loop() {
  M5.update();

  if(flag==1) {
    M5.IMU.getAccelData(&accX, &accY, &accZ);
    if(accX!=prevX || accY!=prevY || accZ!=prevZ) {
      file = SD.open("/test2.csv", FILE_APPEND);
      file.println((String)millis() + "," + (String)accX + "," + (String)accY + "," + (String)accZ);
      file.close();
      cnt++;
      prevX=accX;
      prevY=accY;
      prevZ=accZ;
    }
  
    currentTime=millis();
    if(currentTime-prevTime>=1000) {
      M5.Lcd.fillScreen(BLACK);
      M5.Lcd.setCursor(0, 0, 4);
      M5.Lcd.println(cnt);
      cnt=0;
      prevTime=currentTime;
    }
  }

  if(M5.BtnA.wasPressed()) {
    flag = (flag+1)%2;
    if(flag==0) {
      M5.Lcd.println("STOP");
    }
  }
}

実験の結果、1秒間に50〜80回程度、データを採取できました。

こちらのデータもExcelで開いてみました。
概ね10〜20ms間隔でデータを採取しています。また、データ採取間隔が50ms程度まで広がっている箇所も所々で見られました。

データ書き込みの都度、ファイルオープンしていると、その処理がボトルネックになってしまい、加速度センサの性能を発揮できないことがわかりました。

追記

上記の2番目の方法で、1秒間に160回程度の間隔で、約1時間分のデータを書き込んでみたところ、できあがったファイルは 約584,000行 で、その時のファイルサイズは 13.7MB でした。
例えば8GBのSDカードを使用する場合、約 24.3日 分のデータを保存できる計算になります。

また、Excelのサポートページ(こちら)によると、ワークシートの最大行数は「1,048,576 行」とのことです。
今回の方法で、1秒間に160行ずつ、csvファイルにデータを書き込んでいくと、1時間で「576,000 行」になる計算になります。
つまり、1.82時間で、Excelの制限を超えてしまうことになります。

この方法で長期間のデータを保存するためには、長くても1時間半ぐらいの間隔で、別ファイルにしていく必要がありそうです。

なお、私がM5Stack、M5StickCの使い方を習得するのにあたっては、以下の書籍を参考にさせていただきました。


ごく基本的なところから、かなり複雑なスケッチや、ネットワーク接続など、比較的高度なものまで、つまづかずに読み進めていけるような構成になっており、大変わかりやすい本です。


このサイトで書いている、M5Stackシリーズ(M5Stack、M5StickCなど)に関するブログ記事を、「さとやまノート」という別のブログページに、あらためて整理してまとめました。

他のM5Stackシリーズの記事にも興味のある方は「さとやまノート」をご覧ください。