質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
保存

保存(save)とは、特定のファイルを、ハードディスク等の外部記憶装置に記録する行為を指します。

Q&A

解決済

1回答

5898閲覧

SDカードへの保存において、一定以上のデータが保存されない

rokurou

総合スコア2

保存

保存(save)とは、特定のファイルを、ハードディスク等の外部記憶装置に記録する行為を指します。

0グッド

1クリップ

投稿2020/08/02 14:55

編集2020/08/08 08:56

目的:ESP32を使ってセンサー値をSDカードに保存したい
問題:データの取得およびSDカードへ保存はできるが、SDカードに一定以上のデータが保存されなくなってしまう(例:5時間起動しても、2時間分のデータしか保存されない等)

上記の問題は「一つのファイルに保存する上限容量が決まっている」ということなのでしょうか?
一つのファイルに一定以上の容量が溜まったら新しいファイルを作成するなどで対応できるでしょうか?
一般的な対策があれば教えていただきたいと思います。


8月8日追記
多くの方からご意見を頂きましてありがとうございます。
情報不足との指摘をいただき、追記をさせていただきます。
また前回質問した問題点を「問題1」とし、「問題2」を新たに追加しました。
文末にプログラムを記載します。

使用部材
・ESP32-DevKitC http://akizukidenshi.com/catalog/g/gM-11819/
・BME280 http://akizukidenshi.com/catalog/g/gK-09421/
・MicroSDcard Adapter https://www.amazon.co.jp/HiLetgo-3%E5%80%8B%E3%82%BB%E3%83%83%E3%83%88-TF%E3%82%AB%E3%83%BC%E3%83%89%E3%83%A1%E3%83%A2%E3%83%AA%E3%82%B7%E3%83%BC%E3%83%AB%E3%83%89%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB-Arduino%E3%81%AB%E5%AF%BE%E5%BF%9C-SPI%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%ADSD%E3%82%A2%E3%83%80%E3%83%97%E3%82%BF/dp/B010GXAFFU
・MicroSDcard 4GB https://www.amazon.co.jp/ADATA-microSDHC%E3%82%AB%E3%83%BC%E3%83%89-4GB-Class4-AUSDH4GCL4-RA1/dp/B002XVX6LY

目的
1秒ごとにBME280で温度湿度気圧を取得し、NTPで取得した時刻と取得データをSDカードへCSVファイルとして保存したい。

問題1
2時間起動したが、1時間半分のデータしか保存されていない(14:55から17:00まで起動したが、14:55から16:32までしか保存されていない。また最後のみ17:00のレコードが保存されている)。なおCSVファイルは65kBであった。

問題2
1秒間隔でデータを取得しているので理屈の上では1分間に60レコードであるはず(プログラム起動のため実際にはその限りではないはずであるが)。
しかし14:56は55レコードであるが、16:31では8レコードであった。

期待する解決
・起動中は常にデータを保存したい
・時間経過によるレコード数の低下を防ぎたい

以上です。
可能な限り情報を追加したつもりですが、不足があった場合ご教示頂けますと幸いです。
よろしくお願いします。

イメージ説明

以下プログラム

