esp32のnvsをちょっと見てみた

ESP-WROOM-32 SPIフラッシュのメモリマップ

ESP32モジュールのSPIフラッシュ内には、nvs (non volatile storage)と呼ばれる領域(0x9000から20Kバイト) があり、文字どおり不揮発領域として使われている。フラッシュメモリなのでモジュールの電源を落としても内容が消えないのは当たり前なのだけど、Arduino IDEからスケッチを書き込んだ際にも、この領域の内容は上書きされない。なので、スケッチが利用する諸情報をあらかじめnvs領域に仕込んでおき、スケッチ自体ではそれらをあたかもプロパティのように使うことができる(と思う)。

一つ前の話で、Arduino IDEからコンパイルしたスケッチをフラッシュに書き込む際には、スケッチを含めて4つのブロックのデータが転送されると書いたのだけど、パーティションテーブルの内容などを参考に転送先のイメージを図にすると以下のようになる。

(注: 現時点でのパーティションテーブル(default.csv) に定義されているのは0x9000以降だが、転送時のメッセージからすると上記のようになっていると推測した。また、パーティションテーブルの定義はシステムの更新に応じてどんどん変更されるかもしれない)。

このメモリマップのうち、nvs領域として使われているのは 0x9000 からの20K(0x5000) バイトのようである。パーティションテーブル領域自体はPCで作成されたファイルがESP32モジュールに転送されて書き込まれるので更新されるが、転送されるバイナリのサイズは4Kバイト未満なのでnvs領域の内容が上書きされることはない。

nvs領域を読み出してみる

nvs領域もフラッシュメモリの一部なので、esproolを使って読み出すことができる。

esptool.exe --port COM3 --baud 921600 read_flash 0x9000 0x5000 part.bin

を実行してpart.binというファイルに吸い上げた。この内容の一部をダンプしてみると、次のような具合。

0x00040以降の32バイトごとにキーのインデックスらしきものが入っている。nvsの概要、使い方、内部構造についてはNon-volatile storage library というドキュメントを参照。

上記のダンプからは、ネームスペース”misc”が内部インデックス番号 1 を、ネームスペース “nvs.net80211″がインデックス番号2 をもっており、”misc”には”log”というキー文字列が、”nvs.net80211″にはキー名 “opmode”や “country”といったキー文字列が登録されていることが読み取れる。内部解析じみたことをやってもあまり得るものがないので、nvs用のapiを使って内容を読み出してみることにした。

今回のプログラムは、ESP32モジュールを使い始めたときから気になっている、アクセスポイント名やパスワードがnvsに保存されていることと、プログラムから読み出しが可能なことを確認するために作成した。

NvsDump.h

nvs内の指定のネームスペース、指定のキー名のデータをblob型として読み出してシリアルモニタにダンプする。

nvs_open() を NVS_READONLY 以外で呼ぶと、存在しないネームスペースを指定したり、存在しないキー名を指定すると、どんどん作られてしまうかもしれないことに注意が必要。

このクラスを使う実装側として、WIFIステーションとして指定したアクセスポイントに接続するだけのスケッチを用意した。

ESP32_WIFI_STA1.ino

このスケッチをNvsDump.hと同じディレクトリに置いておき、Arduino IDEで開くと2つのファイルがおのおのソースタブとして表示されるので、選択/編集が容易にできる。

指定のアクセスポイント(今回は、AndroidスマホのWiFIティザリング機能を使った)に接続し、その状態で関係ありそうな項目をnvsから読み出してシリアルモニタに表示する。このスケッチではWIFI_STAモードしか使わないが、前回書いたスケッチでのアクセスポイント情報も読み出してみた。

上記のように、WIFI_STAでの接続情報だけではなく、別のスケッチで書いたWIFI_AP用の設定情報(ap.ssidとap.passwd)もちゃんと残ってました。

さらに、下記のようなnvs内容の表示だけを行うスケッチを実行しても、シリアルモニタには同じ内容が表示される。

先の書いた方の ESP32_WIFI_STA1.ino というスケッチでは、 WiFi.setAutoConnect(false); によってリスタート時の自動接続を禁止しているが、自動接続を許可している場合はnvsから接続先情報を読み出して自動接続すると思われる。

きょうのまとめ

nvs apiには保存されているネームスペースの一覧や、あるネームスペースに登録されているキー名の一覧を得る方法がない。しょうがないので最初に吸い上げた part.bin のダンプからそれらしいものを探して使った。

同じ機能をもつが接続先AP名がいろいろなWiFiステーションだったり、同じ場所にWIFI_AP機能をもつ複数のモジュールを配置するような場合、nvsを外から書き換える方法だけ考えておけば、プロパティ用にファイルシステムを使うこともないし、スケッチも一つで済むから楽かもしれない。何か思いついたら使ってみることにしよう。