simotin13's message

simotin13といいます。記事の内容でご質問やご意見がありましたらお気軽にコメントしてください\^o^/

esp-idfのソースコードを読んでみた 〜bootloaderとfreeRTOSの起動〜

ESP32について調べ始めました。
ESP32ではfreeRTOSが標準で採用されていてexamplesのアプリケーションではmain_app_taskというタスクのアプリケーションが動き始めます。
freeRTOSは初めて触るのですが、電源ONからどのようにESP32/freeRTOSが動くのか分からなかったので調べておきたいと思います。

ブートについて

First Stage Bootloader と Second Stage Bootloader

ESP32の起動フローは以下のページで解説されています。
docs.espressif.com

この解説によれば、First Stage BootloaderはFlashROMの0x1000に書いてあるSecond Stage Bootloaderをメモリ上にロードし実行するそうです。
CPUのリセットベクタのコードはESP32内のマスクROM領域に保存されており変更できないそうです。

PRO CPUと APP CPU

コードを読む際に並行してesp-idfのドキュメントも読んでいると唐突にPRO CPUとAPP CPUという用語が登場します。

www.esp32.com

こちらのフォーラムのコメントによれば、何のことはないESP32はマルチコアなので
PRO CPU→Core 0
APP CPU→Core 1
の意味でした。
PROはProtocol AppはApplicationの略で、ESP32が非対称な初期化(asymmetric multiprocessor setup)を行う設計に起因しているようです。FreeRTOSではSMPをサポートしているのでPRO、APPという呼称はあまり意識する必要はないかもしれません。ただし、ログ出力では Pro cpu, App cpuといった単語でログ出力が行われている箇所があるので意味を把握しておく必要はあります。

Second Stage Bootloader

Second Stage Bootloaderではオフセット0x8000 に配置されているパーティションテーブルを読み出し、必要に応じてOTA Updateを行うそうです。

ソースコードの場所

esp-idfから提供されるOS・ライブラリ等のソースコードはesp-idf-vX.X.X\components 以下に配置されています。(X.X.Xはインストールしたesp-idfのバージョンで読みかえてください)

Second Stage Bootloaderのコードは components\bootloader\subproject\mainに

  • bootloader_start.c
  • bootloader_hooks.h

というファイルがあり、同じ階層のldフォルダにはリンカスクリプトが配置されています。

エントリポイント

bootloader.ldでは

/*  Default entry point:  */
ENTRY(call_start_cpu0);

の記述があり、Second Stage Bootloaderは call_start_cpu0 から始まります。
この関数では bootloader_init という初期化関数を呼び出しています。
bootloader_init の実体はbootloader_support/src以下のCPU種別(esp32,esp32c2,esp32s2,esp32h2...etc)のフォルダ以下のファイルで実装されています。

具体的な処理としてはCPUの初期化などになります。
この後 パーティションテーブルからブートすべきパーティションを選んで bootloader_utility_load_boot_imageにより該当するパーティションをロードしてそちらに処理が移ります。

アプリケーションの起動

ロードされたイメージのエントリポイントは esp_system/port/cpu_start.c で定義されている call_start_cpu0 になります。
Second Stage Bootloader のエントリポイントも call_start_cpu0 という関数名で定義されていたのでこれは正直違う関数名の方が良かったのではという気がします。

call_start_cpu0 ではCPU、HWに関わるいくつかの初期化処理を行ったあとSYS_STARTUP_FNのマクロ関数を呼び出しています。
SYS_STARTUP_FNの実体は esp_system/include/esp_private/startup_internal.h で次のように定義されています。

#define SYS_STARTUP_FN()  ((*g_startup_fn[(esp_cpu_get_core_id())])())

ようするにコア毎の初期化用関数ポインタのテーブルに登録されている関数を呼び出しているのですがこの
g_startup_fnの実体は esp_system/startup.cで定義されており

  • PRO CPU(core 0): start_cpu0
  • APP CPU(core 1〜): start_cpu_other_cores

