StrawberryLinux CCS811 と ESP32 で CO2 を測る

# 環境

- Arduino 1.8.16 for Linux
- esp32 by Espressif System Version 2.0.1
- WEMOS LOLIN32
- StrawberryLinux エアークオリティセンサモジュール CCS811
https://strawberry-linux.com/catalog/items?code=11811

# コード

```
#include <Wire.h>
#define ADDRESS 0x5B

void setup()
{
    int data;
    Serial.begin(115200);
    delay(1000);
    Serial.println("CCS811 CO2 SENSOR");
    Wire.begin();
    Wire.beginTransmission(ADDRESS);
    Wire.write(0x20);       // HW_ID GET
    Wire.endTransmission();
    Wire.requestFrom(ADDRESS,1);
    data = Wire.read();
    Serial.println(data,HEX);   // may be 0x81(HW_ID)
    Wire.beginTransmission(ADDRESS);
    Wire.write(0x21);       // HW_Version GET
    Wire.endTransmission();
    Wire.requestFrom(ADDRESS,1);
    data = Wire.read();
    Serial.println(data,HEX);   // may be 0x12(HW_Version)
    Wire.beginTransmission(ADDRESS);
    Wire.write(0xF4);       // APP_START command
    Wire.endTransmission();
    Wire.beginTransmission(ADDRESS);
    Wire.write(0x01);       // 0x01 register(MEAS_MODE)
    delay(30);  // 重要 ?
    Wire.write(0x10);       // Constant power mode, IAQ measurement every second)
    delay(30);  // 重要 ?
    Wire.endTransmission();
    delay(30);  // 重要 ?
}
void loop()
{
    static int counter=0;
    Serial.print(counter++);
    readData();
    delay(1000);
}
void readData()
{
    uint8_t data[32];
    int i=0;
    Wire.beginTransmission(ADDRESS);
    Wire.write(0x02);
    Wire.endTransmission();
    Wire.requestFrom(ADDRESS,8);
    while(Wire.available()){
        data[i] = Wire.read();
        i++;
    }
    int co2=data[0]*256+data[1];
    Serial.print(" co2:");
    Serial.print(co2);
    int tvoc=data[2]*256+data[3];
    Serial.print(" TVOC:");
    Serial.print(tvoc);
    Serial.print(" STATUS:");
    Serial.print(data[4],BIN);
    Serial.print(" ERROR_ID:");
    Serial.print(data[5]);
    Serial.print(" RAW_DATA:");
    Serial.print(data[6],HEX);
    Serial.print(" ");
    Serial.print(data[7],HEX);
    Serial.print(" (");
    for ( i = 0 ; i < 8 ; i++){
        Serial.print(" ");
        Serial.print(data[i],HEX);
    }
    Serial.println(" )");
}

```

# 実行結果

```

CCS811 CO2 SENSOR
81
12
0 co2:0 TVOC:0 STATUS:0 ERROR_ID:0 RAW_DATA:0 0 ( 0 0 0 0 0 0 0 0)
1 co2:0 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 8F ( 0 0 0 0 98 0 25 8F)
2 co2:0 TVOC:0 STATUS:10010000 ERROR_ID:0 RAW_DATA:25 8C ( 0 0 0 0 90 0 25 8C)
3 co2:0 TVOC:0 STATUS:10010000 ERROR_ID:0 RAW_DATA:29 B6 ( 0 0 0 0 90 0 29 B6)
4 co2:400 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 8D ( 1 90 0 0 98 0 25 8D)
5 co2:400 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 91 ( 1 90 0 0 98 0 25 91)
6 co2:405 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 90 ( 1 95 0 0 98 0 25 90)
7 co2:446 TVOC:7 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 87 ( 1 BE 0 7 98 0 25 87)
8 co2:436 TVOC:5 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 89 ( 1 B4 0 5 98 0 25 89)
9 co2:433 TVOC:5 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 8A ( 1 B1 0 5 98 0 25 8A)
10 co2:425 TVOC:3 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 8B ( 1 A9 0 3 98 0 25 8B)
```
しばらくすると落ち着きます

```
102 co2:403 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 96 ( 1 93 0 0 98 0 25 96)
103 co2:400 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 97 ( 1 90 0 0 98 0 25 97)
104 co2:403 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 96 ( 1 93 0 0 98 0 25 96)
105 co2:400 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 97 ( 1 90 0 0 98 0 25 97)
106 co2:403 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 96 ( 1 93 0 0 98 0 25 96)
107 co2:408 TVOC:1 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 95 ( 1 98 0 1 98 0 25 95)
108 co2:408 TVOC:1 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 95 ( 1 98 0 1 98 0 25 95)
109 co2:403 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 96 ( 1 93 0 0 98 0 25 96)
110 co2:400 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 97 ( 1 90 0 0 98 0 25 97)
111 co2:400 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 97 ( 1 90 0 0 98 0 25 97)
112 co2:403 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 96 ( 1 93 0 0 98 0 25 96)
113 co2:403 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 96 ( 1 93 0 0 98 0 25 96)
114 co2:403 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 96 ( 1 93 0 0 98 0 25 96)
115 co2:408 TVOC:1 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 95 ( 1 98 0 1 98 0 25 95)
116 co2:408 TVOC:1 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 95 ( 1 98 0 1 98 0 25 95)
```


