M5Stackシリーズを使うと、内部で ESP32のチップが使わているのでWi-FiやBluetooth周りのテストをするのに非常に便利です。手元の BLE 関係の通信も、いったん M5Stack で試し実装をしてみてスマホで動作確認をしてから、専用ボードに書き込むと動作がわかりやすいですよね。
手元でTIのボードを使ってBLE通信をしているのですが、そこでRTOSを使うことになっています。なぜRTOSを使うことになったのかはさておき、最近では AWS から FreeRTOSが配布されているので、これの準じたサンプルコードがあちこちで配布されています。当然ながらTIでも配布されているので、これを参考に作ります。
で、各ボードでのRTOS対応のコードは結構な職人プログラマが整備しているので、うまいことRTOSの説明や動きを解説することができあません。せっかくRTOSという統一されたOSのAPIを使っているわけですが、それぞれのボードに対応した専用関数で置き換えられているので(これはこれで仕事上は便利)コーディングがしやすいのですが、まあ、内部でどう動いているのか不可解なところがあるので、できることならば生のRTOSのAPIを叩いて試しておきたい。
ちなみに、私の場合 μiTronで仕事をしたことがあるのでリアルタイムOSまわりの動きはわかるのですが、RTOSのほうは初見ですね。
VSCode で PlatformIO を使う。
以前はESP32の開発環境を整えるのが一苦労だったのですが、最近は VSCode に「PlatformIO」という拡張をいれるだけで十分です。コンパイラ諸々をインストールしてくれます。
VSCode-PlatformIO IDEを使って、ESP32の開発環境を構築およびLチカ https://zenn.dev/kotaproj/articles/esp32_vscode_pio
デバッガ機能等便利なものが多いのですが、私の場合は Arduino IDE の上位互換機能があれば十分なので、build と upload だけあれば十分なところです。
Arduino IDE の場合はコード補完機能(インテリセンス機能)が効かないのでコーディングに苦労することが多いのですが、VSCode だと補完が効くし、GitHub Copilot を入れた現状だとかなりの部分をコード補完してくるので便利ですね。
RTOS対応のプロジェクトを作る
RTOSを使ってプログラミングをするのに、何かライブラリを入れないといけないのでは?と思いますが、実は ESP32の「espidf」というフレームワークには既に、RTOSのAPIが含まれています。他のボードだと結構苦労しそうな感じなのですが、少なくともM5StackシリーズでRTOSをやるときは特に追加のライブラリは必要ありません。
手元にあるのが、初期のm5stackなのでBoardには「M5Stack Core ESP32」を選択します。
ここでは、Framework に「Espidf」を選択していますが、実は「Android」でも構いません。どういうライブラリの構造かわかりませんが、ここで指定する「Android」のほうにもRTOSのライブラリが入っています。
最初、m5stackでRTOSプログラミングをするときのサンプルコードが、「Espidf」が多かったので、これにしたのが以下の躓きの始まりです。結論から言えば、(おそらくライブラリの容量の関係)「Android」で問題がないと思われます。
platformio.ini を比較する
プロジェクトを作成したら platformio.ini を確認しておきます。ここでフレームワークの指定やライブラリの追加ができます。
Framework が 「Espidf」の場合
[env:m5stack-core-esp32]
platform = espressif32
board = m5stack-core-esp32
framework = espidf
Framework が Android の場合
[env:m5stack-core-esp32]
platform = espressif32
board = m5stack-core-esp32
framework = arduino
platformio.ini の中身は framework のところ以外は変わりません。
ただし、このままでは M5.Lcd のような、モニタを使ったコードが書けないので、lib_deps を追加しておきます。これで M5.* 系のライブラリが自動でインストールされます。
[env:m5stack-core-esp32]
platform = espressif32
board = m5stack-core-esp32
framework = arduino
lib_deps =
m5stack/M5Stack@^0.3.9
ChatGPT に RTOS を使ったサンプルコードを書いて貰う。
実は GPT4o を使うと、ほどよく M5Satck を使って RTOS のサンプルコードを書いてくれます。
以下は、提案してもらったコードなのですが、実はビルドができません。
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_system.h>
#include <esp_log.h>
#include <M5Stack.h>
// ログタグの定義
static const char *TAG = "main";
// タスクの定義
void task1(void *pvParameters) {
while (1) {
ESP_LOGI(TAG, "Hello from Task 1");
vTaskDelay(pdMS_TO_TICKS(1000)); // 1秒待機
}
}
void task2(void *pvParameters) {
while (1) {
ESP_LOGI(TAG, "Hello from Task 2");
vTaskDelay(pdMS_TO_TICKS(2000)); // 2秒待機
}
}
// メイン関数
void app_main(void) {
// M5Stack の初期化
M5.begin();
// タスクの作成
xTaskCreate(&task1, "task1", 2048, NULL, 5, NULL);
xTaskCreate(&task2, "task2", 2048, NULL, 5, NULL);
// メインタスクのループ
while (1) {
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(10, 10);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(2);
M5.Lcd.print("Hello, M5Stack with FreeRTOS!");
vTaskDelay(pdMS_TO_TICKS(3000)); // 3秒待機
}
}
error “This library only supports boards with ESP32 processor.”
なエラーが M5Stack.h 内で出ています。
よくわからないので、framework が「Android」のほうに、main.c をコピーすると以下のようなエラーがでます。
“class” がないというエラーなのですが、実は main.c をそのままコピーしたので C言語で扱われているからです。なので、main.cpp にして C++ でコンパイルされるようにします。
実は SD.h がないとか諸々エラーになることもあるのですが、結論としては main.c のままなのが原因で、main.cpp にすれば ok です。
コンパイルは順調に進むのですが、最後のリンクで失敗します。
どうやら、
- framework が espidf のときはC言語で、app_main がエントリー関数(俗にいうmain関数)
- framework が android のときはC++で、setup と loop が呼び出される
という違いがあるようですね。TI のサンプルコードが C言語のほうの *.c に限られていたので、Android のほうが C++ のほうの *.cpp に統一されていたのを忘れていました、というオチです。
で、最終的には以下のコードで動くようになります。
#include <M5Stack.h>
// タスクの定義
void task1(void *pvParameters) {
while (1) {
Serial.println("Hello from Task 1");
vTaskDelay(pdMS_TO_TICKS(1000)); // 1秒待機
}
}
void task2(void *pvParameters) {
while (1) {
Serial.println("Hello from Task 2");
vTaskDelay(pdMS_TO_TICKS(2000)); // 2秒待機
}
}
// Arduinoのsetup関数
void setup() {
// M5Stack の初期化
M5.begin();
Serial.begin(115200);
// タスクの作成
xTaskCreate(&task1, "task1", 2048, NULL, 5, NULL);
xTaskCreate(&task2, "task2", 2048, NULL, 5, NULL);
}
// Arduinoのloop関数
void loop() {
// メインタスクのループ
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(10, 10);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(2);
M5.Lcd.print("Hello, M5Stack with FreeRTOS!");
vTaskDelay(pdMS_TO_TICKS(3000)); // 3秒待機
}
先頭部分の「freertos/FreeRTOS.h」あたりは、M5Stack.h から自動的にインクルードされているらしく必要ありません。RTOS 特有の関数としては、タスク生成の xTaskCreate の部分で、これが呼び出せていれば RTOS でビルドできています。
This page describes the RTOS xTaskCreate() FreeRTOS API function which is part of the RTOS task control API. FreeRTOS is a professional grade, small footprint, open source RTOS for microcontrollers. https://www.freertos.org/a00125.html
アップロードして動作させる
アップロードでして動作させると m5stack のモニタに「Hello, M5Stack with FreeRTOS!」の表示がでて、シリアルポートには、以下のような2つのタスクが交互っぽい形で動いていることがわかります。
ここまでで半日費やしてしまったので力尽きて、何をやろうとしていたのか忘れてしまいました。まあ、いいんですが。
後日、BLE通信のコードをちょっと入れ込んで試しおくつもりです。
余談
ちなみに、これらのサンプルコードは ChatGPT の GPT4o を使って尋ねているのですが、最終的に main.c と main.cpp に気付いた部分はこれになります。
いやいやいや、そもそも ESP-IDF フレームワークと Android フレームワークを混在させてきたのは君なんですが!をぐっとこらえてw
まあ、それでも GPT4o を使って、何度も「このコードだと動かないのですが、どうしたらいいですか?」を繰り返している訳で。なかなか一発で解決にはなりませんね。
それでも最初のスタートダッシュは生成AIを使うと便利です。