が登録されています。
start_cpu0,start_cpu_other_coresの実体はそれぞれ、

  • start_cpu0_default
  • esp_startup_start_app_other_cores_default

で weak pointer として登録されているのでユーザが任意の処理に上書き可能です。

freeRTOSの起動

start_cpu0_default では主にライブラリやC++のコンストラクタの呼び出しなどソフトウェア的な初期化を行っているようです。
本格的にアプリケーションの機能を利用できるようにする前段階という雰囲気があります。

ソフトウェア的な初期化処理が終わったあと、 freertos/app_startup.c に定義されている esp_startup_start_appを呼び出します。

esp_startup_start_appでは

    BaseType_t res = xTaskCreatePinnedToCore(main_task, "main",
                                             ESP_TASK_MAIN_STACK, NULL,
                                             ESP_TASK_MAIN_PRIO, NULL, ESP_TASK_MAIN_CORE);

を実行した後、vTaskStartSchedulerを呼び出しています。vTaskStartSchedulerは文字通りカーネルのスケジューリングを開始するので、ここからfreeRTOSが起動した状態になります。

main_taskはデーモンタスクとして位置づけられています。
ただし、必ずしもこのタスクがループを持って動作し続ける必要がある訳ではありません。他のタスクを起動したらそのまま終了しても特に問題が生じるわけではありません。

まとめ

浅くですが、esp-idfでの起動処理を追いかけてみました。
個人的な印象として他のCPUベンダーのSDKよりオープンで開発者が使いやすいように考えられている印象を受けました。
そもそもSDKgithubで提供しているというのもあまりない気がします。

CPUベンターが提供する初期化処理やライブラリには時々バグがあってふとしたことでそのバグを踏むことがありますが、報告する場所がオープンになっていなくてそれが本当にバグなのかどうかも疑わないといけない状況だったりします。*1
そういう経験を踏まえるとgithubである程度気軽にISSUEを立てたりPRを出したりできるエコシステムがあるのはとてもありがたいです。

ということで、主にブート周りのコードを読んでみましたが、気が向いたらfreeRTOSの使い方もまとめて記事にするかもしれません。

*1:そして気がついたらSDKの更新でしれっと直されてたりします

Ubuntu上のVSCodeでESP32をJTAGデバッグする

仕事でESP32を使うプロジェクトが始まるのでESP32について勉強を始めてみました。
ESP32はこれまでお遊び程度に軽く触ったことはありましたが、本格的に触るのは初めてです。
esp-idfで開発環境を整備していますが、 Ubuntu上でJTAGデバッグをした人の記事が少なかったので書いておきたいと思います。

試した環境

OS

Ubuntu 24.04 LTS

ボード

ESP-WROOM-32D
秋月電子さんで買ったものです。
akizukidenshi.com

開発環境

VSCode拡張機能 Espressif IDF からesp-idfをインストールして試してみました。
esp-idf の詳細についてはこの記事では省略します。

Build and Flash

getting startedに含まれるhelloworldプロジェクトを使って動作確認をしてみました。
このプロジェクトではシリアルモニタにログが出力されるので実際に基板が動作していることが確認できます。
基板への書き込みは

idf.py flash

で書き込めます。

注意点として、Ubuntu環境に対して基板をUSBで接続したとき、基板は

/dev/ttyUSB0
|<<
のようなデバイスファイルとして見えると思います。

デバイスファイルへアクセス権は最初は付与されていないので
>||
sudo chmod 777 /dev/ttyUSB0

のようにターミナルで権限を与えておく必要があります。(UARTとしてRead,Writeができればいいので766とかでよいのかもしれません)

JTAG接続

今回のデバッグではAdafruit FT232Hの基板を使用しました。
こちらは秋月電子さんやAmazonでも売られています。
akizukidenshi.com

ピン配置