息を吹きかけると反応していることがわかります。


```

129 co2:400 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 97 ( 1 90 0 0 98 0 25 97)
130 co2:403 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 96 ( 1 93 0 0 98 0 25 96)
131 co2:415 TVOC:2 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 93 ( 1 9F 0 2 98 0 25 93)
132 co2:1882 TVOC:556 STATUS:10011000 ERROR_ID:0 RAW_DATA:24 ED ( 7 5A 2 2C 98 0 24 ED)
133 co2:1798 TVOC:446 STATUS:10011000 ERROR_ID:0 RAW_DATA:24 F8 ( 7 6 1 BE 98 0 24 F8)
134 co2:1515 TVOC:181 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 27 ( 5 EB 0 B5 98 0 25 27)
135 co2:915 TVOC:78 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 4F ( 3 93 0 4E 98 0 25 4F)
136 co2:905 TVOC:76 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 50 ( 3 89 0 4C 98 0 25 50)
137 co2:742 TVOC:52 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 60 ( 2 E6 0 34 98 0 25 60)
138 co2:659 TVOC:39 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 69 ( 2 93 0 27 98 0 25 69)
139 co2:600 TVOC:30 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 72 ( 2 58 0 1E 98 0 25 72)
140 co2:537 TVOC:20 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 7C ( 2 19 0 14 98 0 25 7C)
141 co2:526 TVOC:19 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 7E ( 2 E 0 13 98 0 25 7E)
142 co2:537 TVOC:20 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 7C ( 2 19 0 14 98 0 25 7C)
143 co2:537 TVOC:20 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 7C ( 2 19 0 14 98 0 25 7C)
144 co2:531 TVOC:19 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 7D ( 2 13 0 13 98 0 25 7D)
145 co2:526 TVOC:19 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 7E ( 2 E 0 13 98 0 25 7E)
146 co2:531 TVOC:19 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 7D ( 2 13 0 13 98 0 25 7D)
147 co2:543 TVOC:21 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 7B ( 2 1F 0 15 98 0 25 7B)
148 co2:531 TVOC:19 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 7D ( 2 13 0 13 98 0 25 7D)
149 co2:493 TVOC:14 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 83 ( 1 ED 0 E 98 0 25 83)

```

# トラブルシュート


## I2C 応答が FDばかりになる


しばらく動かすと、データが FD ばかり帰ってくるようになりました。


```

330 co2:482 TVOC:12 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 93 ( 1 E2 0 C 98 0 25 93)
331 co2:482 TVOC:12 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 93 ( 1 E2 0 C 98 0 25 93)
332 co2:482 TVOC:12 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 93 ( 1 E2 0 C 98 0 25 93)
333 co2:482 TVOC:12 STATUS:10011000 ERROR_ID:0 RAW_DATA:25 93 ( 1 E2 0 C 98 0 25 93)
334 co2:65021 TVOC:65021 STATUS:11111101 ERROR_ID:253 RAW_DATA:FD FD ( FD FD FD FD FD FD FD FD)
335 co2:20225 TVOC:0 STATUS:1010 ERROR_ID:0 RAW_DATA:0 0 ( 4F 1 0 0 A 0 0 0)
336 co2:20481 TVOC:0 STATUS:1010 ERROR_ID:0 RAW_DATA:0 0 ( 50 1 0 0 A 0 0 0)
337 co2:65021 TVOC:65021 STATUS:11111101 ERROR_ID:253 RAW_DATA:FD FD ( FD FD FD FD FD FD FD FD)
338 co2:20993 TVOC:0 STATUS:1010 ERROR_ID:0 RAW_DATA:0 0 ( 52 1 0 0 A 0 0 0)
339 co2:21249 TVOC:0 STATUS:1010 ERROR_ID:0 RAW_DATA:0 0 ( 53 1 0 0 A 0 0 0)
340 co2:21505 TVOC:0 STATUS:1010 ERROR_ID:0 RAW_DATA:0 0 ( 54 1 0 0 A 0 0 0)
341 co2:65021 TVOC:65021 STATUS:11111101 ERROR_ID:253 RAW_DATA:FD FD ( FD FD FD FD FD FD FD FD)
342 co2:65021 TVOC:65021 STATUS:11111101 ERROR_ID:253 RAW_DATA:FD FD ( FD FD FD FD FD FD FD FD)
343 co2:65021 TVOC:65021 STATUS:11111101 ERROR_ID:253 RAW_DATA:FD FD ( FD FD FD FD FD FD FD FD)
344 co2:65021 TVOC:65021 STATUS:11111101 ERROR_ID:253 RAW_DATA:FD FD ( FD FD FD FD FD FD FD FD)
345 co2:65021 TVOC:65021 STATUS:11111101 ERROR_ID:253 RAW_DATA:FD FD ( FD FD FD FD FD FD FD FD)
346 co2:65021 TVOC:65021 STATUS:11111101 ERROR_ID:253 RAW_DATA:FD FD ( FD FD FD FD FD FD FD FD)
347 co2:65021 TVOC:65021 STATUS:11111101 ERROR_ID:253 RAW_DATA:FD FD ( FD FD FD FD FD FD FD FD)
348 co2:65021 TVOC:65021 STATUS:11111101 ERROR_ID:253 RAW_DATA:FD FD ( FD FD FD FD FD FD FD FD)
```

