補正パラメータであるBASELINEレジスタですが、CCS811では電源を切ったら忘れてしまいます。デバッグのために何度か電源OFF/ONを繰り返していると、だんだん収束までの時間は短くなっているようですが、それでも忘れてしまうことには変わりありません。
まあ、連続通電すればいいか、とも思ったのですが、やはり気になって保存するように修正してみました。
まず、BASELINEレジスタはAdafruitのライブラリでは読み書きできませんので、読み書きできるように修正します。Adafruit_CCS811.h のクラス宣言にメンバ関数とprivate変数を追加します。
class Adafruit_CCS811 { public: (途中略) // check if data is available to be read bool available(); uint8_t readData(); uint16_t readBaseline(); // 2020/9/21追加 void writeBaseline(uint16_t baseline); // 2020/9/21追加 bool checkError(); (途中略) private: (途中略) uint16_t _TVOC; uint16_t _eCO2; uint16_t _BASELINE; // 2020/9/21追加 (以下略)
次に、Adafruit_CCS811.cpp にメンバ関数の記述を追加します。
/* * BASELINEデータの読み出し/書き込み */ uint16_t Adafruit_CCS811::readBaseline() { uint8_t buf[2]; this->read(CCS811_BASELINE, buf, 2); _BASELINE = ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]); return _BASELINE; } void Adafruit_CCS811::writeBaseline(uint16_t baseline) { uint8_t buf[] = {(uint8_t)(baseline >> 8),(uint8_t)(baseline & 0xff)}; this->write(CCS811_BASELINE, buf, 2); }
BASELINEレジスタは16ビットなので、uint16_tで受け渡しします。
次に、本体のコードの方を修正します。こちらの記事を参考にさせていただいて、内蔵FLASHを擬似EEPROMとして扱うコードを追加しました。
#include <EEPROM.h> #define EEDATA_VERSION "TVOC1.0" struct EEDATA_SET{ uint16_t baseline; uint32_t pcount; char check[10]; }; EEDATA_SET eedata; time_t pontime = 0; time_t savetime = 0; bool restored = false; //データをEEPROMから読み込む。保存データが無い場合デフォルトにする。 void load_data() { EEPROM.get<EEDATA_SET>(0, eedata); if (strcmp(eedata.check, EEDATA_VERSION)) { //バージョンをチェック //保存データが無い場合デフォルトを設定 eedata.baseline = 0xFFFF; eedata.pcount = 0; Serial.println("EEPROM data initialized."); } } //EEPROMへの保存 void save_data() { //EEPROMに設定を保存する。 strcpy(eedata.check, EEDATA_VERSION); EEPROM.put<EEDATA_SET>(0, eedata); EEPROM.commit(); Serial.println("EEPROM commited."); } void eeprom_setup() { EEPROM.begin(1024); //1kbサイズ load_data(); Serial.print("EEPROM Baseline : "); Serial.println(eedata.baseline,HEX); Serial.print("EEPROM Count : "); Serial.println(eedata.pcount); restored = false; // save_data(); } void eeprom_loop() { // もともと1分間に1回で呼ばれる前提 time_t t = time(NULL); struct tm *tm = localtime(&t); if(tm->tm_year+1900 > 2000){ // 2000年以降=NTPで時刻が取れている場合のみ実行 if(pontime == 0){ pontime = t; // 電源投入時刻として保存 savetime = pontime + 24*60*60; // 24時間後を保存時刻とする } if((restored != true) && (t - pontime > 30*60)){ // 値がリストアされていない&30分経過 Serial.println("30minutes passed from power on."); if(eedata.pcount > 0){ Serial.println("Baseline data was restored from EEPROM."); ccs811.writeBaseline(eedata.baseline); } else { Serial.println("There was no data in EEPROM to restore to baseline."); } restored = true; } if((savetime != 0) && (t > savetime)){ Serial.println("Time to store baseline to EEPROM."); eedata.baseline = ccs811baseline; eedata.pcount++; save_data(); Serial.println("Baseline data saved to EEPROM."); if( t - pontime > 500*60*60 ){ savetime = t + 5*24*60*60; // 電源投入から500時間経過している場合には5日に1回 } else { savetime = t + 24*60*60; // 電源投入から500時間経過していない場合には1日に1回 } } } }
これをsetup()とloop()の適当なところから呼び出しています。ちゃんとしたデバッグには、何週間もかかると思います。(値を変えてそれなりには試していますが)
さて、どうなるでしょうか。