補正パラメータである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()の適当なところから呼び出しています。ちゃんとしたデバッグには、何週間もかかると思います。(値を変えてそれなりには試していますが)
さて、どうなるでしょうか。