diy/esp8266

voc센서(GSBT11-P110, 공기질 측정기) 만들기 2

do121 2022. 9. 28. 08:25
/*
   5v - vin
   scl - d1
   sda - d2
   
   VOCs Sensor(GSBT11-p11)
    vcc - 5v
    vvolt-a0
*/

// includes
#include "ESP8266WiFi.h"
#include "WiFiClientSecure.h"
#include <ArduinoOTA.h>

#include "SSD1306Wire.h"        // oled 라이브러리,  아두이노 우노 사용시는 #include "SSD1306.h"

#define DEBUG true

#include <Wire.h>
#include "SparkFunBME280.h"
BME280 mySensor;

//SSD1306  display(0x3c, 4, 5);//0x3C being the usual address of the OLED sda d2-4, sck d1-5
SSD1306Wire display(0x3c, SDA, SCL);   // ADDRESS, SDA, SCL  -  SDA and SCL usually populate automatically based on //your board's pins_arduino.h e.g. https://github.com/esp8266/Arduino/blob/master/variants/nodemcu/pins_arduino.h

// user config:
const char* wifi_ssid = "";             // replace MySSID with your WiFi network name
const char* wifi_password = "";         // replace MyPassword with your WiFi password

// thingspeak config.
//자신의 thingspeak 채널의 Read API key 입력
String apiKey = "";
String response1 = "";
String ip = "";
String cmd = "";

const char* https_host = "api.thingspeak.com";         // thingspeak host name
const int https_port = 443;                        // https port

// create thingspeak client
WiFiClientSecure client;



//oled update interval
unsigned long starttime_lcd = 0;
unsigned long sampletime_lcd = 3000; // 30초 TIME BETWEEN MEASURES AND UPDATES

//temp, humid update interval
int err;
float temp, humi, hpa, pa;
unsigned long starttime_temp = 0;
unsigned long sampletime_temp = 60000; // 1분 TIME BETWEEN MEASURES AND UPDATES



// thingspeak update interval
unsigned long starttime_bl = 0;
unsigned long sampletime_bl = 600000; // 10분 600,000 TIME BETWEEN MEASURES AND UPDATES


//voc
int data;
float voc;
unsigned long starttime_voc = 0;
unsigned long sampletime_voc = 600000; // 10분 GSBT11-P110 TIME BETWEEN MEASURES AND UPDATES

#define DEBUG

#ifdef DEBUG 
#define Serial_p(x)  Serial.println(x)
#define Serial_p2(x,y)  Serial.println(x,y)
#define Serial_b(x)  Serial.begin(x)
#define Serial_f(x,y)  Serial.printf (x,y)
#else
#define Serial_p(x)
#define Serial_f(x)
#define Serial_b(x)
#define Serial_p2(x)
#endif