#include "secrets.h" #include <WiFi.h> #include <Wire.h> #include "SparkFunBME280.h" #include <SD.h> #include <SPI.h> #define FILE_NAME "/log.csv" //SPI_setting #define SCK 5 #define MISO 16 #define MOSI 17 #define SS 18 //データ取得間隔 int delay_time = 1000 * 1; //1秒間隔 //SD_setting const uint8_t SD_CS = SS; // GPIO5=CS static File myFile; //WIFI_setting char ssid[] = SECRET_SSID; // your network SSID (name) char pass[] = SECRET_PASS; // your network password int keyIndex = 0; // your network key Index number (needed only for WEP) WiFiClient client; // BME280_setting BME280 mySensorA; //Uses default I2C address 0x77 BME280 mySensorB; //Uses I2C address 0x76 (jumper closed) float Temp; float Hum; float hPa; //SD void SD_init() { Serial.print("Initializing SD card..."); if (!SD.begin(SD_CS)) { Serial.println("initialization failed!"); return; } Serial.println("initialization done."); } //SD_read String SD_read() { String str; File file = SD.open(FILE_NAME, FILE_READ); if (file) { //---1byteずつ読み込んだ文字を結合 while (file.available()) { str += char(file.read()); } } else { Serial.println(" error..."); } //---ファイルを閉じる file.close(); return str; } void setup() { delay(1000); SPI.begin(SCK, MISO, MOSI, SS); Serial.begin(115200); //Initialize serial //NTP_setup if (WiFi.begin(SECRET_SSID, SECRET_PASS) != WL_DISCONNECTED) { ESP.restart(); } while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("wifi_disconnected..."); } configTime(9 * 3600L, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp"); //wifi_setup Wire.begin(); WiFi.mode(WIFI_STA); while (!Serial); Serial.println(); //SD_setup SD_init(); Serial.println(SD_read()); //BME280_setup mySensorA.setI2CAddress(0x76); //The default for the SparkFun Environmental Combo board is 0x77 (jumper open). if (mySensorA.beginI2C() == false) Serial.println("Sensor A connect failed"); mySensorB.setI2CAddress(0x76); //Connect to a second sensor if (mySensorB.beginI2C() == false) Serial.println("Sensor B connect failed"); } struct tm timeInfo;//時刻を格納するオブジェクト char s[20];//文字格納用 void loop() { //BME280 Temp = mySensorA.readTempC(); Hum = mySensorA.readFloatHumidity(); hPa = mySensorA.readFloatPressure() / 100; //NTP getLocalTime(&timeInfo);//tmオブジェクトのtimeInfoに現在時刻を入れ込む sprintf(s, "%04d/%02d/%02d %02d:%02d:%02d", timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday, timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec);//人間が読める形式に変換 //Serial.print Serial.println(s); Serial.print("Temp: "); Serial.print(Temp); Serial.println(" ℃"); Serial.print("Hum: "); Serial.print(Hum); Serial.println(" %"); Serial.print("Press: "); Serial.print(hPa); Serial.println(" hPa"); //SD_writting String backLog = SD_read(); myFile = SD.open(FILE_NAME, FILE_WRITE); if (myFile) { Serial.print("Writing to "); Serial.println(FILE_NAME); myFile.print(backLog); myFile.print(s); myFile.print(","); myFile.print(Temp); myFile.print(","); myFile.print(Hum); myFile.print(","); myFile.print(hPa); myFile.println(); myFile.close(); Serial.println("done."); } else { Serial.print("error opening "); Serial.println(FILE_NAME); } delay(delay_time); // Wait time }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

y_waiwai

2020/08/02 22:52

どれだけの容量で頭打ちになるんでしょうか。 また、コードを提示しましょう #具体的な情報がまったくない。。
thkana

2020/08/02 22:54

> 一定以上のデータが保存されなくなってしまう 「なってしまう」という表現は、そうではない状態もある、ということをも意味します。そうなのでしょうか。 「一定以上」と断言していますが、その後の文章で「ということなのでしょうか」と問うています。つまり一定以上なのかどうかはまだわからない、ということですね? > (例:5時間起動しても、2時間分のデータしか保存されない等) まだ何の推測も出来ていないこの段階では、情報は「等」で削ってしまわないで、できるだけ生の状態で数多く提示してください。
coco_bauer

2020/08/03 01:25

プログラムのコードを示さずに、プログラムの動作の質問をするのは、料理を食べる前に「美味しかったですか?」と質問するようなものです。 適切な答えが期待できないのは、明らかですよね?
rokurou

2020/08/08 08:58

情報不足のご指摘ありがとうございます。 プログラムと現状の問題をなるべく詳細に記載したつもりです。 ご意見を頂けたら助かります。
guest

回答1

0

ベストアンサー

8/10追記
そもそもの質問は

一般的な対策があれば教えていただきたいと思います。

