M5StickCでできること 〜センサでのデータ採取と画面表示を同時に行う

M5StickCをIoTデバイスとして利用する場合、各種センサでデータを採取したり、採取したデータをサーバに送信したり画面に表示したりと、いくつかの処理を行うことになります。
その際、例えば画面に何かを表示する処理を行っているが、その処理の途中でも、センサでのデータ採取を中断したくないというような場合があります。

M5StickCに搭載されている「ESP32-PICO」はデュアルコアのプロセッサなので、上記のような状況に対応するため、M5StickCでの並列処理の方法を確認しました。


まず最初に、以下のスケッチを動かしてみました。M5StickC内蔵の加速度センサ「MPU6886」で加速度情報を採取し、値が変化したらシリアルモニタに書き出します。
以前行った調査(記事は こちら)で、1秒間に160回程度、データを採取できることが分かっています。

#include <M5StickC.h>

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();
}

void loop() {
  M5.IMU.getAccelData(&accX, &accY, &accZ);
  if(accX!=prevX || accY!=prevY || accZ!=prevZ) {
    Serial.print(accX);
    Serial.print(",");
    Serial.print(accY);
    Serial.print(",");
    Serial.println(accZ);
    prevX=accX;
    prevY=accY;
    prevZ=accZ;
    delay(1);
  }
}

シリアルモニタへの出力は以下のような感じになっています。情報は非常に高速に更新されます。

次に、スケッチを以下のように変更しました。先ほどのスケッチに対し、M5StickCのLCD画面に文字を書き出す処理を追加しています。
100ms毎に “*” をひとつずつ表示させ、10個表示されるまで繰り返します。10個表示されたらシリアルモニタに “##########” という文字列を書き出します。
つまり、1秒毎に、シリアルモニタに “##########” と表示されることになります。

#include <M5StickC.h>

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.Lcd.setCursor(0, 0, 4);
  M5.Lcd.fillScreen(BLACK);
  for(int i=0; i<10; i++) {
    M5.Lcd.print("*");
    delay(100);
  }
  Serial.println("##########");
  
  M5.IMU.getAccelData(&accX, &accY, &accZ);
  if(accX!=prevX || accY!=prevY || accZ!=prevZ) {
    Serial.print(accX);
    Serial.print(",");
    Serial.print(accY);
    Serial.print(",");
    Serial.println(accZ);
    prevX=accX;
    prevY=accY;
    prevZ=accZ;
    delay(1);
  }
}

シリアルモニタへの出力は以下のとおりです。”##########” は1秒毎に表示されているので、加速度センサの情報も、1秒に1回しか採取できていないことが分かります。
つまり、LCD画面への文字の書き出し処理によって、加速度センサの情報採取が中断してしまっています。

次に、LCD画面に文字を書き出す処理と、加速度センサで情報を採取する処理を並列で処理するよう、スケッチを以下のように変更しました。

#include <M5StickC.h>

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 task1(void *pvParameters) {
  while(1) {
    M5.IMU.getAccelData(&accX, &accY, &accZ);
    if(accX!=prevX || accY!=prevY || accZ!=prevZ) {
      Serial.print(accX);
      Serial.print(",");
      Serial.print(accY);
      Serial.print(",");
      Serial.println(accZ);
      prevX=accX;
      prevY=accY;
      prevZ=accZ;
      delay(1);
    }
  }
}

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

  xTaskCreatePinnedToCore(
    task1,     /* Function to implement the task */
    "task1",   /* Name of the task */
    4096,      /* Stack size in words */
    NULL,      /* Task input parameter */
    1,         /* Priority of the task */
    NULL,      /* Task handle. */
    1);        /* Core where the task should run */
}

void loop() {
  M5.Lcd.setCursor(0, 0, 4);
  M5.Lcd.fillScreen(BLACK);
  for(int i=0; i<10; i++) {
    M5.Lcd.print("*");
    delay(100);
  }
  Serial.println("##########");
}

別のコアで処理を行うには「xTaskCreatePinnedToCore」という関数を使うようです。
「ファイル」>「スケッチ例」>「M5Stack」>「Advanced」>「MultiTask」などを参考にして、上記のスケッチをつくりました。
LCD画面に文字を書き出す処理は通常のloop関数で、加速度センサで情報を採取する処理は「xTaskCreatePinnedToCore」を使って、別のコアで処理させています。

シリアルモニタへの出力は以下のとおりです。LCD画面に文字を書き出している途中にも、加速度センサで情報を採取し、シリアルモニタに書き出すことができていることが分かります。

なお、ネット上の多くの情報では、M5StickCで並列処理を行う場合は「コア0」を指定するように、と書かれているのですが、今回の私の場合は、それではうまくいかず、「コア1」を指定する必要がありました。
この辺りについては、もう少し調査が必要です。

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


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


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

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