JTAG接続では、MTCK,MTDI,MTDO,MTSMの4つのピンをつなぐ必要があります。
Adafruit FT232HとESP32の基板の接続については、こちらの記事を参考にさせていただきました。
wondercode.hatenablog.jp

参考にさせて頂きありがとうございます。


ESP32の多くのボードではJTAG用にピンが割り当てられているようです。
今回使用した基板でのピンの割当は、

機能 ピン
MTCK GPIO13
MTDI GPIO12
MTDO GPIO15
MTMS GPIO14

になります。

ちなみに、ESP32-S3-DevKitC-1-N8やFreenove_ESP32_S3_WROOMの基板では

機能 ピン
MTCK GPIO39
MTDI GPIO41
MTDO GPIO40
MTMS GPIO42

になるようです。市販のESP32の基板ではどちらかのパターンで割当されていることが多いようです。

JTAGの接続

上記の参考にさせて頂いた記事では、FT232H側から5Vで電源供給もされていましたので私も電源をFT232H側から取るようにしてみました。

デバッガの起動

FT232Hの基板とESP32の基板をつないだらVSCode上でデバッグを開始するだけです。
注意点として、launch.jsonのconfigurationsにEclipse用の設定が含まれていると起動できませんでした。
削除してespidfのみが含まれるようにします。

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "espidf",
      "name": "Launch",
      "request": "launch"
    }
  ]
}
VSCodeでのESP32デバッグ中画面

デバッガが起動するとapp_main関数でブレークした状態になります。
F10でステップ実行ができ、変数の上にマウスカーソルを持っていくと現在の値も確認できます。

まとめ

Ubuntu上でのデバッグ方法についてまとめてみましたが、実はWindows上でのデバッグはうまくできませんでした。
カメラのついたESP32-S3-WROOMの基板も持っているのでこちらでのデバッグも試してみて追記したいと思います。

MSIのMordern-14Hを買った

普段外に持ち運びできる作業用ノートPC としてMacBook Pro Late 2012 を使っていましたが、購入した2014年からちょうど10年が経ったのでMSIの Morden-14-C13M-1501JPというMSIのノートPCに買い替えました。
MacBook Pro Late 2012はBoot CampでWindows10を入れて実質的にWindowsマシンとして使っていて、大きな不満はなかったのですが、規模の大きなOSSのプロジェクトをビルドするときなどに時間がかかるのが辛いと感じることが多くなってきていたことが理由の1つです。

購入したMordern-14HはCore-i9 メモリ32GB、ストレージはSSD1TBでOSも最初からWindows11 Proがインストールされているという開発者にとってはちょうどよいスペックのモデルです。
まさに自分が欲しかったスペックだったので買ってみましたが、今のところいい感じに使えてますので簡単ですがレビュー記事を書いておきたいと思います。

重さ

計量していませんが本体1.6Kgとのことです。特別軽いわけではありませんが、実際持ってみた感じ今どきの一般的なノートPCの重さだなという印象で、普通に軽いと感じます。

キーボード

Morden-14-C13M-1501JPのキーボードは日本語配列のモデルです。
使っていたMac Book Proと比べて、

  • 全体的にキーがすこし右寄りに配置されている(ように感じる)
  • キービッチが少し浅い(不満に感じるほどではない)
  • 右下のカーソルキー周りのキーが小さい(これは少し不満に感じる)

といった点が気になりますがこれはある意味慣れの問題でもあるのでそのうち気にならなくなると思います。

キーボード

モニタ

解像度は1920x1200です。モニタ上部にシャッターつきのカメラもついています。
特に違和感を感じる点はありません。

端子・インターフェース類

左側に、

  • イヤホンジャックの端子
  • USB TypeA x 1
  • USB TypeC(PD対応) x1
  • 電源アダプタ用端子

後ろ側に、

  • RJ45(LANケーブル)
  • USB TypeA x2
  • HDMI

となっています。
右側には端子類は一切ありません。

