DIY 아두이노 PM10·온도·습도 측정 키트 – 코로나19 시대 공기 질 모니터링
부품 및 소모품
| 부품 | 수량 | 비고 |
| Arduino Nano R3 | 1 | |
| SDS018 PM10/PM2.5 레이저 센서 | 1 | UART 출력, 내장 팬, 0.3µg/m³ 해상도 |
| DHT22 온습도 센서 모듈 | 1 | 풀업 저항·커패시터 내장 |
| OLED 디스플레이 I2C (SSD1306, 128×32) | 1 | |
| 3mm LED (녹색, 노랑, 적색) | 각 1 | 상태 표시용 |
| 390Ω 저항 | 3 | LED 전류 제한 |
| 100nF 커패시터 | 1 | DHT22 안정화용 (선택) |
| 4.7kΩ 저항 | 1 | DHT22 풀업용 (선택, 모듈에 내장 시 불필요) |
| 파워뱅크 (5V USB 출력) | 1 | 휴대용 전원 |
| M3×16 나사 (케이스 조립) | 4 | |
| M3×6 나사 (SDS018 고정) | 1 | |
| 기타: 열수축 튜브, 핀 헤더, 점퍼 와이어, 글루건 등 | - | |
필요 도구
- 납땜기, 무연 납
- 3D 프린터 (케이스 출력용, 주황색 PLA 권장)
- 무선 드릴/드라이버
- 글루건
소프트웨어
- Arduino IDE
- 라이브러리:
Adafruit_SSD1306 (v2.2.1), DHT sensor library (v1.3.10)
프로젝트 배경
미세먼지와 코로나19의 상관관계
이탈리아 연구진(2020.3)은 롬바르디아 등 북부 지역의 높은 PM10 농도가 코로나19 확산 속도와 치명률을 높였다는 상관관계를 발표했습니다. 집중 축산(공장식 축산)에서 발생하는 암모니아·미세먼지가 봉쇄 기간에도 농도를 유지시켰고, 미세먼지가 바이러스를 운반하는 '캐리어' 역할을 했을 가능성을 제시했습니다. 또한 상대습도 40~60%에서 바이러스 생존율이 낮고, 그 외 구간에서 높다는 연구 결과도 있습니다.
해결책: 휴대용 위험도 알림 장치
PM10 농도, 온도, 상대습도를 실시간 측정해 4단계(정상/낮음/경고/위험)로 시각·청각 경고하는 저비용 휴대용 장치를 제작했습니다. 야외 활동 시 마스크 착용 또는 실내 대피 판단을 돕습니다.
위험도 판단 기준 및 LED 표시
| 단계 | PM10 (µg/m³) | 습도 (%) | LED 표시 |
| 정상 (Normal) | 0~25 | 40~60 | 전체 OFF |
| 낮음 (Low) | 0~25 | <40 또는 >60 | 녹색 ON |
| 낮음 (Low) | 26~50 | 40~60 | 녹색 ON |
| 경고 (Warning) | 26~50 | <40 또는 >60 | 녹색+노랑 ON |
| 경고 (Warning) | >51 | 40~60 | 녹색+노랑 ON |
| 위험 (Alarm) | >51 | <40 또는 >60 | 녹색+노랑+적색 ON |
센서 상세 및 연결
SDS018 - PM10/2.5 센서
레이저 산란 원리, 0.3~10µm 입자 측정, UART(TTL) 출력, 내장 팬, 응답시간 <10초.
핀아웃: 1-NC, 2-PWM(1µm), 3-5V, 4-PWM(2.5µm), 5-GND, 6-RX, 7-TX
연결: SDS018 5V→Arduino 5V, GND→GND, TX(7)→Arduino RX(D0)
DHT22 - 온습도 센서
디지털 출력, 3~5V, 0~100% 습도(±2~5%), -40~80°C(±0.5°C), 0.5Hz 샘플링.
연결: VDD→5V, GND→GND, VDD-GND 사이 100nF, DATA-VDD 사이 4.7kΩ 풀업, DATA→Arduino D2
OLED I2C 디스플레이 (SSD1306)
128×32, 백색 텍스트, I2C 인터페이스.
연결: VCC→5V, GND→GND, SCL→A5, SDA→A4
LED 연결
- 적색 애노드 → D3, 녹색 → D4, 노랑 → D5
- 각 캐소드에 390Ω 저항 직렬 후 GND
아두이노 나노 하드웨어 수정 (필수)
SDS018 TX가 아두이노 RX(D0)와 공유되므로, 온보드 USB-시리얼 컨버터(RX 라인)의 SMD 저항(R1)을 제거해 간섭을 방지해야 합니다. 저항을 수직으로 세우고 와이어를 납땜해 핀 헤더로 뽑아내면, 프로그래밍 시에만 점퍼로 연결하고 평소에는 분리해 센서 통신 가능. (영구 제거 시 재프로그래밍 불가)
전원 공급
파워뱅크 USB 출력을 나노 USB 포트에 연결 (또는 Vin 핀에 7~12V 입력).
케이스 및 조립
3D 프린팅 케이스(상/하 2파츠, 주황색 PLA).
- 하부: 아두이노 나노, SDS018 고정 (M3×6 나사), 미니 USB 포트 구멍
- 상부: DHT22, OLED, 3개 LED 고정 (LED 마운트 하드웨어 사용 시 구멍 5mm로 확대)
- 글루건으로 모듈 고정, 열수축 튜브로 배선 절연, 점퍼로 SDS018 연결
STL 파일: 상부, 하부
펌웨어 로직 (플로우차트 기반)
- 초기화: 주변장치 설정, LED 테스트(녹→노→적 순차 점등)
- 메인 루프 (2초 주기):
- DHT22 읽기 (습도, 온도) → 시리얼 모니터 출력
- SDS018 읽기 (PM2.5, PM10) → 시리얼 모니터 출력 (0xAA 0xC0 헤더, 체크섬 검증)
- 위험도 판별 → LED 제어
- 디스플레이 전환 표시: PM10+습도 ↔ PM2.5+온도 (알람 시 "Alarm!!!" 표시)
소스 코드
#include <Adafruit_SSD1306.h>
#include "DHT.h"
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET 4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
int startdhr22 = 0;
int switchDisplay = 0;
const int ledRed = 3, ledGreen = 4, ledYellow = 5;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(ledRed, OUTPUT); pinMode(ledGreen, OUTPUT); pinMode(ledYellow, OUTPUT);
dht.begin();
Serial.begin(9600);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) for(;;);
display.clearDisplay(); display.setTextSize(2); display.setTextColor(SSD1306_WHITE);
// LED 테스트
digitalWrite(ledGreen, HIGH); delay(500);
digitalWrite(ledYellow, HIGH); delay(500);
digitalWrite(ledRed, HIGH); delay(500);
digitalWrite(ledGreen, LOW); digitalWrite(ledYellow, LOW); digitalWrite(ledRed, LOW); delay(500);
}
void loop() {
if(0 == startdhr22) { delay(2000); startdhr22 = 1; }
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) { Serial.println("DHT read failed"); return; }
Serial.print("Humidity: "); Serial.print(h); Serial.print("% Temp: "); Serial.print(t); Serial.println("°C");
// SDS018 읽기 (10바이트 프레임 파싱)
uint8_t dataIN[10] = {0}; float pm25, pm10;
while(Serial.available() > 0) {
for(int i=0; i<10; i++) dataIN[i] = Serial.read();
if(dataIN[0]==0xAA && dataIN[1]==0xC0 && dataIN[9]==0xAB) {
uint8_t cksum=0; for(int i=2;i<=7;i++) cksum+=dataIN[i];
if(cksum == dataIN[8]) {
digitalWrite(LED_BUILTIN, HIGH);
pm25 = ((dataIN[3]*256.0)+dataIN[2])/10.0;
pm10 = ((dataIN[5]*256.0)+dataIN[4])/10.0;
Serial.print("PM2.5: "); Serial.print(pm25); Serial.println(" ug/m3");
Serial.print("PM10: "); Serial.print(pm10); Serial.println(" ug/m3");
}
Serial.println("-----------------");
}
Serial.flush();
}
// LED 제어 (위험도 판별)
if(pm10<=25 && h>=40 && h<=60) { digitalWrite(ledGreen,LOW); digitalWrite(ledYellow,LOW); digitalWrite(ledRed,LOW); }
else if(pm10<=25 && (h<40 || h>60)) { digitalWrite(ledGreen,HIGH); digitalWrite(ledYellow,LOW); digitalWrite(ledRed,LOW); }
else if(pm10>25 && pm10<=50 && h>=40 && h<=60) { digitalWrite(ledGreen,HIGH); digitalWrite(ledYellow,LOW); digitalWrite(ledRed,LOW); }
else if(pm10>25 && pm10<=50 && (h<40 || h>60)) { digitalWrite(ledGreen,HIGH); digitalWrite(ledYellow,HIGH); digitalWrite(ledRed,LOW); }
else if(pm10>50 && h>=40 && h<=60) { digitalWrite(ledGreen,HIGH); digitalWrite(ledYellow,HIGH); digitalWrite(ledRed,LOW); }
else { digitalWrite(ledGreen,HIGH); digitalWrite(ledYellow,HIGH); digitalWrite(ledRed,HIGH); }
// 디스플레이 토글 표시
if(0 == switchDisplay) {
display.clearDisplay(); display.setTextColor(SSD1306_WHITE); display.setCursor(0,0);
if(pm10>50) display.print("Alarm!!!"); else { display.print("PM10:"); display.println(pm10); }
display.print("HR%:"); display.println(h); display.display(); switchDisplay=1;
} else {
display.clearDisplay(); display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); display.setCursor(0,0);
display.print("PM25:"); display.println(pm25); display.print("T[C]:"); display.println(t); display.display(); switchDisplay=0;
}
delay(500); digitalWrite(LED_BUILTIN, LOW); delay(1500);
}
3D 케이스 파일
회로도
전체 전기 회로도: 아두이노 나노 중심, SDS018(RX), DHT22(D2), OLED(I2C: A4/A5), LED(D3~D5), 파워뱅크(USB) 연결.