아두이노 ESP32로 온습도 데이터 수집하기

힘센캥거루·2025-07-07

오늘은 아두이노 ESP32로 온습도를 측정하고 데이터를 전송하는 와이파이 백엽상을 만들어볼 것이다.

해당 내용은 학교에서 진행할 16+1 수업 원고를 바탕으로 작성한 글이다.

1. 준비물

준비물은 간단하다.

ESP32, DHT-22, 전선 3개

준비물

먼저 ESP32를 간단하게 설명하자면, 와이파이 모듈이 박혀있는 아두이노이다.

하지만 크기는 일반 아두이노보다 훨신 작은 편이다.

2. ESP32 핀맵

준비물

어어?? 그런데 핀 번호가 좀 이상하다?

이건 지난 글에서도 설명했지만, 와이파이 모듈이 들어가면서 핀맵핑이 변경되었기 때문이다.

아래 ESP32 아두이노의 핀맵핑을 올려 준다.

그래도 이 녀석은 GPIO 핀 번호와 핀 이름이 거의 일치하는 듯 하다.

아래의 핀들 중에서도 안전한 D4 핀을 이용해 dht-22를 굴려볼 예정이다.

아두이노 핀맵

Arduino D 핀GPIO 번호주의사항기능 및 설명
D0GPIO0strapping: 부팅 시 LOW 또는 floating 필요flash 모드 진입에 영향 ([randomnerdtutorials.com][1])
D1GPIO1 (TX0)USB 시리얼 충돌 주의업로드·디버깅용 UART0 TX
D2GPIO2strapping: 부팅 시 LOW/floating 필요on‑board LED → 부팅 영향 있음
D3GPIO3 (RX0)USB 시리얼 충돌 주의UART0 RX, 부팅 시 HIGH
D4GPIO4★ 안전디지털 I/O, ADC2_CH0, 터치 가능
D5GPIO5strapping: 부팅 시 HIGH 필요부트 중 PWM 출력 가능
D6D11GPIO6–11절대 사용 금지내부 SPI 플래시 핀
D12GPIO12strapping: 부팅 시 LOW 필요ADC2_CH5, 터치, 부팅 영향 있음
D13GPIO13안전ADC2_CH4, 터치 가능
D14GPIO14부팅 시 PWM 나옴, 부트 스트래핑 가능ADC2_CH6, 터치 가능
D15GPIO15strapping: 부팅 시 HIGH 필요, PWM 출력ADC2_CH3, 터치 가능
D16GPIO16Wrover 모듈 사용 시 PSRAM용 주의UART2 기본 TX/RX (17/16)
D17GPIO17Wrover 모듈 주의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 채널 (추가 기능 없음)

3. DHT-11 vs DHT-22

보통 아두이노를 사면 기본적으로 들어있는 온습도 온도계는 DHT-11이다.

색깔이 파란색인 녀석인데, 이걸 쓰면 오차 범위가 2도 정도로 무척 크다.

아래에 DHT와 DHT-22의 성능을 표로 정리해보았다.

항목DHT-11DHT-22 (AM2302)
외형dth-11dth-22
온도 측정 범위0 ~ 50°C-40 ~ +80°C
온도 정확도±2°C±0.5°C
습도 측정 범위20 ~ 80% RH0 ~ 100% RH
습도 정확도±5% RH±2~3% RH
측정 해상도온도: 1°C, 습도: 1% RH온도: 0.1°C, 습도: 0.1% RH
측정 주기 (샘플링 간격)1초 이상2초 이상
데이터 전송 속도저속저속
크기소형약간 더 큼
가격저렴DHT-11보다 비쌈
사용 전압3 ~ 5V3 ~ 5V

오차 범위가 너무 크면 데이터를 신뢰할 수 없기에, 오차가 다소 적은 DHT-22를 사용하기로 했다.

이보다 더 정확한 온도를 얻고 싶다면 PT-100 온도계를 쓰면 되지만, 그정도 까지 필요하지는 않은 것 같다.

4. CH340 드라이버 설치

준비물

위의 사진에서 빨간 동그라미 안에 보면 칩이 하나 박혀있다.

이게 중국에서 만드는 CH340이라는 칩셋인데, 컴퓨터로 치면 CPU에 해당한다고 생각하면 된다.

