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シリーズの記事にも興味のある方は「さとやまノート」をご覧ください。