void setup() {

  Serial.begin(115200);
  Serial.println("init....");

  // Initialising the UI will init the display too.
  display.init();

  display.flipScreenVertically();
  display.setFont(ArialMT_Plain_16);

  dis_oled2("init ... ", "", "", "", "");

  delay(100);

  //sparkfun bme280
  Wire.begin();
  mySensor.setI2CAddress(0x76); //Connect to a second sensor
  if (mySensor.beginI2C() == false) //Begin communication over I2C
  {
    Serial.println("The sensor did not respond. Please check wiring.");
    dis_oled2("BME280 ERROR!", "Failed to start", "Check the wiring", "", "");

    while (1); //Freeze
  }
  else
  {
    dis_oled2("BME280 OK~", "", "", "", "");

  }

  Serial.println("connecting wifi...");
  dis_oled2("wifi connecting ...", "", "", "", "");

  WiFi.begin(wifi_ssid, wifi_password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }


  Serial.println("wifi connected~");

  // Set Client to insecure
  client.setInsecure();

  ip = WiFi.localIP().toString();
  Serial.println("ip:" + ip);
  dis_oled2(ip, "", "", "", "");

  ArduinoOTA.setHostname("voc"); // ota 이름 설정
  ArduinoOTA.setPassword("1234"); //ota 접속 비번 설정

  ArduinoOTA.onStart([]() {
    Serial_p("Start");
  });
  ArduinoOTA.onEnd([]() {
    Serial_p("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial_f("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial_f("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial_p("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial_p("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial_p("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial_p("Receive Failed");
    else if (error == OTA_END_ERROR) Serial_p("End Failed");
  });
  ArduinoOTA.begin();
  Serial_p("OTA ready");

}

void loop() {

  if ((millis() - starttime_temp) > sampletime_temp) {


    //sparkfun bme280
    Serial.print("Humidity: ");
    humi = mySensor.readFloatHumidity();
    Serial.print(humi, 0);

    Serial.print(" Pressure: ");
    pa = mySensor.readFloatPressure();
    hpa = pa / 100;
    Serial.print(pa, 0);

    Serial.print(" Alt: ");
    Serial.print(mySensor.readFloatAltitudeMeters(), 1);
    //Serial.print(mySensor.readFloatAltitudeFeet(), 1);

    Serial.print(" Temp: ");
    temp = mySensor.readTempC() - 2; //slightly higher than dht22 about 2 degree
    Serial.print(temp, 2);
    //Serial.print(mySensor.readTempF(), 2);

    Serial.println();


    starttime_temp = millis();
  }


  if ((millis() - starttime_voc) > sampletime_voc) {
    data = analogRead(A0);      // 실제로 측정된 ppm에 따른 ADC값
    Serial.println(data);
    voc = (float(data) * 5) / 1024; //analogRead 코드로 읽히는 A0 값의 범위는 0 ~ 3.3V(5v) 전압에 대해 nodemcu 기기 

                                                   //내부의 Analog-Digtal-Converter에 의해 0 ~ 1023 범위로 변환된 값
   
    Serial.println(voc );

    starttime_voc = millis();
  }



  if ((millis() - starttime_lcd) > sampletime_lcd) {
    
    dis_oled2("tmp:" + String(temp), "hum:" + String(humi), "voc:" + String(voc), "hPa:" + String(hpa), ip);
    starttime_lcd = millis();



  }

  if ((millis() - starttime_bl) > sampletime_bl) {


    // GET 방식으로 보내기 위한 String, Data 설정
    String getStr = "";
    getStr += "&field1=";
    getStr += String(temp);
    getStr += "&field2=";
    getStr += String(humi);
    getStr += "&field3=";
    getStr += String(voc);
    getStr += "&field6=";
    getStr += String(hpa, 1);

    // Send Data
    send_data(getStr);
    Serial.println(getStr);

    starttime_bl = millis();
  }

}
void dis_oled2(String msg1, String msg2, String msg3, String msg4, String msg5) {
  display.clear();

  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.setFont(ArialMT_Plain_16);


  int idx[] = {0, 13, 26, 39, 52}; 

  String str[5];

  str[0] = msg1;

  str[1] = msg2;

  str[2] = msg3;

  str[3] = msg4;

  str[4] = msg5;

  for (int i = 0; i < 5; i++) {

    display.drawString(0, idx[i], str[i]);//

  }

  display.display();
}


void send_data(String msg)
{

  if (!client.connect(https_host, https_port)) {
    delay(200);
        Serial.println("client connection error !");
    return;
  }
  Serial.println("client connection success !");
  // Create a URL for the request
  String url = "/update?api_key=";
  url += apiKey;
  url += msg;


  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + https_host + "\r\n" +
               "Connection: close\r\n\r\n");

  client.connected();
}

GSBT11-P110의 datasheet로 볼 때 1볼트 미만으로 관리하는 게 톨루엔과 포름알데히드의 영향을 최소화 가능할 듯함



관리기준은 아래 국토부 신규 제작자동차 실내공기질 관리기준 자료 참고
http://www.molit.go.kr/USR/policyTarget/dtl.jsp?idx=518

정책Q&A

  관리대상 물질의 농도에 따라 여러 가지 증상들로 나타날 수 있으나 일반적인 특성 및 영향은 다음과 같습니다. 물질명,농도(㎍/㎥),특성 및 영향 물질명 농도(㎍/㎥) 특성 및 영향 폼알데하이

www.molit.go.kr