ちなみに付属の電源アダプタは少しかさばるのですが、Type-Cの端子はPower Deliveryに対応しているのでType-Cの充電器を使って移動するときは少しでも楽になるようにしています。

性能面

Core i9-13900Hはコア数16、スレッド総数20というとても強力なCPUが搭載されています。
www.intel.co.jp

試しにruby-3.3.5をダウンロードしてビルドしてみました。
CPUのスレッド数に合わせて、make -j 20 してみましたが17秒でビルドが終わりました。./configureの方が時間がかかっています。

make[1]: Leaving directory '/home/miyazaki/ruby-3.3.5'
Generating RDoc documentation
Parsing sources...
100% [1026/1026]  yjit.rb

Generating RI format into /home/miyazaki/ruby-3.3.5/.ext/rdoc...

  Files:       1026

  Classes:     1238 ( 296 undocumented)
  Modules:      229 (  72 undocumented)
  Constants:   2383 ( 650 undocumented)
  Attributes:  1464 ( 249 undocumented)
  Methods:    12191 (2214 undocumented)

  Total:      17505 (3481 undocumented)
   80.11% documented

  Elapsed: 17.1s

試しに普段自分が使用しているデスクトップPC(i5-8500、6スレッド、メモリ16GB、SSD:1TB)でもビルドしてみましたが23.6秒かかっていました。
ビルドにかかる時間が短縮されるのはとてもありがたいです。

まとめ

ということでスペック的に申し分ないノートPCを手に入れていまのところ満足しています。
AmazonではMSIの公式から出品されているものを購入しましたが、この商品は人気があるようで、割とすぐに売り切れては入荷待ちの状態になっています。
私もAmazonで購入しようとして数日眺めている間に売り切れてしまい2週間後に入荷されたタイミングですぐに購入しました。


今後も使っていて気づいた点があれば追記していきたいと思います。

追記

2024/09/21

タッチパッドについて

本格的に使い始めましたが、タッチパッドでの操作感が気になりました。
私はタッチパッドは基本的に右手のみで操作していますが、Mac Book Proと比べてタッチパッドが横に広いので、手をあまり動かさずに操作すると左クリックのつもりでタッチしていても右クリックとしての操作になってしまいます。なので左クリック・右クリックに対応する操作をしようとすると手をしっかり動かしてからタッチパッドを操作する必要があります。これは以外と使い辛い点ですね。

発熱について

ノートパソコンなので膝の上に置いて作業をすることが多いですが、今のところ発熱に関する違和感は感じたことがありません。

FFmpegのコードを読み始めた

自分向けの備忘録です。

ソースコードの入手とビルド

ソースコードの入手

githubにミラーされているのでclone
github.com

git clone git@github.com:FFmpeg/FFmpeg.git

ビルド方法

Ubuntu上で作業をしているので以下のページを参照します。
trac.ffmpeg.org

aptで色々と入れる必要はあります。

sudo apt update
sudo apt install nasm yasm libx264-dev libnuma-dev libx265-dev libvpx-dev libfdk-aac-dev libopus-dev libaom-dev autoconf automake build-essential cmake git-core libass-dev libfreetype6-dev libgnutls28-dev libmp3lame-dev libsdl2-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev meson ninja-build pkg-config texinfo wget libunistring-dev libsvtav1enc-dev libvorbis-dev

私の環境では依存関係の問題などが出ましたがとりあえず以下の./configureが通るようになりました。

./configure --prefix="$HOME/ffmpeg_build" --pkg-config-flags="--static" --extra-cflags="-I$HOME/ffmpeg_build/include" --extra-ldflags="-L$HOME/ffmpeg_build/lib" --extra-libs="-lpthread -lm" --ld="g++" --bindir="$HOME/bin" --enable-gpl --enable-gnutls --enable-libaom --enable-libass --enable-libfdk-aac --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libsvtav1 --enable-libdav1d --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libx265 --enable-nonfree