でしたっけ。それに答えるならば、
起こっている現象・事実をしっかり観察し、その結果から原因を推察し、必要に応じて実験するなど推察の正当性を確認し、その原因に対してなんらかの手を打ち、効果を確認する
ということになるでしょうか。

「一つのファイルに保存する上限容量が決まっている」ということ

起こっている現象をちゃんと観察せず、誤った推測に飛びついて

一つのファイルに一定以上の容量が溜まったら新しいファイルを作成するなどで

適切でない対応を考えていたわけで。そういうことのないように。

以下元の回答


出来たファイルの容量が一定...というのは結果です。

その結果に至る原因の推測としては例えば以下が考えられるでしょう
・ファイルシステムやファイルライブラリで1ファイルのサイズが制限されている
・時間計算などのミスでプログラムの連続動作時間がある時間を超えるとで動作不良を起こす
・メモリリークなどでプログラムの内部の繰り返しがある回数を超えると動作不良を起こす
・その他(現時点ではまだなんともいえない)

もちろん、それぞれに適切な対策は異なりますし、これらのどれであるかを絞り込むだけの情報は質問では提示されておらず、従って現時点では対策は立てられません。

一つのファイルに一定以上の容量が溜まったら新しいファイルを作成するなどで対応できるでしょうか?

アマチュア的には、原因を絞り込む前にとりあえずやってみてそれで動いたならOK、という解決も駄目とは言い切れないですかね。プロなら駄目ですけど。


質問に情報が追加されたので...

ファイル書き込みと同時にシリアルにも出力しているのですね? では何故シリアルがどうなっているか見ていないのでしょう。見なかったら何のために出力したのですか?

さて。毎秒以下の情報がファイルに追加されるわけですね。

C++

1 myFile.print(s);//sは日付情報19文字 2 myFile.print(","); 3 myFile.print(Temp);//5文字ぐらい 4 myFile.print(","); 5 myFile.print(Hum);//4文字ぐらい 6 myFile.print(","); 7 myFile.print(hPa);//6文字ぐらい 8 myFile.println();//CR/LFで2文字

でおおよそ39文字、ですか。
データの追加の処理を全て自分でメモリに抱え込んでやっていますから、メモリサイズ以上のファイルは作れません。ESP32のSRAMサイズは520KB、これをフルに使えたとして520x1024/39=13653.xx秒なので、どんなに頑張っても4時間弱でロギングは不可能になり、

・起動中は常にデータを保存したい

を満たさないことが明白です。
「データの追加の処理を全て自分でメモリに抱え込んでやって」いるのが非常識なので、ファイルはアペンド(追記)モードで開いてそのまま追加データをprintしてやればいいのでは。データの「追加」はライブラリに任せましょう。

C++

1 //SD_writting 2 //String backLog = SD_read(); //削除 3 myFile = SD.open(FILE_NAME, FILE_APPEND); //追記モードで開く 4 if (myFile) { 5 Serial.print("Writing to "); 6 Serial.println(FILE_NAME); 7 //myFile.print(backLog); //削除 8 myFile.print(s); 9 myFile.print(","); 10 myFile.print(Temp); 11 myFile.print(","); 12 myFile.print(Hum); 13 myFile.print(","); 14 myFile.print(hPa); 15 myFile.println(); 16 myFile.close(); 17 Serial.println("done."); 18 }

さて。
そもそも14:56の時点で55サンプルしか取れていないというのも看過できない情報です。
SD_read()が結構重い処理なので、これを削っただけでも相当軽くはなると思いますが、2kB程度のデータを処理しただけでそんなに重くなるかなぁ...
場合によっては、データの書き込み毎にファイルをopen/closeするのをやめた方がいいかも知れません。そうすると、測定終了時にはボタンを押すなどしてファイルをクローズしてやるという操作方法になるかも知れませんが。
この辺は後で(明日か)ちょっと実験してみようかしら。


追記。
M5Stackで実験してみました。多分ライブラリは一緒だと思います。
ソースは、loop()関数を以下のように変更。

C++

