파나소닉 SN-GCJA5 공기질 (PM) 센서를 사용하는 Machinechat

요약

이 프로젝트는 파나소닉의 SN-GCJA5 센서로부터 PM2.5 및 PM10 미세 먼지 질량 밀도를 읽을 수 있도록 아두이노 MKR WiFi 1010 보드를 설정하고 , AQI(Air Quality Index, 공기질 지수) 값을 계산한 후 와이파이를 사용하여 AQI 데이터를 Machinechat의 JEDI One IoT 데이터 수집기에 HTTP POST합니다. JEDI One은 라즈베리 파이 4로 구동됩니다.

image

image

하드웨어

소프트웨어

  • JEDI One
    JEDI One은 바로 사용할 수 있는 IoT 데이터 관리 소프트웨어 솔루션입니다. 기능에는 센서, 장치 및 기계로부터 데이터 수집, 직관적인 실시간 및 과거 데이터와 시스템 뷰 대시보드 구축, 데이터를 모니터링하여 자동으로 데이터 조건에 응답하는 규칙 생성, 이메일과 SMS로 경고 알림 수신 등이 있습니다.
  • 아두이노
    아두이노는 사용하기 쉬운 하드웨어와 소프트웨어에 기반한 오픈 소스 전자 장치 플랫폼입니다.

배경

미국 EPA(Environmental Protection Agency, 환경보호청)에 따르면, “미세먼지는 미세한 고체 또는 액체 입자를 포함하고 있으며, 이는 흡입될 수 있어서 심각한 건강 문제를 야기할 수 있습니다. 직경이 수 마이크로미터 이하인 입자는 폐 깊숙이 침투할 수 있으며 일부는 혈류에까지 침투할 수 있습니다. 이중 직경이 2.5마이크로미터 이하인 입자들은 미세입자 또는 PM2.5라고도 부르며, 건강에 가장 큰 위험을 초래합니다.” 미국 환경보호청의 웹사이트 Particulate Matter (PM) Basics는 미세먼지 오염에 대한 좋은 참고 자료입니다.

파나소닉의 SN-GCJA5 센서는 레이저에 기반한 광학 방식을 사용하여 공기 중의 미세먼지를 감지합니다. 센서는 PM1.0, PM2.5 그리고 PM10 입자에 대한 질량 밀도를 µg/m3 단위로 출력합니다. PM2.5 및 PM10에 대한 AQI 값은 EPA의 기술 문서에 제공된 공식에 기반하여 계산됩니다.

구현

이 프로젝트에서, MKR WiFi 1010은 SN-GCJA5 센서에 전원을 공급하며 통신을 위해 I2C 인터페이스가 연결되어 있습니다. 센서와 통신하여 센서 상태와 데이터를 읽기 위해 깃허브에 있는 Sparkfun의 "SparkFun Particle Sensor SN-GCJA5 Arduino Library"를 프로젝트의 아두이노 코드에 사용하였습니다. 센서의 JST 커넥터와 인터페이스하기 위해 JST의 GHR-05V-S 하우징과 AGHGH28K305 접점/점퍼선을 사용해 커넥터를 제작하였습니다. 전기적 연결은 아래와 같습니다:

MKR WiFi 1010 핀 SN-GCJA5 센서 커넥터 단자
5V 5 VDD
GND 4 GND
12 SCL 3 SCL
11 SDA 2 SDA

MKR WiFi 1010과 SN-GCJA5 센서 애플리케이션 설정

1 - MKR WiFi 1010에 아두이노를 설정합니다. 링크 참조: Getting started with the MKR WiFi 1010

2 - 애플리케이션에 필요한 라이브러리를 설치합니다. 아두이노의 Library Manager를 통해 아래 라이브러리를 추가합니다:

3 - 코드 검토 (파일명: MKR1010wifiPanSNGCJA5_AQI.ino)

초기 설정 및 와이파이 네트워크에 연결

#include <Arduino.h>
#include <WiFiNINA.h>
#include <ArduinoHttpClient.h>
#include "arduino_secrets.h" 
#include <Wire.h>

#include <ArduinoJson.h>   //include Json library

#include "SparkFun_Particle_Sensor_SN-GCJA5_Arduino_Library.h" //Click here to get the library: http://librarymanager/All#SparkFun_Particle_SN-GCJA5

// Create a unique ID for the data from the MKRWIFI1010 running this code
const char* jediID = "MKR1010WiFiSensor_SNGCJA5";

///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;        // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS;     // the Wifi radio's status

// IP address of server or Raspberry Pi running Machinechat JEDI software
// If you changed the JEDI port number, replace 8100 with the new port
char serverAddress[] = "192.168.1.7";  // server address
int port = 8100;

