ESP32は安いのにCPUが強力で技適あり無線LAN、Bluetooth等が付いている便利なマイコンである。
さらに凄いのは、ESP32はCPUがデュアルコアになっている所である。ハード的に2つの処理を並列に行えるのである(注:シングルコア版のESP32であるESP32-SOLOも存在する)。
色々便利な使い方が出来る。
- 片方でセンサーからのデータ収集、もう片方でサーバーへのデータアップロード処理(センサーデータの蓄積が中断されない)。
- 片方で表示処理、もう片方でサーバーから最新データのダウンロードを行い、ダウンロードが終わった時点で表示内容を差し替える(最新データが入るまで既存のデータで表示継続できる)。
デュアルコアで処理をする際メモリアクセスや、デバイスにアクセスする際に処理が衝突しない様に注意が必要であるが、デュアルコアを使う事自体は簡単に出来る。
以下が最低限の処理のみのスケッチ。subProcessの関数はcore 0で延々と変数に1000を足してシリアル出力する。お馴染みのloopはcore 1で変数に1を足してシリアル出力する。
volatile int intdata = 0;
void subProcess(void * pvParameters) {
while (1) {
intdata = intdata + 1000;
Serial.println(intdata);
delay(500);
}
}
void setup() {
Serial.begin(9600);
xTaskCreatePinnedToCore(subProcess, "subProcess", 4096, NULL, 1, NULL, 0); //Core 0でタスク開始
}
void loop() {
intdata = intdata + 1;
Serial.println(intdata);
delay(500);
}
xTaskCreatePinnedToCoreで別コアで処理を開始している。異なるタスクから同じ変数を使用する場合、変数にvolatileを付けておく。
上記スケッチも一応動作するが、より複雑な処理で相互に関係するタスクを両方のコアで実行している場合、同期を取る必要が出てくるケースがある。
色々方法はあるが、セマフォを使う簡単な例は以下の通り。
SemaphoreHandle_t xMutex = NULL;
volatile int intdata = 0;
void subProcess(void * pvParameters) {
while (1) {
if (xSemaphoreTake( xMutex, ( portTickType ) 100 ) == pdTRUE) {
intdata = intdata + 1000;
Serial.println(intdata);
xSemaphoreGive(xMutex);
}
delay(500);
}
}
void setup() {
Serial.begin(9600);
xMutex = xSemaphoreCreateMutex();
xSemaphoreGive(xMutex);
xTaskCreatePinnedToCore(subProcess, "subProcess", 4096, NULL, 1, NULL, 0); //Core 0でタスク開始
}
void loop() {
if (xSemaphoreTake( xMutex, ( portTickType ) 100 ) == pdTRUE) {
intdata = intdata + 1;
Serial.println(intdata);
xSemaphoreGive(xMutex);
}
delay(500);
}
xSemaphoreTake~xSemaphoreGiveの間は同時にどちらか片方しかその内部の処理が実行されない。2つ目のパラーメーターはこの時間待機して、待機時間が過ぎてももう片方の処理が終わらなった場合にタイムアウトして諦める時間。
ESP32のCore 0はESPのシステムも動作しているらしいので、何でも出来ると言う訳ではないしマルチタスクはメモリ管理、デバイス管理、デッドロック等考慮すべき事が多々あるが、電子工作で手軽にマルチコアが使えるのは凄い事である。