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の使い方を習得するのにあたっては、以下の書籍を参考にさせていただきました。
ごく基本的なところから、かなり複雑なスケッチや、ネットワーク接続など、比較的高度なものまで、つまづかずに読み進めていけるような構成になっており、大変わかりやすい本です。