아두이노 우노 미니 기상 관측소 - ESP8266 IoT 연동 DIY 센서 허브
부품 및 소모품
| 아두이노 우노 |
1개 |
| ESP8266 ESP-01 모듈 |
1개 |
| 아두이노 프로토 실드 (또는 일반 브레드보드) |
1개 |
| DHT22 온도 센서 |
1개 |
| Seeed Grove 조도 센서 |
1개 |
| Seeed Grove UV 센서 |
1개 |
| Seeed Grove 기압 센서 |
1개 |
| Seeed Grove 먼지 센서 |
1개 |
| 저항 1kΩ |
2개 |
| 저항 10kΩ |
1개 |
| 저항 4.75kΩ |
1개 |
| 점퍼 와이어 (범용) |
1세트 |
앱 및 온라인 서비스
프로젝트 소개
와이파이 연결 기능이 있는 첫 번째 아두이노 기반 미니 기상 관측소로, ThingSpeak 플랫폼을 통해 데이터를 공개 온라인에 게시할 수 있습니다.
다양한 센서를 사용해 다음과 같은 기상 및 환경 데이터를 수집합니다:
목표는 오픈 하드웨어를 사용해 작고 간단한 기상 관측소를 만드는 것입니다. 시작해볼까요!
전자 부품
조립에 특별한 도구는 필요 없습니다. 모든 부품은 즐겨 찾는 온라인 쇼핑몰에서 구매 가능합니다.
회로는 USB 포트(컴퓨터 또는 일반 휴대폰 충전기)로 전원 공급되며, 아두이노 파워 잭에 외부 DC 전원이나 배터리를 연결할 수도 있습니다.
기상 관측소 케이스 제작은 이 프로젝트 범위 외입니다.
부품 연결하기
회로도에 따라 모든 부품을 연결하세요. 각 센서를 브레드보드에 연결하기 위해 점퍼 와이어가 필요합니다. 프로토실드(더 콤팩트한 회로), 일반 브레드보드, 또는 직접 아두이노 실드를 설계해도 됩니다.
아두이노 우노에 USB 케이블을 연결하고 다음 단계로 진행하세요.
코드
최신 아두이노 IDE가 설치되어 있다고 가정하고, 다음 라이브러리를 다운로드해 설치하세요:
- DHT22 라이브러리
- Adafruit BMP085 라이브러리
라이브러리 추가 방법은 아두이노 공식 가이드를 참고하세요.
코드 섹션에 포함된 weatherBox.ino를 다운로드하세요. XXXXX를 WiFi 라우터 SSID, YYYYY를 라우터 비밀번호, ZZZZZ를 ThingSpeak 채널 쓰기 API 키로 교체하세요(획득 방법은 다음 단계 참조).
아두이노 보드를 컴퓨터 USB 포트에 연결하고 코드를 업로드하세요.
ThingSpeak 설정
- ThingSpeak 계정 생성
- 새 채널 생성
- 기상 관측소 이름과 설명 입력. 다음 채널 할당 후 저장:
- 채널 1 = 조도 (light)
- 채널 2 = 습도 (humidity)
- 채널 3 = 온도 (DHT22)
- 채널 4 = UV 지수
- 채널 5 = 먼지 농도
- 채널 6 = 기압
- 채널 7 = 온도 (BMP085)
- API 쓰기 키 복사. 아두이노 코드의 이전 단계에서 사용. 관측소 켜면 센서 값이 주기적으로 채널에 업로드됨. 각 변수의 공개/비공개 시각화 설정 가능.
공개 채널 예시: https://thingspeak.com/channels/35540
안드로이드 앱 활용
브라우저에서 언제든 기상 관측소 데이터를 볼 수 있지만, 안드로이드 스마트폰에서도 확인 가능합니다.
- 구글 플레이 스토어에서 ThingsView 앱 다운로드 및 설치
- 앱에서 채널 ID 입력 후 추가 클릭. 채널 ID는 ThingSpeak 채널 설정에서 확인 가능
- 각 변수의 현재 값이 그래프로 표시됨
즐거운 제작 되세요!
소스 코드 (weatherBox.ino)
#include
#include
#include
#include
#include
#include
#define SSID "XXXXX" // 본인 라우터 SSID로 교체
#define PASS "YYYYY" // 본인 라우터 비밀번호로 교체
#define IP "184.106.153.149" // thingspeak.com IP
#define DHT22_PIN 2
String GET = "GET /update?key=ZZZZZ&field1="; // ZZZZZ를 ThingSpeak 채널 쓰기 키로 교체
SoftwareSerial monitor(10, 11); // ESP8266 모듈과의 시리얼 통신 (RX, TX)
dht DHT;
Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085);
// 변수들
int luminancePin = A0;
int uvPin = A1;
int dustPin = 8;
unsigned long duration;
unsigned long starttime;
unsigned long sampletime_ms = 30000;
unsigned long delay_time = 60000;
unsigned long lowpulseoccupancy = 0;
float ratio = 0;
float concentration = 0;
void setup() {
Serial.begin(9600);
monitor.begin(9600);
Serial.println("Initializing...");
pinMode(dustPin, INPUT);
Serial.println("Detecting BMP085 pressure sensor...");
if (!bmp.begin()) {
Serial.println("BMP085 sensor wasn't detected. Verify your connections or I2C ADDR!");
while (1);
}
Serial.println("BMP085 detected!");
// 와이파이 모듈 통신 확인
monitor.flush();
monitor.println("AT");
delay(2000);
if (monitor.find("OK")) {
Serial.println("Communication with ESP8266 module: OK");
} else {
Serial.println("ESP8266 module ERROR");
}
// 와이파이 연결
connectWiFi();
Serial.print("Sampling (");
Serial.print(sampletime_ms / 1000);
Serial.println("s)...");
// 타이머 초기화
starttime = millis();
}
void loop() {
// 먼지 입자 측정
duration = pulseIn(dustPin, LOW);
lowpulseoccupancy = lowpulseoccupancy + duration;
// 30초 주기
if ((millis() - starttime) >= sampletime_ms) {
ratio = lowpulseoccupancy / (sampletime_ms * 10.0); // 백분율 (0~100%)
concentration = 1.1 * pow(ratio, 3) - 3.8 * pow(ratio, 2) + 520 * ratio + 0.62; // 데이터시트 공식
lowpulseoccupancy = 0;
// 다른 센서 읽기
char buffer[10];
// 조도 센서
float luminance = analogRead(luminancePin);
// UV 센서
float uv = analogRead(uvPin);
uv = uv * 0.0049; // 볼트로 변환
uv = uv * 307; // mW/m²로 변환
uv = uv / 200; // UV 지수 계산
// 온습도
int chk = DHT.read22(DHT22_PIN);
float humidity = DHT.humidity;
float temperature = DHT.temperature;
// 기압 및 온도 (BMP085)
sensors_event_t event;
bmp.getEvent(&event);
float pressure = 0;
float temperature1 = 0;
if (event.pressure) {
pressure = event.pressure;
bmp.getTemperature(&temperature1);
}
// 센서 값을 문자열로 변환
String luminanceStr = dtostrf(luminance, 4, 1, buffer); luminanceStr.replace(" ", "");
String uvStr = dtostrf(uv, 4, 1, buffer); uvStr.replace(" ", "");
String humidityStr = dtostrf(humidity, 4, 1, buffer); humidityStr.replace(" ", "");
String temperatureStr = dtostrf(temperature, 4, 1, buffer); temperatureStr.replace(" ", "");
String dustStr = dtostrf(concentration, 4, 1, buffer); dustStr.replace(" ", "");
String pressureStr = dtostrf(pressure, 4, 1, buffer); pressureStr.replace(" ", "");
String temperature1Str = dtostrf(temperature1, 4, 1, buffer); temperature1Str.replace(" ", "");
// ThingSpeak로 데이터 전송
updateSensors(luminanceStr, humidityStr, temperatureStr, uvStr, dustStr, pressureStr, temperature1Str);
// 다음 샘플링 주기까지 대기
Serial.print("Wait ");
Serial.print(delay_time / 1000);
Serial.println("s for next sampling");
Serial.println();
delay(delay_time);
// 새 주기 초기화
Serial.println();
Serial.print("Sampling (");
Serial.print(sampletime_ms / 1000);
Serial.println("s)...");
starttime = millis();
}
}
// ThingSpeak로 데이터 전송
void updateSensors(String luminanceStr, String humidityStr, String temperatureStr, String uvStr, String dustStr, String pressureStr, String temperature1Str) {
String cmd = "AT+CIPSTART=\"TCP\",\"";
cmd += IP;
cmd += "\",80";
monitor.println(cmd);
delay(2000);
cmd = GET;
cmd += luminanceStr;
cmd += "&field2=";
cmd += humidityStr;
cmd += "&field3=";
cmd += temperatureStr;
cmd += "&field4=";
cmd += uvStr;
cmd += "&field5=";
cmd += dustStr;
cmd += "&field6=";
cmd += pressureStr;
cmd += "&field7=";
cmd += temperature1Str;
cmd += "\r\n";
delay(1000);
int strsize = cmd.length();
monitor.println("AT+CIPSEND=" + String(strsize));
delay(2000);
monitor.print(cmd);
if (monitor.find("OK")) {
Serial.println("Transmission completed with success");
} else {
Serial.println("Transmission failed!");
}
}
void sendDebug(String cmd) {
Serial.print("SEND: ");
Serial.println(cmd);
monitor.println(cmd);
}
boolean connectWiFi() {
Serial.println("Connecting wi-fi...");
String cmd = "AT+CWMODE=1";
monitor.println(cmd);
delay(2000);
monitor.flush();
cmd = "AT+CWJAP=\"";
cmd += SSID;
cmd += "\",\"";
cmd += PASS;
cmd += "\"";
monitor.println(cmd);
delay(5000);
if (monitor.find("OK")) {
Serial.println("Connection succeeded!");
return true;
} else {
Serial.println("Connection failed!");
return false;
}
Serial.println();
}
라이브러리 다운로드