오늘은 아두이노 ESP32로 온습도를 측정하고 데이터를 전송하는 와이파이 백엽상을 만들어볼 것이다.
해당 내용은 학교에서 진행할 16+1 수업 원고를 바탕으로 작성한 글이다.
준비물은 간단하다.
ESP32, DHT-22, 전선 3개
먼저 ESP32를 간단하게 설명하자면, 와이파이 모듈이 박혀있는 아두이노이다.
하지만 크기는 일반 아두이노보다 훨신 작은 편이다.
어어?? 그런데 핀 번호가 좀 이상하다?
이건 지난 글에서도 설명했지만, 와이파이 모듈이 들어가면서 핀맵핑이 변경되었기 때문이다.
아래 ESP32 아두이노의 핀맵핑을 올려 준다.
그래도 이 녀석은 GPIO 핀 번호와 핀 이름이 거의 일치하는 듯 하다.
아래의 핀들 중에서도 안전한 D4 핀을 이용해 dht-22를 굴려볼 예정이다.
Arduino D 핀 | GPIO 번호 | 주의사항 | 기능 및 설명 |
---|---|---|---|
D0 | GPIO0 | strapping: 부팅 시 LOW 또는 floating 필요 | flash 모드 진입에 영향 ([randomnerdtutorials.com][1]) |
D1 | GPIO1 (TX0) | USB 시리얼 충돌 주의 | 업로드·디버깅용 UART0 TX |
D2 | GPIO2 | strapping: 부팅 시 LOW/floating 필요 | on‑board LED → 부팅 영향 있음 |
D3 | GPIO3 (RX0) | USB 시리얼 충돌 주의 | UART0 RX, 부팅 시 HIGH |
D4 | GPIO4 | ★ 안전 | 디지털 I/O, ADC2_CH0, 터치 가능 |
D5 | GPIO5 | strapping: 부팅 시 HIGH 필요 | 부트 중 PWM 출력 가능 |
D6–D11 | GPIO6–11 | 절대 사용 금지 | 내부 SPI 플래시 핀 |
D12 | GPIO12 | strapping: 부팅 시 LOW 필요 | ADC2_CH5, 터치, 부팅 영향 있음 |
D13 | GPIO13 | 안전 | ADC2_CH4, 터치 가능 |
D14 | GPIO14 | 부팅 시 PWM 나옴, 부트 스트래핑 가능 | ADC2_CH6, 터치 가능 |
D15 | GPIO15 | strapping: 부팅 시 HIGH 필요, PWM 출력 | ADC2_CH3, 터치 가능 |
D16 | GPIO16 | Wrover 모듈 사용 시 PSRAM용 주의 | UART2 기본 TX/RX (17/16) |
D17 | GPIO17 | Wrover 모듈 주의 | UART2 기본 TX/RX (17/16) |
D18, D19, D21–D23, D25–D27, D32–D33 | 생략 | 안전 | 디지털, PWM, I2C, SPI, ADC/DAC 사용 가능 |
D34, D35, D36(VP), D39(VN) | 34,35,36,39 | 입력 전용, 풀업/풀다운 없음 | ADC1 채널 (추가 기능 없음) |
보통 아두이노를 사면 기본적으로 들어있는 온습도 온도계는 DHT-11이다.
색깔이 파란색인 녀석인데, 이걸 쓰면 오차 범위가 2도 정도로 무척 크다.
아래에 DHT와 DHT-22의 성능을 표로 정리해보았다.
항목 | DHT-11 | DHT-22 (AM2302) |
---|---|---|
외형 | ![]() | ![]() |
온도 측정 범위 | 0 ~ 50°C | -40 ~ +80°C |
온도 정확도 | ±2°C | ±0.5°C |
습도 측정 범위 | 20 ~ 80% RH | 0 ~ 100% RH |
습도 정확도 | ±5% RH | ±2~3% RH |
측정 해상도 | 온도: 1°C, 습도: 1% RH | 온도: 0.1°C, 습도: 0.1% RH |
측정 주기 (샘플링 간격) | 1초 이상 | 2초 이상 |
데이터 전송 속도 | 저속 | 저속 |
크기 | 소형 | 약간 더 큼 |
가격 | 저렴 | DHT-11보다 비쌈 |
사용 전압 | 3 ~ 5V | 3 ~ 5V |
오차 범위가 너무 크면 데이터를 신뢰할 수 없기에, 오차가 다소 적은 DHT-22를 사용하기로 했다.
이보다 더 정확한 온도를 얻고 싶다면 PT-100 온도계를 쓰면 되지만, 그정도 까지 필요하지는 않은 것 같다.
위의 사진에서 빨간 동그라미 안에 보면 칩이 하나 박혀있다.
이게 중국에서 만드는 CH340이라는 칩셋인데, 컴퓨터로 치면 CPU에 해당한다고 생각하면 된다.
CH340칩의 드라이버를 제공하는 공식 사이트와 다운로드 링크로 건다.
다운받아서 무지성으로 다음을 눌러 설치해주자.
위 파일은 아래의 모든 칩들을 포함하는 드라이버다.
참고로 맥북에서는 해당 칩셋 드라이버를 기본으로 제공하므로 설치할 필요가 없다.
CH340G,CH340T,CH340C,CH340N, CH340K, CH340E,CH340B,CH341A,CH341F, CH341T,CH341B,CH341C,CH341U
이제 아두이노 IDE에 보드관리자를 세팅해주자.
Arduino IDE 에서 File > Preferences(기본 설정)으로 들어간다.
그리고 추가 보드 관리자 URL에 아래의 주소를 복사해 입력한다.
https://espressif.github.io/arduino-esp32/package_esp32_index.json
그리고 보드매니저에서 ESP32를 검색하고, 제공자가 by Espressif Systems인 것을 찾아 설치한다.
그리고 USB포트에 아두이노를 연결하면 포트가 자동으로 잡힌다.
이제 해당 포트에 해당하는 아두이노 보드를 골라서 선택해주면 된다.
그리고 간단한 예제를 하나 업로드 해보자.
파일 -> 예제 -> HttpClient -> BasicHttpsClient를 클릭하면 Http요청 예제가 나온다.
이제 이 예제에서 SSID, PASSWORD 부분만 변경해준다.
void setup() {
USE_SERIAL.begin(115200);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for (uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
// 아래의 SSID에 와이파이 이름, PASSWORD에 비밀번호 입력
wifiMulti.addAP("SSID", "PASSWORD");
}
혹시 만약 업로드에 실패한다면 업로드 속도를 좀 조절해주자.
도구 -> Upload Speed를 조금 낮은 값으로 변경하면 안정적으로 잘된다.
그리고 있는 그대로 예제 사이트로 접속하도록 하면 시리얼 모니터에서 html을 출력한다.
이렇게 까지만 한다면 1/3 정도는 완성한 것이다.
이제 아두이노에 DHT-22 센서를 연결해 온습도 값을 받아 와보자.
먼저 라이브러리를 찾아 깔아준다.
dht sensor library를 검색해서 adafruit이 제작한 라이브러리를 받아주자.
이제 파일->예제로 들어가면 온습도계 예제가 있다.
이 예제들 중 센서 테스트를 한번 해보자.
주석들을 빼고 핵심은 아래와 같다.
#include "DHT.h"
#define DHTPIN 4 // 핀번호 지정
#define DHTTYPE DHT22 // DHT 22
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(115200); // 시리얼 통신 속도. 115200으로 변경
Serial.println(F("DHTxx test!"));
dht.begin();
}
void loop() {
delay(2000);
float h = dht.readHumidity();
float t = dht.readTemperature();
float f = dht.readTemperature(true); // true 넣으면 화씨 됨
if (isnan(h) || isnan(t) || isnan(f)) {
Serial.println(F("Failed to read from DHT sensor!"));
return;
}
float hif = dht.computeHeatIndex(f, h); // 이건 체감온도
float hic = dht.computeHeatIndex(t, h, false);
Serial.print(F("Humidity: "));
Serial.print(h);
Serial.print(F("% Temperature: "));
Serial.print(t);
Serial.print(F("°C "));
Serial.print(f);
Serial.print(F("°F Heat index: "));
Serial.print(hic);
Serial.print(F("°C "));
Serial.print(hif);
Serial.println(F("°F"));
}
이렇게 아두이노로 전송한 뒤 온도가 출력되는걸 시리얼 모니터에서 확인하면 된다.
입으로 한번 후 불어주었더니 습도가 100%까지 치솟았다.
이제 아두이노 와이파이 연결과 센서 연결이 해결되었으니, 구글 스프레드 시트 설정으로 넘어가자.
왠만하면 그냥 내가 글을 쓰는데, 구글 스프레드 시트 설정만 해도 길이가 너무 길어질 것 같아 예전에 썼던 글을 첨부한다.
아래의 링크에서 한번 확인해보길 바란다.
이제 구글 스프레드 시트 설정까지 끝났다면, 아두이노에서 GET 요청을 날려볼 차례이다.
위에서 와이파이 연결에서 있었던 예제를 가져온다.
여기서 요청 주소만 구글 스크립트로 바꿔주면 된다.
중간부분은 생략하고 올려본다.
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <NetworkClientSecure.h>
...
String host = "https://script.google.com/";
String googleScriptID = "나의 구글 스크립트 아이디";
String scriptUrl = "macros/s/"+googleScriptID+"/exec";
String queryString = "?value1='안녕'&value2='테스트중!'";
String getReqUrl = host + scriptUrl + queryString;
void setup() {
...
WiFiMulti.addAP("와이파이 이름", "와이파이 비밀번호");
...
}
void loop() {
NetworkClientSecure *client = new NetworkClientSecure;
if (client) {
client->setCACert(rootCACertificate);
{
HTTPClient https;
Serial.print("[HTTPS] begin...\n");
if (https.begin(*client, getReqUrl)) { // <--- 이부분만 수정하면 됨
...
}
}}
}
이렇게 한 후 실행하면 스프레드 시트로 값이 들어오는게 보인다.
이제 마지막 스텝은 DHT-22의 데이터를 받아 전송하는 것이다.
이제 두 코드를 합쳐보자.
코드를 합치기 어렵다면 그냥 무지성으로 AI에게 맡기는 방법도 추천해본다.
일단 나는 먼저 DHT-22의 loop 코드를 변경해 Weather이라는 구조체를 반환하는 함수로 만들어 보았다.
#include "DHT.h"
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(115200);
dht.begin();
}
struct WeatherData {
float humi;
float temp;
float heat;
bool valid;
};
WeatherData getWeather() {
WeatherData data;
data.humi = dht.readHumidity();
data.temp = dht.readTemperature();
if (isnan(data.humi) || isnan(data.temp)) {
Serial.println(F("Failed to read from DHT sensor!"));
data.valid = false;
return data;
}
data.valid = true;
data.heat = dht.computeHeatIndex(data.temp, data.humi, false);
Serial.print(F("Humidity: "));
Serial.print(data.humi);
Serial.print(F("% Temperature: "));
Serial.print(data.temp);
Serial.print(F("°C "));
Serial.print(F("°Heat index: "));
Serial.print(data.heat);
Serial.print(F("°C "));
return data;
}
여기서 필요한 것들을 적절히 배분해 httpsClient 코드에 합쳐주면 된다.
이제 합쳐보자.
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <NetworkClientSecure.h>
#include "DHT.h"
...
인증서 생략
...
void setClock() {
...
}
#define DHTPIN 4
#define DHTTYPE DHT22
WiFiMulti WiFiMulti;
DHT dht(DHTPIN, DHTTYPE);
char* SSID = "와이파이 이름";
char* PASSWORD = "와이파이 비번";
String host = "https://script.google.com/";
String googleScriptID = "나의 구글 스크립트 아이디";
String scriptUrl = "macros/s/"+googleScriptID+"/exec";
struct WeatherData {
...
};
WeatherData getWeather(); //오류 발생으로 미리 선언해 줌
void setup() {
dht.begin();
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFiMulti.addAP(SSID, PASSWORD);
Serial.print(F("Waiting for WiFi to connect..."));
while ((WiFiMulti.run() != WL_CONNECTED)) {
Serial.print(F("."));
}
Serial.println(F(" connected"));
setClock();
}
WeatherData getWeather() {
...
}
void loop() {
NetworkClientSecure *client = new NetworkClientSecure;
if (client) {
client->setCACert(rootCACertificate);
{
HTTPClient https;
WeatherData data = getWeather();
if (!data.valid){
Serial.println(F("날씨 정보를 얻을 수 없습니다."));
Serial.println("Waiting 10s before the next round...");
delay(10000);
return;
};
Serial.print("[HTTPS] begin...\n");
String queryString = "?value1=" + String(data.humi) + "&value2=" + String(data.temp) ;
String getReqUrl = host + scriptUrl + queryString;
if (https.begin(*client, getReqUrl)) { // HTTPS
Serial.print("[HTTPS] GET...\n");
int httpCode = https.GET();
if (httpCode > 0) {
Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
} else {
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
}
https.end();
} else {
Serial.printf("[HTTPS] Unable to connect\n");
}
}
delete client;
} else {
Serial.println("Unable to create client");
}
Serial.println();
Serial.println("Waiting 10s before the next round...");
delay(10000);
}
이렇게 한 후 시리얼 모니터에서 습도, 온도가 뜨고 Get요청 코드가 200이 뜨면 성공한 것.
이제 스프레드 시트에 들어가서 확인해면 된다.
추가적으로 체감 온도도 기록하고 싶다면 구글 스크립트를 수정해주면 된다.
파라미터를 파싱하는 부분에서 변수를 추가하거나, 변수명을 변경할 수 있다.
...
for (var param in e.parameter) {
Logger.log('In for loop, param=' + param);
var value = stripQuotes(e.parameter[param]);
Logger.log(param + ':' + e.parameter[param]);
switch (param) {
case 'humi':
rowData[2] = value; // value1 in column C
result = '습도 기록됨';
break;
case 'temp':
rowData[3] = value; // Humidity in column D
result += ', 온도 기록됨';
break;
case 'heat':
rowData[4] = value;
result += ", 체감 온도 기록됨";
break
default:
result = "지원되지 않는 파라미터 입니다.";
}
...
물론 이렇게 해주면 아두이노 코드에서도 쿼리스트링을 변경해 주어야 한다.
어차피 개인 프로젝트이기에 이렇게 하는건 앞으로의 유지 보수를 위함이거나, 개인 취향인 것 같다.
과거에 학생들과 실험을 했을 때는 아래와 같은 데이터가 나왔다.
중간에 데이터 결측치는 밤에 전원이 차단되면서 생긴것...
그래도 간단한 장치만 만들어 놓으면 이렇게 자동으로 데이터를 수집해 주니, 이걸 우리는 분석만 하면 된다는 점에서는 무척 좋은 것 같다.
만들어진 결과물만 보면 내용이 없어 보이는데 풀어 쓰다보니 글이 엄청나게 길어졌다.
16+1 수업에서 앞에 3시간은 C언어를, 뒤에 2시간은 아두이노 온습도계를, 나머지 시간은 개인 프로젝트를 진행할 예정인데 시간이 부족하지 않을까 걱정이 된다.
아무쪼록 열심히 준비한 수업이 잘 끝나고 여름방학을 맞이했으면 좋겠다.