ESP32で自作マウス〜その4〜
前回のあらすじ
前回は躯体を作ったり、ホイールの実装をして、だいぶ形になってきました。
今回はマルチペアリングや省電力化を実現するために、ESP-IDFの導入に挑戦します。
ESP-IDF導入の動機
そもそも、なぜ今までArduino IDEで開発してきたのに、ESP-IDFに変更する必要があるのか?そして変更すべきなのか?というのは当然の疑問としてあると思います。
まず第一に、前述したように「マルチペアリングと省電力化の実現のため」です。少電力を実現するには、動作していないときに自動でスリープするautomatic light sleepが不可欠ですが、Arduino IDEのデフォルト設定では無効化されています。
同様に、マルチペアリングも処理の課程でArduino IDEではできなさそうなものがありそうだということが分かりました。
そして第2に、そもそもArduino IDEは正直使いやすいツールであるとは言えません。コード補完が効かないどころか、そもそも言語もCっぽいなにかで、いまいちよくわかりません。加えてLInuxの場合はホームフォルダーの下に設定用の隠しフォルダを作成されて、ただでさえぐちゃぐちゃしているホームフォルダーを汚されます。
以上の理由からESP-IDFを導入するのですが、一点注意として、VSCodeにはESP-IDFの拡張機能がありますが、これは使わないことをおすすめします。僕も試したんですが、ビルドが通らないとかいうレベルじゃなくて、ビルドツールが壊れていて、情報も少ないのでやめたほうがいいです。
ESP-IDFの導入
導入するときは以下の記事に沿ってやりました。
インストールまではこれでいいでしょうから、使い方を軽く書いておきます。
インストールが済むと、esp-idf直下のexport.shを使って環境変数を登録するとIDFが使えるようになるんですが、面倒だな〜と思って.bashrcや.bash_profileに処理を追加して見るんですけど、どっちもうまく行かないので、頑張って毎回パスを打ちましょう。一応export.sh使う時のコマンドを書いておきます。
$ source $HOME/esp/esp-idf/export.sh
それができたらidf.pyというツールが使えるようになって、ビルド、設定、書き込みをできるようになります。とりあえずプロジェクトを作るところから説明します。
プロジェクトの作成
普通、この手のツールにはcargo new的なコマンドがあると思うんですけど、調べた感じ、idf.pyの場合は毎回サンプルフォルダーをコピーするしか無いようです。バカバカしいですが、毎回
$ cp -r $HOME/esp/esp-idf/examples/get-started/hello_world ./
でフォルダーをコピーしましょう。プロジェクトはespフォルダー直下に作って行くのが正解かなと想います。
フォルダーの使い方
こうして作ったフォルダーはこんな感じ(READMEに書いてあるまんま)
├── CMakeLists.txt
├── pytest_hello_world.py Python script used for automated testing
├── main
│ ├── CMakeLists.txt
│ └── hello_world_main.c
└── README.md This is the file you are currently reading
多分pytest_hello_world.pyとREADME.mdは消しても差し支えないと思います。
で、一番上のフォルダーにあるCMakeLists.txtはCMakeが正常に動作するためにあるやつで、基本的に僕らがいじるのはmainフォルダー以下です。
ここにあるCMakeLists.txtでファイルの指定をします。こんな感じに書いてあります。
idf_component_register(SRCS "pm_config.c"
PRIV_REQUIRES spi_flash esp_pm
INCLUDE_DIRS "")
SRCSはそのまんまメインのソースファイルです。PRIV_REQUIRESはESP32のヘッダファイルで必要なものがあれば記述します。ここでは電力管理のためにesp_pmを追加しています。そしてINCLUDE_DIRSはプロジェクトで使用しているヘッダファイルがある場所を指定します。
複数の値を書く場合はPRIV_REQUIRESがそうなっているようにスペースで区切って書きます。
SDKの設定
前述したとおり、ESP-IDFを導入した理由は設定をいじることですが、具体的にはsdkconfigというファイルをいじります。ただし、直接いじるのではなく、idf.py経由でいじります(多分直接いじっても問題ないですが、それなりに使いやすいので素直にidf.py経由で良いと思います)。
まずこのコマンドで設定画面を開きます。
$ idf.py menuconfig
あとはBIOSチックな操作で設定をいじることができます。
Automatic Light Sleepの有効化
それではESP-IDFの使い方も分かったところで、Automatic Light Sleepを有効化してみましょう。まず設定で以下の2つを有効化します。
Component config>Power Management>Support for Power Management
Component config>FreeRTOS>Kernel>configUSE_TICKLESS_IDE
ちなみに最初にPower Managementを有効化しないと、FreeRTOSの方は設定が表示されません。
これで設定は無事有効化されたので、以下のコードをpm_config.cに書きます。いい忘れていましたが、ESP-IDFは普通にC言語で記述できます(Arduinoは微妙に違うものを使っている)。多分Arduinoのライブラリもそのまま使えるはず。
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "esp_pm.h"
void app_main(void)
{
printf("Hello world!\n");
/* Print chip information */
esp_chip_info_t chip_info;
uint32_t flash_size;
esp_chip_info(&chip_info);
printf("This is %s chip with %d CPU core(s), %s%s%s%s, ",
CONFIG_IDF_TARGET,
chip_info.cores,
(chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "",
(chip_info.features & CHIP_FEATURE_BT) ? "BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "",
(chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : "");
unsigned major_rev = chip_info.revision / 100;
unsigned minor_rev = chip_info.revision % 100;
printf("silicon revision v%d.%d, ", major_rev, minor_rev);
if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) {
printf("Get flash size failed");
return;
}
printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());
esp_pm_config_t pm_config = {
.max_freq_mhz = 240,
.min_freq_mhz = 80,
.light_sleep_enable = true
};
printf("configure pm\n");
esp_err_t err = esp_pm_configure(&pm_config);
printf("OK\n");
if (err != ESP_OK) {
printf("failed to configure pm\n");
printf(esp_err_to_name(err));
printf("\n");
} else {
printf("successed to enable automatic light sleep\n");
}
for (int i=0; i<100; i++) {
printf("%d\n...", i);
}
}
別に上の方は削れるんですが面倒なのでほぼそのまま使ってます。
ただし、一番最後のfor文は必要で、これがないとSerial通信をしている間にLight Sleepに入ってしまうので、ちゃんと出力を確認するために追加してます。
あと、CMakeLists.txtは先程書いたものを使っています。
ここまで準備ができたら以下のコマンドでビルドから書き込みをして、シリアルポートを確認します。
$ idf.py build
$ idf.py flash
$ idf.py monitor
以上でAutomatic Light Sleepの実装は終了です。ただもしかするとBluetoothの維持には他の設定をいじる必要があるかもしれませんが、それはまた後日。
まとめ
今回はESP-IDFを導入して、Automatic Light Sleepを有効化してみました。正直情報が少なくて苦慮しましたが、だいぶ分かってきたのであとはスムーズに開発できるんじゃないかなと思います。
次回は今ArduinoIDEで管理してるフォルダーをGitレポジトリごとこちらに移して、作業の続きをしたいと思います。
ところで、最近ChatGPT-o1がGithub Copilotに統合されるという話を聞いて、Github Copilotを使うべく、学生登録したんですが、承認されてからなかなか特典が有効化されずもやもやしています。アメリカがちょうど9月始まりで申請が混雑しているんでしょうかね。機会があればGithub Copilotについても書こうと思います。