2018年5月9日水曜日

空気質+CO2センサー CCS811

空気質センサーとOLEDをESP32に接続し、Ambientで可視化しました。

Strawberry LinuxさんのCCS811モジュールとAdafruitさんのライブラリで何事もなく動作します。Adafruitさん、今度買うからね(AdaとSparkfunさんには購入ノルマを設定して時々お布施しています)。

ただ、ハマりポイントがいくつか。
  1. 電源、I2Cの他にReset(VDDへ)とWake(GNDへ)も接続しよう
  2. 基板裏のアドレス選択ジャンパをハンダ付けしよう(0 : 0x5A)
  3. I2CはGPIO21(SDA)とGPIO22(SCL)
これでサンプルコードは動きます。

さらに、OLEDに値を表示するようにしてみました。OLEDライブラリもCCS811ライブラリも初期化でWire.begin()呼んだりするので、初期化の順番が違うとCCS811がヘソを曲げます。この複数のライブラリが勝手にWire.begin呼び出す問題、なんとかならんですかね。どうせWireはオブジェクト一個なので、ライブラリを使うユーザが自主的に初期化すべし、の方がありがたいのですが…初心者保護でしょうがないのかなぁ…。

で、とりあえずCCS811から読んだ値をOLEDに表示できるようになった…と思ったら、途中で動作が不安定に。CCS811から値が返らなくなり、電源を切るかCCS811のRESET端子を一度GNDに落とさないと動いてくれません。I2Cの処理が干渉しているようで、それぞれの通信後にdelayを入れたら安定して動作するようになりました。あるある。

ついでにAmbientにデータを上げるようにしました。Ambientホント楽。

化学物質過敏症で困っている方に貸し出してしまったので写真はありません。いやー、歩きながらディスプレイ見ていると、時々すっごい値が出てきますね。ちょっと窓を閉めているとあっという間に1000ppmに達します。近くに手を置いただけで600ppmになったり。人間がどれだけ二酸化炭素を噴出しているかがわかります。

TVOCの最高値はとある喫煙室で、6000ppbを記録しました。おおおおお。

ってことで、ソースです。


//
// Sensor
#include "Adafruit_CCS811.h"
Adafruit_CCS811 ccs;
//
// WiFi
#include <WiFi.h>
#include <WiFiClient.h>
#include <Ambient.h>
char *ssid;
const char *ssid1 = "<ssid1>";
const char *pass1 = "<pass1>";
const char *ssid2 = "<ssid2>";
const char *pass2 = "<pass2>";
WiFiClient client;
Ambient ambient;
unsigned int channelId = <ch number>;
const char* writeKey = "<ambient write api key>";
void connect() {
Serial.print("WiFi Connectiong");
ssid = (char *)ssid1;
WiFi.begin(ssid, pass1);
int cnt = 0;
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500); // 0.5秒
if (++cnt == 10) {
WiFi.disconnect();
Serial.print("|");
delay(500);
ssid = (char *)ssid2;
WiFi.begin(ssid, pass2);
}
if (cnt > 20) {
break;
}
}
Serial.println("");
}
//
// 時間
#include <Time.h>
#include <TimeLib.h>
//
// OLED
//
#include "SSD1306.h"
SSD1306 display(0x3c, 21, 22);
//
// Setup
//
void setup() {
//
// Serial
Serial.begin(115200);
//
// OLED
display.init();
display.flipScreenVertically();
display.displayOn();
display.clear();
display.setFont(ArialMT_Plain_16);
display.setTextAlignment(TEXT_ALIGN_RIGHT);
display.drawString(0, 32, "Hello");
display.display();
delay(100);
//
// CCS881
//
int ccsReady = true;
// 設定パラメータをセット
if (!ccs.begin()) {
Serial.println("Failed to start sensor! Please check your wiring.");
ccsReady = false;
}
//
// WiFi
//
// 接続&接続確認
connect();
ambient.begin(channelId, writeKey, &client);
//
// show status
showStatus(ccsReady);
display.display();
}
//
// メインループ
//
int prevMinute = minute();
void loop() {
int ccsReady = false;
if (ccs.available()) {
float temp = ccs.calculateTemperature();
if (!ccs.readData()) {
ccsReady = true;
//
// 文字列へ変換
char tBuf[10], cBuf[10], vBuf[10];
dtostrf(temp, 7, 2, tBuf);
dtostrf(ccs.geteCO2(), 7, 2, cBuf);
dtostrf(ccs.getTVOC(), 7, 2, vBuf);
// シリアルに出力
Serial.print("Temperature: ");
Serial.print(tBuf);
Serial.println(" degrees C");
Serial.print(" CO2: ");
Serial.print(cBuf);
Serial.println(" ppm");
Serial.print(" TVOC: ");
Serial.print(vBuf);
Serial.println(" ppb");
Serial.println();
//
// OLEDへ表示
delay(10);
display.clear();
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.drawString(16, 16, "Sen temp");
display.drawString(16, 32, "Value1");
display.drawString(16, 48, "Value2");
display.setFont(ArialMT_Plain_16);
display.setTextAlignment(TEXT_ALIGN_RIGHT);
display.drawString(127, 16, tBuf);
display.drawString(127, 32, cBuf);
display.drawString(127, 48, vBuf);
// 1分ごとにThingSpeakへ送出
if (prevMinute != minute()) {
if (WiFi.status() == WL_CONNECTED) {
ambient.set(1, tBuf);
ambient.set(2, cBuf);
ambient.set(3, vBuf);
ambient.send();
} else {
connect();
}
}
} else {
Serial.println("ccs read fail");
}
showStatus(ccsReady);
display.display();
} else {
Serial.println("css not ready");
}
delay(200);
}
void showStatus(int inCssReady) {
char buf[20];
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_LEFT);
sprintf(buf, "%s:%s", ssid, WiFi.status() == WL_CONNECTED ? "OK" : "NG");
display.drawString( 0, 0, buf);
display.setTextAlignment(TEXT_ALIGN_RIGHT);
sprintf(buf, "CSS:%s", inCssReady == true ? "OK" : "NG");
display.drawString(127, 0, buf);
}

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。