85542
[状況]  
現在、10Hz(100msec)間隔で3軸加速度の値を取得し、SPRESENSEのFlashメモリへtxtファイルに書き込んで記録するプログラムを作成しています。  
しかし、microSDカードに記録することは問題がないのですが、Flashメモリに記録すると時間が経過するにつれセンサ値の取得の間隔時間が大きくなってしまいます。  
この原因はvoid loop()内でファイルオープン→書き込み→ファイルクローズをおこなってしまっているからと考えています。

[質問]  
1.ファイルを毎回オープンクローズせずに、Flashメモリにセンサ値が一定間隔で記録できるような手法がありますでしょうか。  
2.microSDカードとFlashメモリに記録する際になぜ添付した写真のような差が出るのでしょうか。

[環境]  
Arduinoにて開発しており、センサはROHM社のKX126-1063を使用しております。

[![画像の説明をここに入力](https://i.stack.imgur.com/S1uu9.png)](https://i.stack.imgur.com/S1uu9.png)
CC BY-SA 4.0
2022-01-07T01:36:32.863
85536
2022-01-07T16:28:36.823
50786
question
spresense arduino
Flashメモリに一定間隔で加速度の値を記録する方法
私自身は SPRESENSE や Arduino は使用していないので少し調べた程度の知識になります。

* * *

一般的に、ファイルへの書き込みはファイルハンドルを閉じる時に行われますが、短い間隔でオープンとクローズを繰り返していると遅延の原因となる可能性があります。  
`print` や `write`
を使っても即座にファイルへ書き込まれるわけではなく、いったんバッファに貯めてからファイルのクローズ時に実際のファイルに反映されます。

任意のタイミングでファイルへの書き込みをSDカードに保存するには、`file.flush()` を使用してみてください。

**参考:**  
[flush() | Arduino
日本語リファレンス](http://www.musashinodenpa.com/arduino/ref/index.php?f=1&pos=1740)  
[Arduino - FileFlush](https://www.arduino.cc/en/Reference/FileFlush)
CC BY-SA 4.0
2022-01-07T02:03:35.230
85538
2022-01-07T02:03:35.230
3060
85536
answer
一般的にフラッシュメモリというのは次の特徴があります

  * 任意のタイミングで任意の１バイトを書くことはできません
  * ブロック消去といって、かなり大きい単位での消去が必要です（例えば 64KiB 単位）
  * 消去済み領域に限り、ページ書き込みが可能です（例えば 2KiB 単位）
  * 消去した回数で寿命が決まります

よってフラッシュメモリ上のファイルに追記してクローズすると、だいたい次のような処理が走ります。

  * ファイルの最終部を保持している領域の現在内容を読みます
  * ファイルの最終部を含む消去領域をブロック消去します
  * ファイルの最終部に、先ほど読んだ内容＋追記したい内容を書き込みます
  * ファイルをクローズすると、ファイルの管理領域（ファイル名やサイズが保管されている部分）を読んで消去して上書きします

同じファイルの末尾に追記を続けるとウエアレベリング（損耗対策）をしていないメモリ装置では同じメモリセルに対して消去・書き込みを繰り返しあっという間に寿命に達してしまいます

A2.  
SPRESENSE の内蔵 SPI フラッシュメモリにアクセスするルーチンはおそらく前述の処理を毎回全部やっているのでしょう（時間が一定なのが根拠）

お使いの microSD
カードのコントローラはウエアレベリングを行っていて、よって同じファイルに書いているつもりでもメモリカード内コントローラが書き込むセルを移動させている。消去済みメモリセルに書き込む場合は高速、メモリセルの消去実施後に書き込む場合は低速と、時間が一定しないわけです（書き込みを継続すると消去済みメモリセルがなくなって時間が余計にかかる）

A1. 毎回 `close()`/`flush()` しなくてもいいのなら `setup()` で `open()` し `loop()` 内部では
`close()`/`flush()` しないことを推奨します。要望に応じて数百回に１回とか適切なタイミングで `flush()` しましょう。

組み込み系の場合、途中で電源が落ちたとか信号線が外れたとか、不慮の事故があった際に直前データをどこまで保証するか、でいろいろ話が変わってくるのですが、前述のとおり

  * 毎回 `close()` する＝事故直前データが後で読める可能性が高い。が、損耗が大きそう。
  * `close()` や `flush()` しない＝事故直前データが失われる可能性が高い。それだけならまだしも、ファイル全部が失われることがありそう。でも損耗は小さい＝長時間保つだろう。

なのであとは案件次第。
CC BY-SA 4.0
2022-01-07T05:20:56.900
85542
2022-01-07T05:20:56.900
8589
85536
answer
加速度の値を取得するたびにファイルに書き込むのはパフォーマンス的にもFlashの寿命的にもよろしくないので、バッファ（メモリ）にある程度溜めておいてからまとめて一気にファイルに書き込む方が良いと思います。

まとめて書き込むとしても、一つのファイルに追記していくと、そのファイルのサイズに依存して書き込み時間も増加していくので、ある程度のサイズになったら新しいファイルに書き込むように切り替えた方が良いです。また、ファイル数が増えると、それも書き込み時間の増加につながっていきます。

Flash書き込みの間に加速度データを取り逃してしまう場合は、マルチコアを使って、加速度をメモリに保存する処理をサブコアで、メモリからFlashへ書き込む処理をメインコアに、と分担させるのもありかと思います。

それ以外には、Arduino環境でもSpresense
SDKのRTOS機能が使えるので、マルチタスク機能を使って、Flashへの書き込み処理を低優先度タスクに処理させるという方法もあると思います。
CC BY-SA 4.0
2022-01-07T16:28:36.823
85554
2022-01-07T16:28:36.823
31378
85536
answer