これは、ソースコード中の `delay(30)` を当初入れていないときに起こりました。以下のように入れると解消しました。

``` 
    Wire.beginTransmission(ADDRESS);
    Wire.write(0x01);       // 0x01 register(MEAS_MODE)
    delay(30);  // 重要 ?
    Wire.write(0x10);       // Constant power mode, IAQ measurement every second)
    delay(30);  // 重要 ?
    Wire.endTransmission();
    delay(30);  // 重要 ?


```

また、一度このようになると、エラーリセットしないと正常動作に復帰しませんでした。エラーは以下でリセットされます


- 電源投入時リセットを発行する
- nRESET 信号をパルス駆動する
- I2C で SW_RESET (メールボックスID 0xFF )にリセットシーケンスを書き込む
- I2C で ERROR_ID (メールボックスID 0xE0 )を読む

## CO2値にスパイクが発生する

時々、謎の値が出ています

```

191 co2:405 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:21 BC ( 1 95 0 0 98 0 21 BC)
192 co2:400 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:21 BD ( 1 90 0 0 98 0 21 BD)
193 co2:400 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:21 BD ( 1 90 0 0 98 0 21 BD)
194 co2:49664 TVOC:0 STATUS:1010 ERROR_ID:0 RAW_DATA:0 0 ( C2 0 0 0 A 0 0 0)
195 co2:413 TVOC:1 STATUS:10011000 ERROR_ID:0 RAW_DATA:21 BA ( 1 9D 0 1 98 0 21 BA)
196 co2:405 TVOC:0 STATUS:10011000 ERROR_ID:0 RAW_DATA:21 BC ( 1 95 0 0 98 0 21 BC)
197 co2:415 TVOC:2 STATUS:10011000 ERROR_ID:0 RAW_DATA:21 B9 ( 1 9F 0 2 98 0 21 B9)
198 co2:415 TVOC:2 STATUS:10011000 ERROR_ID:0 RAW_DATA:21 B9 ( 1 9F 0 2 98 0 21 B9)
384 co2:427 TVOC:4 STATUS:10011000 ERROR_ID:0 RAW_DATA:21 B5 ( 1 AB 0 4 98 0 21 B5)
385 co2:424 TVOC:3 STATUS:10011000 ERROR_ID:0 RAW_DATA:21 B6 ( 1 A8 0 3 98 0 21 B6)
386 co2:33281 TVOC:0 STATUS:1010 ERROR_ID:0 RAW_DATA:0 0 ( 82 1 0 0 A 0 0 0)
387 co2:432 TVOC:4 STATUS:10011000 ERROR_ID:0 RAW_DATA:21 B4 ( 1 B0 0 4 98 0 21 B4)

```

CO2にスパイクが発生、TVOCは0で STATUS が 0b00001010 であることは共通しているようです。


STATUS についてデータシートを見るとこの状況では FW_MODEが boot mode となっていることがわかります。
以下に、データシートの内容を転記しました。

|bit|名前|0の場合|1の場合|
|:---:|:---:|:---:|:---:|
|7|FW_MODE|Firmware is in boot mode, this allows new firmware to be loaded|Firmware is in application mode. CCS811 is ready to take ADC measrements|
|6|(Reserved)|
|5|(Reserved)|
|4|APP_VALID|No application firmware loaded|Valid application firmware loaded|
|3|DATA_READY|No new data samples are ready|A new data sample is ready in ALG_RESULT_DATA,this bit is cleared when ALG_RESULT_DATA is read on the I2C interface|
|2|(Reserved)|||
|1|(Reserved)|||
|0|ERROR|No error has occured|There is an error on the I2C or sensor, the ERROR_ID register (0xE0) contains the error source |

なので、STATUSを見てbit7が0のときのデータを使わないようにするのが良さそうです。