Nordic nRF5ライブラリにFDS(Flash Data Storage)という簡易なファイルシステムライブラリがあるのでそれの紹介です。
サンプルコード
https://github.com/ksksue/fds-example
githubのコードでは以下の操作を行うサンプルを用意しています。
- 3ファイル書込
- 3ファイル検索
- 3ファイル削除
- 3ファイル検索(NOT FOUND期待)
実行結果
write file_id:0001 rec_key:0001, data:00001234 write file_id:0002 rec_key:0002, data:00005678 write file_id:0003 rec_key:0003, data:00009ABC found file_id:0001 rec_key:0001, data:00001234 length:1 found file_id:0002 rec_key:0002, data:00005678 length:1 found file_id:0003 rec_key:0003, data:00009ABC length:1 delete file_id:0001 rec_key:0001 delete file_id:0002 rec_key:0002 delete file_id:0003 rec_key:0003 ERROR drv_fds_find(171) : ErrNo. 10 // 削除した後、検索してもNotFoundエラーとなる ERROR drv_fds_find(171) : ErrNo. 10 ERROR drv_fds_find(171) : ErrNo. 10
FDSを使う背景
IoT製品においてThings側(マイコン)にデータを保存したくなるケースがあります。 ログ情報、ID情報をマイコン側でキャッシュすることでユーザー体感レイテンシを下げ、UXを上げるといったテクニックがありますが、 それを実現するためにはマイコンでしっかりとデータ保存する仕組みを構築しなければなりません。
そこで立ちはだかる問題としては、まずマイコンにはサーバーやPCのような使いやすいストレージがあるわけではないので256kBほどの容量の少ないFlashを利用することになります。 またBLE通信をベースとすると、BLE通信もFlashアクセスもCPU占有時間が長いため排他処理方法を考慮しなければなりません。 そして、やっかいなのはFlashの書込/イレース10,000回上限がある点です。仮に1日10回ほど同じ箇所に上書き更新するだけで1.5年ほどで寿命がきてしまうという点です。
FDSは、以下の3つの特長をそろえつつ、小さい容量で実装できる優れたファイルシステムライブラリです。
ほどよい検索:データに2種類の数値キー情報を付与することができます。そのキーで検索することができます。組込みにおいてはこれくらいのカンタンな検索性で十分です。
BLE通信とコンカレントに利用可能:nRF5シリーズはBLE通信ができるのが最大の特長ですが、そのBLE通信とFDSはコンカレントに利用可能です。 Flashの書込/イレースは時間のかかる処理のため同時に処理するとFlashアクセス/BLE通信のいずれかを止めることになりますが、 FDSではタスクをペリフェラルに委譲するといった処理を内部でしているため開発者はそこを意識する必要はありません。
ウェアレベリング:書込/イレース回数にかぎりのあるFlashにおいては重要な仕様です。 FDSでは削除しても実際にイレースするわけではなくフラグを立て、ガベージコレクトするときに実際にイレースするといった方法を取っています。
また、ハード的な制約としては2つありますので留意しておきましょう。
FDSの仕様
FDSの関数にはいわゆるCRUD(Create/Read/Update/Delete)が用意されていて FDSではwrite/find/update/deleteという名前が付いています。 以下、writeとfind, deleteについて見ていきます。
write
writeでは、データ情報の他に、後で検索するためのfile_idとrec_keyの数値を付与します。
writeのコード例
uint16_t file_id = 1; uint16_t rec_key = 1; uint32_t data = 0x1234; FDS_DPRINTF("write file_id:%04x rec_key:%04x, data:%08x\n", file_id, rec_key, data); ret = drv_fds_write(file_id, rec_key, &data, 1); CHECK_ERR(ret);
実行結果
write file_id:0001 rec_key:0001, data:00001234
※ drv_fds_writeはこちらで用意したFDSのラッパーですが、FDSを使うには十分な引数を備えています。
file_idとrec_keyは、範囲が - file_id 0x0000 - 0xBFFF - rec_key 0x0001 - 0xBFFF という以外は制約はありません。重複もOKです。 これら情報は検索のキーとして使われます
dataはワードアライメントのデータ、その長さをワード長で指定します。
find
findのコード例
file_id = 1; rec_key = 1; ret = drv_fds_find(file_id, rec_key, &data, &length); CHECK_ERR(ret); FDS_DPRINTF("found file_id:%04x rec_key:%04x, data:%08x length:%d\n", file_id, rec_key, data, length);
先程のwriteを実行した後、このコードを実行すると以下の結果になります。
実行結果
found file_id:0001 rec_key:0001, data:00001234 length:1
もし該当するfile_id&rec_keyのファイルが見つからなかった場合は、ret に FDS_ERR_NOT_FOUND ( = 10 )が返ってきます。
delete
file_idとrec_keyを指定して削除します。
file_id = 1; rec_key = 1; FDS_DPRINTF("delete file_id:%04x rec_key:%04x\n", file_id, rec_key); ret = drv_fds_delete(file_id, rec_key); CHECK_ERR(ret);
実行結果
delete file_id:0001 rec_key:0001
ガベージコレクト
ガベージコレクトを走らせることでFlashから物理的にデータをイレースすることができます。
FDSでは、deleteをしたときにファイルの削除フラグを立てることで論理的に削除しますが実際にFlashをイレースするわけではありません。 つまり、何度もwrite/deleteし続けるとFlash容量がオーバーすることになります。 そうならないよう、定期的にガベージコレクトを実行する必要があります。
サンプルコード内では最後に実行しています。
ret = drv_fds_gc(); CHECK_ERR(ret);
利用する際の注意点
自身のプロジェクトでFDSを使うには以下の点に気をつける必要があります。
softdeviceのsys_evt_dispatchでfs_sys_event_handlerを呼ぶ
fds内で使われているfstorageでsoftdeviceからのコールバックを受け取る処理があります。 fstorageではsoftdeviceに対してflashのwrite/eraseを要求し、その結果がコールバックとして返ってきます。 そのコールバックはsys_evt_dispatchに飛んでくるためfs_sys_event_handlerでそれをキャッチするという仕組みです。
コード上ではsoftdeviceに対してsys_evt_dispatchを登録します。
err_code = softdevice_sys_evt_handler_set((sys_evt_handler_t)sys_evt_dispatch); APP_ERROR_CHECK(err_code);
そのsys_evt_dispatchコールバック内でfs_sys_event_handlerを呼びます。
void sys_evt_dispatch(uint32_t sys_evt) { fs_sys_event_handler(sys_evt); }
SDK 12以上のFDSを使う
本記事では都合でSDK11のFDSを使っていますが、SDK12にてfds_gcにクリティカルな修正が入っています。 SDK12以上を使うか、SDK11にSDK12のfdsをポーティングすることをオススメします。
まとめ
FDSを使うことでnRF5 BLEマイコン上で簡易なファイルシステムを構築することができます。 FDSはNordicのnRF5シリーズ限定のライブラリではありますがIoT開発において非常に強力なツールになるでしょう。
告知
フォトシンスでは採用を強化しています。 IoT製品に興味のある方はぜひご検討ください。 www.wantedly.com