./configureが通れば makeコマンドを叩くだけですんなりビルドできました。

実行

ビルドが完了するとフォルダ内に ffmpegなどの成果物ができてみます。
Tp-LinkのC210で録画を試してみました。

 ./ffmpeg -i rtsp://username:password@192.168.1.123:554/stream1 -c:v copy -c:a aac -strict experimental output.mp4

mp4のフォーマットに関する部分を知りたい

自分が作っているRTSPのクライアントではH.264の動画を.mp4のファイルに保存する(録画)機能を実装しようとしています。
H.264AACのパケットを受信するところまではできるようになったのですが、mp4のフォーマットをどのように出力するのかがよく分かっていません。

mp4にはアトムやボックスと呼ばれるデータ構造の単位がありこのデータ構造に動画や音声のデータを入れたり、動画の再生などで必要となるメタデータを入れる必要があります。
どのようなボックスにどういったデータがあるのかはある程度理解できてきたのですが、細部については不明な点が多いので理解のためにFFmpegのコードを読むことにしました。

libavformatを読む

FFMpegはマルチメディア処理に関連するいくつかのモジュール群から構成されています。
私が知りたいのはmp4のデータフォーマットの書き込み(エンコード)方法なのですが、これは libavformat というディレクトリ以下で実装されています。
mp4のボックスに関するコードは moven.cで主に実装されていました。

例えば mp4のファイルの先頭にはftypというメタデータ用のボックスが格納される必要がありますが、これは mov_write_ftyp_tag という関数で実装されています。
ファイルへの書き込み処理はメインスレッドとは別スレッドで行われます。

とりあえずどの辺で書き込みが実装されているかは把握できたので続きは別の日にします。

自宅に太陽光発電を設置した結果~2年目~

太陽光発電・蓄電池のシステムを自宅に設置して2年目の結果が出たので簡単に振り返ってみたいと思います。(厳密には2年と半年ほどになります)

1年目の結果は以下の記事を参照。
mcommit.hatenadiary.com

2023年のデータ

発電データとグラフは以下のような結果でした。

2023年発電データ
2023年発電グラフ

2年間の推移

発電量(kWh) 消費量(kWh) 売電量(kWh) 買電量(kWh) 蓄電池放電量(kWh) 自給率(%)
2022 2,378 4,306 386 2,397 1,047 55
2023 2,441 4,344 435 2,423 1,030 56

2年目までの推移は上記の通りです。
化内容を見ると、

  • 発電量が63kWh増えた
  • 消費量が26kWh増えた
  • 売電量が49kWh増えた
  • 買電量が26kWh増えた
  • 蓄電池放電量が17kWh減った
  • 自給率は1%増えた

という結果です。

シミュレーション結果との比較

導入時に営業の人が持ってきたシミュレーション結果との比較を見てみましょう。
年間推定発電量は2,921kWhなので2023年の結果と比較すると480kWhの差異があり、金額にすると約10,000円程度はシミュレーション結果より損した結果になっています。
この点は2022年の結果と同じですね。

https://cdn-ak.f.st-hatena.com/images/fotolife/s/simotin13/20230129/20230129011749.png

電気料金と再エネ賦課金の推移について

再エネ賦課金は2022年度は3.45円/kWhでしたが、2023年は1.4円/kWhになっています。
これは再エネ賦課金を求める式のうち、「回避可能費用」に相当する金額が大幅に上がったためだそうです。
再エネ賦課金は2023年は下がりましたが、基本的には2030年までは上昇すると予想されています。

感想

太陽光発電・蓄電池のシステムは得か損か」の最終的な結果を知るために毎年このような振り返りの記事を書いてみていますが今のところ「やや損している」といったところでしょうか。
得をしているということはつまり電気代が上昇しているということなのでそれ自体は素直に喜べる状況ではないのですが、今年もまた電力事情には注目しておきたいと思います。