CCS811で空気品質測定(3)

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

さて、どうなるでしょうか。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)