1unsigned long cnt=0; 2const int delay_time=1;//待ち時間に耐えられないので変更 3void loop() { 4 unsigned long t0=millis(); 5 Serial.print(++cnt); 6 Serial.print(" "); 7 Serial.print(__LINE__);//__LINE__はソースの行数を得るマクロ。ここは110行 8 Serial.print(" "); 9 Serial.println(millis()-t0); 10 11 //BME280 デバイスがないので暫定データを与えるだけ 12 //Temp = mySensorA.readTempC(); 13 Temp=25.01; 14 //Hum = mySensorA.readFloatHumidity(); 15 Hum=50.01; 16 //hPa = mySensorA.readFloatPressure() / 100; 17 hPa=1000.01; 18 19 //NTP 20 getLocalTime(&timeInfo);//tmオブジェクトのtimeInfoに現在時刻を入れ込む 21 sprintf(s, "%04d/%02d/%02d %02d:%02d:%02d", 22 timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday, 23 timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec);//人間が読める形式に変換 24 25 //Serial.print 26 Serial.println(s); 27 Serial.print("Temp: "); 28 Serial.print(Temp); 29 Serial.println(" ℃"); 30 Serial.print("Hum: "); 31 Serial.print(Hum); 32 Serial.println(" %"); 33 Serial.print("Press: "); 34 Serial.print(hPa); 35 Serial.println(" hPa"); 36 37 //SD_writting 38 Serial.print(cnt); 39 Serial.print(" "); 40 Serial.print(__LINE__);//ここは143行 41 Serial.print(" "); 42 Serial.println(millis()-t0); 43 //String backLog = SD_read(); //変更点 44 Serial.print(cnt); 45 Serial.print(" "); 46 Serial.print(__LINE__);//ここは149行 47 Serial.print(" "); 48 Serial.println(millis()-t0); 49 myFile = SD.open(FILE_NAME, FILE_APPEND); //変更点,元はFILE_WRITE 50 Serial.print(cnt); 51 Serial.print(" "); 52 Serial.print(__LINE__);//ここは155行 53 Serial.print(" "); 54 Serial.println(millis()-t0); 55 if (myFile) { 56 Serial.print("Writing to "); 57 Serial.println(FILE_NAME); 58 //myFile.print(backLog); //変更点 59 myFile.print(s); 60 myFile.print(","); 61 myFile.print(Temp); 62 myFile.print(","); 63 myFile.print(Hum); 64 myFile.print(","); 65 myFile.print(hPa); 66 myFile.println(); 67 myFile.close(); 68 Serial.println("done."); 69 } else { 70 Serial.print("error opening "); 71 Serial.println(FILE_NAME); 72 } 73 Serial.print(cnt); 74 Serial.print(" "); 75 Serial.print(__LINE__);//ここは178行 76 Serial.print(" "); 77 Serial.println(millis()-t0); 78 delay(delay_time); // Wait time 79 Serial.print(cnt); 80 Serial.print(" "); 81 Serial.print(__LINE__);//ここは184行 82 Serial.print(" "); 83 Serial.println(millis()-t0); 84 Serial.println(); 85}

//変更点の3箇所を元のソースのままで実行した場合、シリアル出力は

Text

1(測定1回め) 21 110 0 32020/08/09 12:47:35 4Temp: 25.01 ℃ 5Hum: 50.01 % 6Press: 1000.01 hPa 71 143 20 81 149 21 91 155 22 10Writing to /log.csv 11done. 121 178 1025 131 184 1026 14(10回目) 1510 110 0 162020/08/09 12:47:36 17Temp: 25.01 ℃ 18Hum: 50.01 % 19Press: 1000.01 hPa 2010 143 0 2110 149 10 2210 155 17 23Writing to /log.csv 24done. 2510 178 29 2610 184 30 27(100回目) 28100 110 0 292020/08/09 12:47:44 30Temp: 25.01 ℃ 31Hum: 50.01 % 32Press: 1000.01 hPa 33100 143 0 34100 149 103 35100 155 110 36Writing to /log.csv 37done. 38100 178 130 39100 184 131 40(500回目) 41500 110 0 422020/08/09 12:51:01 43Temp: 25.01 ℃ 44Hum: 50.01 % 45Press: 1000.01 hPa 46500 143 0 47500 149 903 48500 155 911 49Writing to /log.csv 50done. 51500 178 963 52500 184 964 53(1000回目) 541000 110 0 552020/08/09 13:06:18 56Temp: 25.01 ℃ 57Hum: 50.01 % 58Press: 1000.01 hPa 591000 143 0 601000 149 2802 611000 155 2810 62Writing to /log.csv 63done. 641000 178 2876 651000 184 2877

