M5Stack で RTOS を試すときに main.cpp で動作させること

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を使うと便利です。

カテゴリー: 開発 パーマリンク