CH340칩의 드라이버를 제공하는 공식 사이트와 다운로드 링크로 건다.

CH340 드라이버 다운

다운받아서 무지성으로 다음을 눌러 설치해주자.

위 파일은 아래의 모든 칩들을 포함하는 드라이버다.

참고로 맥북에서는 해당 칩셋 드라이버를 기본으로 제공하므로 설치할 필요가 없다.

CH340G,CH340T,CH340C,CH340N, CH340K, CH340E,CH340B,CH341A,CH341F, CH341T,CH341B,CH341C,CH341U

5. Arduino IDE 보드관리자 설정

이제 아두이노 IDE에 보드관리자를 세팅해주자.

Arduino IDE 에서 File > Preferences(기본 설정)으로 들어간다.

아두이노 ESP32 보드관리자 세팅

그리고 추가 보드 관리자 URL에 아래의 주소를 복사해 입력한다.

https://espressif.github.io/arduino-esp32/package_esp32_index.json

아두이노 ESP32 보드관리자 세팅 아두이노 ESP32 보드관리자 세팅

그리고 보드매니저에서 ESP32를 검색하고, 제공자가 by Espressif Systems인 것을 찾아 설치한다.

alt text

그리고 USB포트에 아두이노를 연결하면 포트가 자동으로 잡힌다.

이제 해당 포트에 해당하는 아두이노 보드를 골라서 선택해주면 된다.

아두이노 esp32

6. 와이파이 연결

그리고 간단한 예제를 하나 업로드 해보자.

파일 -> 예제 -> 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를 조금 낮은 값으로 변경하면 안정적으로 잘된다.

upload

그리고 있는 그대로 예제 사이트로 접속하도록 하면 시리얼 모니터에서 html을 출력한다.

이렇게 까지만 한다면 1/3 정도는 완성한 것이다.

https Test

7. DHT-22 센서 연결

이제 아두이노에 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%까지 치솟았다.

serialMonitor

이제 아두이노 와이파이 연결과 센서 연결이 해결되었으니, 구글 스프레드 시트 설정으로 넘어가자.

8. 구글 스프레드 시트 설정

왠만하면 그냥 내가 글을 쓰는데, 구글 스프레드 시트 설정만 해도 길이가 너무 길어질 것 같아 예전에 썼던 글을 첨부한다.

아래의 링크에서 한번 확인해보길 바란다.

구글 스프레드 시트 설정 방법

9. 아두이노에서 Get요청 날리기

이제 구글 스프레드 시트 설정까지 끝났다면, 아두이노에서 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의 데이터를 받아 전송하는 것이다.

reqTest

10. 코드 합치기

fusion meme

이제 두 코드를 합쳐보자.

코드를 합치기 어렵다면 그냥 무지성으로 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이 뜨면 성공한 것.

이제 스프레드 시트에 들어가서 확인해면 된다.

스프레드 시트

11. 추가 작업

추가적으로 체감 온도도 기록하고 싶다면 구글 스크립트를 수정해주면 된다.

파라미터를 파싱하는 부분에서 변수를 추가하거나, 변수명을 변경할 수 있다.

...
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 = "지원되지 않는 파라미터 입니다.";
  }
...

물론 이렇게 해주면 아두이노 코드에서도 쿼리스트링을 변경해 주어야 한다.

어차피 개인 프로젝트이기에 이렇게 하는건 앞으로의 유지 보수를 위함이거나, 개인 취향인 것 같다.

12. 후기

과거에 학생들과 실험을 했을 때는 아래와 같은 데이터가 나왔다.

중간에 데이터 결측치는 밤에 전원이 차단되면서 생긴것...

그래도 간단한 장치만 만들어 놓으면 이렇게 자동으로 데이터를 수집해 주니, 이걸 우리는 분석만 하면 된다는 점에서는 무척 좋은 것 같다.

과거 데이터

만들어진 결과물만 보면 내용이 없어 보이는데 풀어 쓰다보니 글이 엄청나게 길어졌다.

16+1 수업에서 앞에 3시간은 C언어를, 뒤에 2시간은 아두이노 온습도계를, 나머지 시간은 개인 프로젝트를 진행할 예정인데 시간이 부족하지 않을까 걱정이 된다.

아무쪼록 열심히 준비한 수업이 잘 끝나고 여름방학을 맞이했으면 좋겠다.