となり、読み込み/書き込みの時間がどんどん増えていっていることがわかります。特にSD_read()関数ですね。1000回測定した時点で、1回の測定で3秒弱かかるようになってしまいました。

//変更点のところをコメントアウト、FILE_APPENDに変更した場合は

Text

1(1回目) 21 110 0 32020/08/09 13:21:48 4Temp: 25.01 ℃ 5Hum: 50.01 % 6Press: 1000.01 hPa 71 143 1 81 149 1 91 155 1 10Writing to /log.csv 11done. 121 178 29 131 184 30 14(100回目) 15100 110 0 162020/08/09 13:21:49 17Temp: 25.01 ℃ 18Hum: 50.01 % 19Press: 1000.01 hPa 20100 143 5 21100 149 6 22100 155 8 23Writing to /log.csv 24done. 25100 178 16 26100 184 17 27(1000回目) 281000 110 0 292020/08/09 13:22:02 30Temp: 25.01 ℃ 31Hum: 50.01 % 32Press: 1000.01 hPa 331000 143 5 341000 149 6 351000 155 9 36Writing to /log.csv 37done. 381000 178 17 391000 184 18 40(10000回目) 4110000 110 0 422020/08/09 13:24:21 43Temp: 25.01 ℃ 44Hum: 50.01 % 45Press: 1000.01 hPa 4610000 143 5 4710000 149 6 4810000 155 9 49Writing to /log.csv 50done. 5110000 178 15 5210000 184 16 53(20363回目) 5420363 110 0 552020/08/09 13:27:07 56Temp: 25.01 ℃ 57Hum: 50.01 % 58Press: 1000.01 hPa 5920363 143 5 6020363 149 6 6120363 155 9 62Writing to /log.csv 63done. 6420363 178 15 6520363 184 16 66(20364回目) 6720364 110 0 682020/08/09 13:27:08 69Temp: 25.01 ℃ 70Hum: 50.01 % 71Press: 1000.01 hPa 7220364 143 5 7320364 149 6 7420364 155 9 75Writing to /log.csv 76done. 7720364 178 15 7820364 184 15

となり、実行時間には大きな変化はありません。この方法であれば、安定して測定が続けられるものと考えられます。

このとき、SDカードに生成したCSVファイルについて

CSV

1(20364行目)2020/08/09 13:27:07,25.01,50.01,1000.01 2(20365行目)2020/08/09 13:27:08,25.01,50.01,1000.01

となっており、少なくともこの範囲ではファイルの書き込みが正常に行えていることを確認しました。

なお、正常動作としても一回の測定値の処理に10~30msかかるわけで、単純にdelayを1000ms入れて「1秒」としていると、100回も測定すれば1回分データの個数がずれてくることになります。それで構わないのならそのままでもよいですが、きっちり1秒を期待するなら、せっかくntpでとっている時刻を使って、「秒」の数字が変化したら測定/記録する、という作りにした方がよろしいかと思います。

投稿2020/08/02 23:05

編集2020/08/10 02:32
thkana

総合スコア7639

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rokurou

2020/08/10 04:15

SDカードへの記録をAPPENDモードで追記することで解決しました。 またloopのdelay制御をNTPで行う手法を教えていただきありがとうございます。 これらにより期待する挙動をさせることができました。 色々と足りないところを丁寧にご指摘いただきありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問