Preferences.putBytes() が0バイト書き込み(wrote=0)で失敗する“気づかない落とし穴”と、安定運用の対策
ESP32で開発していると、次のような「原因が見えにくい不具合」に出会うことがあります。
- 設定を保存したはずなのに、再起動すると元に戻る
- 昨日まで動いていたのに、突然不安定になる
- Wi-Fiやセンサーなど別の問題に見えるが、根本原因が分からない
- ログを見ると、保存処理が成功したり失敗したり揺れる
こうした“うまく動かない”の正体として見落とされがちなのが、NVS(Non-Volatile Storage)です。Arduino環境では Preferences(Preferences.h) がこの領域を使っており、ここが詰まったり条件を満たせなくなると、設定保存が失敗して挙動が破綻します。
この記事では、ESP32-C3環境で実際に遭遇した「Preferences.putBytes() が wrote=0 を返して保存に失敗する」事例をもとに、なぜ起きるのか、どう直すのが筋が良いかを整理します。Wi-Fi設定に限らず、ESP32の“設定永続化”全般に刺さる内容です。
この記事でわかること
- ESP32で「設定が保存されない」「不安定」「突然おかしい」原因がNVSにあるケース
Preferences.putBytes()が wrote=0 になる理由(単純な最大サイズ超過だけではない)- NVS統計(
nvs_get_stats)で見える「状態依存の揺らぎ」 - 運用で強い設計:NVSは最小、設定本体はLittleFS/SPIFFSへ
- 安定保存の実装方針:CRC/ヘッダ、デバウンス、tmp→rename(原子的更新)
NVSとは?ESP32の「意識しない領域」
ESP32のフラッシュ(例:4MB)は、内部でパーティション(区画)に分割されます。代表的には次のような領域があります。
- プログラム領域(app)
- ファイルシステム領域(SPIFFS / LittleFS)
- NVS領域(nvs)
- OTA管理領域(otadata)など
ここで重要なのは、次のズレです。
フラッシュ全体が4MBあっても、NVSは別区画(例:20KB)として固定される。
FSが空いていても、NVSが厳しい状態なら設定保存が壊れます。
この「4MBあるのに?」という直感とのズレが、NVS問題が気づきにくい最大の理由です。
いま起きている問題(現象)
A) Preferences.putBytes() が「0バイト書き込み」で失敗する
ログでは次の形で出ます。
putBytes total=8199 → putBytes wrote=0 → FAILED
putBytes total=16391 → putBytes wrote=0 → FAILED
つまり、保存したいデータ(JSON+ヘッダ+CRC)は組めているのに、書き込みAPIが0を返してストレージに載らない。
B) “同じサイズ”でも成功したり失敗したりが揺れる
さらに厄介なのが再現の揺らぎです。
- 4095(=4103 total)が失敗したログと成功したログが混在
- 8191(=8199 total)も成功と失敗が混在
単純な「最大サイズ超過」なら同じサイズは常に失敗するはずですが、実際は状況によって結果が変わるため、原因特定が難しくなります。
C) NVS統計で used/free が変動し、namespace も複数
例:
used_entries=430 free_entries=200 total_entries=630 namespace_count=4
(別ログ)used_entries=280 free_entries=350 namespace_count=1
NVS領域がそこそこ埋まっている状態があり、状態次第で成功/失敗が揺れている可能性が見えます。
D) NVSパーティションが小さい(20KB)
ログ例:
label=nvs size=20480 bytes (20 KB)
ESP32-C3でNVS 20KBはかなり小さめで、数KB〜十数KBのBLOBや更新頻度があるとすぐ厳しくなります。
根本原因(なぜそうなるか)
原因1:NVSは「巨大なデータを頻繁に上書き」する用途に向かない
NVSはKey-Value形式の永続領域ですが、内部的にはページ管理をしており、更新のたびに新しいエントリを使い、GC(ガベージコレクション)で整理します。
そのため、
- 同じキーを繰り返し
putBytesで上書き - サイズが大きい(数KB〜十数KB)
- 領域が小さい(例:20KB)
という条件が重なると、「連続した空き」や「GCのための作業余裕」が確保できず、書き込みが失敗しやすくなります。
原因2:「空きがある」≠「BLOBが書ける」
nvs_get_stats の free_entries が残っていても、BLOBに必要な内部条件(ページ/連続性/移動用の余裕など)が満たせないことがあります。だから空きがあるのに wrote=0という事象が起き得ます。
原因3:断片化+GC条件で“閾値付近”が揺れる
試験傾向として、
- 16KB級はほぼ保存不可
- 8KB級も状況次第で成功/失敗が揺れる
が出ています。これは典型的に、断片化やGCタイミングなど内部状態で結果が変わっているサインです。
原因4:伸びるデータ(ログ/履歴/一覧)をNVSに混ぜると地雷化する
設定JSONに、ログ・履歴・配列など「伸びる要素」が混ざると、NVSの用途(小さく安定した設定)から逸脱します。すると、ある日突然「保存できない」「設定が消える」「挙動が変」として表面化します。
対策はどれが筋が良いか(結論)
あなたの方針の中で、運用の確実性が最も高いのは次です。
✅ 推奨:NVSは“最小”、設定本体はLittleFS/SPIFFSへ移す
理由:
- NVSの断片化やGC条件に左右されない
- ファイルは大きくても扱いやすい(数十KBでも現実的)
- tmp→rename の原子的更新ができ、電源断にも強い
- 保存頻度を落とせばフラッシュ寿命にも優しい
今回の putBytes wrote=0 を根本から回避でき、将来の設定拡張にも耐性が出ます。
具体的な設計・実装方針(安定運用のテンプレ)
解決策A:設定ファイルをLittleFSに保存(推奨)
方針
cfg_blob.binにHeader{crc,len} + JSONを保存(現行方式を踏襲)- 保存は「値が変わった時だけ」
- 変更が続く場合は「最後の変更から10秒後に1回だけ保存」(デバウンス)
- 書き込みは
cfg_blob.tmpに書いてrename(原子的更新)
得られる効果
- NVS容量・断片化・GC由来の失敗が消える
- 大きな設定でも安定
- 電源断耐性(途中で落ちても壊れにくい)
解決策B:NVSは“小さいものだけ”に限定
NVSには、固定長で小さい値だけを保存します(例)。
configured=true/false(初期設定済みフラグ)bootCountlast_ok_crc(最後に成功した設定のCRC)fail_countなど
NVSに入れるべきでないもの:
- 巨大JSON
- ログ
- 履歴・一覧(増える配列)
- 可変長の大きいBLOBを頻繁に上書きするもの
解決策C:ログはFS/サーバへ
ログの保存先の優先順位は現実的にこうなります。
- サーバへ送る(HTTPでPOST)
- 端末内に残すならFSでローテーション
- SDがあるならSDへ
NVS拡張(パーティション変更)はどう扱うべきか
結論:拡張は“保険”にはなりますが、設計としては二番手です。
- 問題は容量だけでなく、BLOB更新+断片化+GC条件が絡む
- NVSを増やしても、肥大化すれば再発し得る
- パーティション変更は運用上やや重い(環境によってフルフラッシュが必要)
おすすめは、
- まず設計としてFSへ退避
- それでも必要ならNVS増量は最後
ログから読み取れる「現状の限界値」(試験結果の要約)
試験ログの傾向は次の通りです。
json_len=8191 (total=8199)は成功することがある(ただし揺らぐ可能性)json_len=16383 (total=16391)は失敗しやすい
つまり現状のNVS環境では、16KB級のBLOBは実質保存不可で、8KB級も内部状態次第で揺れる可能性があります。これは「設定が肥大化した瞬間に突然死する」地雷になり得ます。
最終提案(おすすめの最終形)
✅ 運用で強い構成
- 設定本体:LittleFS(CRC+len+json)
- NVS:フラグ/小さい値だけ
- 保存:変更時のみ、10秒デバウンス
- ログ:サーバ or FSローテーション
これで、
putBytes wrote=0問題を根本から回避- 将来の機能追加(設定拡張)にも耐性
- 設定の信頼性(CRC)も担保
が成立します。
チェックリスト:ESP32が「思うように動かない」時にNVSを疑うサイン
- Preferencesで保存した値が、再起動で戻る/一部だけ反映されない
putBytes/putStringの戻り値をチェックしていない- 大きいJSONや履歴・一覧をNVSに入れている
- UI操作(特にスライダー)で頻繁に保存している
- 同じサイズの保存が成功したり失敗したり「揺れる」
- NVSが20KB級の小さな構成
おわりに:原因が見えない不具合ほど、NVSが犯人になりやすい
ESP32には、開発者が意識しないまま踏む「小さな領域」があります。それがNVSです。
フラッシュ4MBの感覚で設計していると、NVS数十KBに引っかかって突然不安定になります。しかも表面症状がWi-Fiやセンサーなど別の問題に見えるため、原因に辿り着けない。
だからこそ、運用で強い方針として「NVSは最小、設定本体はFSへ」が効きます。
もしあなたが今、ESP32で「設定が保存されない」「突然おかしい」「原因不明の揺らぎ」に悩んでいるなら、まずはNVS統計(nvs_get_stats)と、Preferencesの戻り値を確認してみてください。意外なほどあっさり原因が見えることがあります。

コメント