WiFiClient client;
HttpClient http = HttpClient(client, serverAddress, port);


SFE_PARTICLE_SENSOR myAirSensor;

float pm2_5Avg = 0.0;  //determine 10 second PM2.5 average
float pm10Avg = 0.0;  //determine 10 second PM2.5 average
float AQI2_5;
float AQI10;
int loop_ctr = 0;

void setup()
{
  Serial.begin(9600);
  Serial.println("begin...");  
  Serial.println(F("Panasonic SN-GCJA5 Example"));

  Wire.begin();

  if (myAirSensor.begin() == false)
  {
    Serial.println("The particle sensor did not respond. Please check wiring. Freezing...");
    while (1)
      ;
  }
  Serial.println("Sensor started");
  Serial.println("PM:1.0, 2.5, 10, Counts: 0.5, 1, 2.5, 5, 7.5, 10,");

  // attempt to connect to Wifi network:
    Serial.print("Attempting to connect to network: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network:
    status = WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  // you're connected now, so print out the data:
  Serial.println("You're connected to the network");
  Serial.println("----------------------------------------");
  printData();
  Serial.println("----------------------------------------");
}

PM2.5 및 PM10에 대한 AQI 계산

/*****
 * purpose: calculate PM2.5 AQI
 * parameters:
 *    float Avg2_5   average PM2.5
 *    
 * return value:
 *    float AQI       PM2.5 AQI
 ****/
float CalcAQI25(float Avg2_5)
 {
  float AQI;
  if (Avg2_5 < 12.0) {
    AQI = 4.17 * Avg2_5;  //calculation for PM2.5 0.0 to 12.0 "Good"
  } else if (Avg2_5 < 35.4) {
    AQI = (2.10 * (Avg2_5 - 12.1)) + 51;  //calculation for PM2.5 12.1 to 35.4 "Moderate"  
  } else if (Avg2_5 < 55.4) {
    AQI = (2.46 * (Avg2_5 - 35.5)) + 101;   //calculation for PM2.5 35.5 to 55.4 "Unhealthy for Sensitive Groups"
  } else if (Avg2_5 < 150.4) {
    AQI = (0.52 * (Avg2_5 - 55.5)) + 151;  //calculation for PM2.5 55.5 to 150.4 "Unhealthy"
  } else if (Avg2_5 < 250.4) {
    AQI = (0.99 * (Avg2_5 - 150.5)) + 201;  //calculation for PM2.5 150.5 to 250.4 "Very Unhealthy"
  } else if (Avg2_5 < 350.4) {
    AQI = (0.99 * (Avg2_5 - 250.5)) + 301; //calculation for PM2.5 250.5 to 350.4 "Hazardous"
  } else if (Avg2_5 < 500.4) {
    AQI = (0.66 * (Avg2_5 - 350.5)) + 401; //calculation for PM2.5 350.5 to 500.4 "Hazardous"
  } else {
    AQI = 501.0;
  }
  return AQI;
 }

 /*****
 * purpose: calculate PM10 AQI
 * parameters:
 *    float Avg10   average PM2.5
 *    
 * return value:
 *    float AQI       PM10 AQI
 ****/
float CalcAQI10(float Avg10)
 {
  float AQI;
  if (Avg10 < 54.0) {
    AQI = 0.93 * Avg10;  //calculation for PM10 0.0 to 54.0 "Good"
  } else if (Avg10 < 154) {
    AQI = (0.49 * (Avg10 - 55)) + 51;  //calculation for PM10 55 to 154 "Moderate"  
  } else if (Avg10 < 254) {
    AQI = (0.49 * (Avg10 - 155)) + 101;   //calculation for PM10 155 to 254 "Unhealthy for Sensitive Groups"
  } else if (Avg10 < 354) {
    AQI = (0.49 * (Avg10 - 255)) + 151;  //calculation for PM10 255 to 354 "Unhealthy"
  } else if (Avg10 < 424) {
    AQI = (1.43 * (Avg10 - 355)) + 201;  //calculation for PM10 355 to 424 "Very Unhealthy"
  } else if (Avg10 < 504) {
    AQI = (1.25 * (Avg10 - 425)) + 301; //calculation for PM10 425 to 504 "Hazardous"
  } else if (Avg10 < 604) {
    AQI = (1.0 * (Avg10 - 505)) + 401; //calculation for PM10 505 to 604 "Hazardous"
  } else {
    AQI = 501.0;
  }
  return AQI;
 }

센서 데이터를 JSON 형식으로 설정하고 HTTP POST를 사용하여 JEDI One으로 전송

void loop()
{
  // prep for sending HTTP post to JEDI One
  String postData1; //Json string 

  // get particle sensor data
  float pm1_0 = myAirSensor.getPM1_0();
  Serial.print(pm1_0, 2); //Print float with 2 decimals
  Serial.print(",");

  float pm2_5 = myAirSensor.getPM2_5();
  pm2_5Avg = pm2_5Avg + pm2_5;
  Serial.print(pm2_5, 2);
  Serial.print(",");

  float pm10 = myAirSensor.getPM10();
  pm10Avg = pm10Avg + pm10;
  Serial.print(pm10, 2);
  Serial.print(",");

  unsigned int pc0_5 = myAirSensor.getPC0_5();
  Serial.print(pc0_5);
  Serial.print(",");

  unsigned int pc1_0 = myAirSensor.getPC1_0();
  Serial.print(pc1_0);
  Serial.print(",");

  unsigned int pc2_5 = myAirSensor.getPC2_5();
  Serial.print(pc2_5);
  Serial.print(",");

  unsigned int pc5_0 = myAirSensor.getPC5_0();
  Serial.print(pc5_0);
  Serial.print(",");

  unsigned int pc7_5 = myAirSensor.getPC7_5();
  Serial.print(pc7_5);
  Serial.print(",");

  unsigned int pc10 = myAirSensor.getPC10();
  Serial.print(pc10);
  Serial.print(",");

  Serial.println();

 loop_ctr = loop_ctr + 1;
 Serial.print("loop_ctr = ");
 Serial.println(loop_ctr);
 if (loop_ctr > 9)  {
   Serial.print("pm2_5Avg = ");
   pm2_5Avg = pm2_5Avg/10;
   Serial.print(pm2_5Avg);
   Serial.print("   pm10Avg = ");
   pm10Avg = pm10Avg/10;
   Serial.println(pm10Avg);
   AQI2_5 = CalcAQI25(pm2_5Avg);   
   Serial.print("AQI PM2.5 = ");
   Serial.println(AQI2_5);
   AQI10 = CalcAQI10(pm10Avg);   
   Serial.print("AQI PM10 = ");
   Serial.println(AQI10);  
   // round and create int version of AQI10
   AQI10 = AQI10 + 0.5;
   int AQI10int = (int) AQI10;
   Serial.println(AQI10int); 

    //Following code creates the serialized JSON string to send to JEDI One
    //using ArduinoJson library
    StaticJsonDocument <200> doc;
    JsonObject context = doc.createNestedObject("context");
    context["target_id"] = String(jediID);
    JsonObject data = doc.createNestedObject("data");
    data["aqiPM2_5"] = AQI2_5;
    data["aqiPM10"] = AQI10int;
    serializeJson(doc, postData1);
    Serial.println(postData1);    //debug, can be commented out

    //HTTP post code start
    if (WiFi.status() == WL_CONNECTED) {
      //format http post to be compatible with JEDI one
      String contentType = "application/json";
      http.post("/v1/data/mc", contentType, postData1);
      // read the status code and body of the response
      int statusCode = http.responseStatusCode();
      String response = http.responseBody();
      Serial.print("Status code: ");
      Serial.println(statusCode);
      Serial.print("Response: ");
      Serial.println(response);
   } else {
      Serial.println("Error in WiFi connection");
   }


   // reset PM averages and loop counter 
   pm2_5Avg = 0;
   pm10Avg = 0;
   loop_ctr = 0;
 }   

  delay(1000); //The sensor has new data every second
}

MKR1010wifiPanSNGCJA5_AQI 센서 애플리케이션의 최신 소스 코드는 아래 링크의 깃허브에 있습니다:

JEDI One 설정

1 - Machinechat의 JEDI One이 라즈베리 파이에 아직 설치되지 않은 경우 아래를 참조하십시오:

2 - JEDI One 대시보드 설정
JEDI One에서, “Dashboards” 탭을 선택한 다음 "+"를 선택하여 새 차트를 추가하고 설정합니다.

차트 이름을 지정하고, "Chart Type"을 선택, "Source"는 MKR1010WiFiSensor_SNGCJA5를 선택, "Property"는 aqiPM2_5와 aqiPM10을 선택, 그리고 "Units"과 "Refresh Interval"을 입력합니다. 완료되면 대시보드는 아래와 비슷하게 보일 것입니다.

결론

파나소닉의 SN-GCJA5 PM 센서와 Sparkfun의 센서 라이브러리 및 MKRWI1010 아두이노 하드웨어 플랫폼을 결합하여 PM 공기질 센서를 빠르고 쉽게 구현할 수 있습니다. 와이파이를 사용하여, 센서 데이터를 라즈베리 파이에서 구동되는 Machinechat의 JEDI One IoT 데이터 관리 소프트웨어에 편리하게 전송할 수 있습니다.

참고 자료



영문 원본: Machinechat with Panasonic SN-GCJA5 Air Quality (PM) Sensor