ESP-01 모듈과 DHT22 센서 연동 트러블 슈팅: 하드웨어 뉴비의 눈물겨운 웹 서버 구축기
우리 집 펫테일 게코 솜이 의 사육장 온습도를 실시간으로 확인하는 데스크톱 위젯을 만들고 싶어서 아두이노 웹 서버를 구축하기로 했다.
부품 선정부터 최종 데이터를 JSON으로 뽑아내기까지 겪었던 수많은 트러블 슈팅 과정을 기록해 본다.
0. 부품 선정: 왜 이 조합인가?
프로젝트를 시작하며 가장 먼저 고민한 것은 부품 선정이었다.
- 온습도 센서 (DHT11 vs DHT22): 저렴한 파란색 DHT11 도 있지만, 하얀색의 DHT22 는 영하 온도까지 잴 수 있고 센서 자체가 좀 더 안정적이라는 이야기가 있어, 이왕 사는 것 좀 더 좋은 센서를 사보았다.
- 무선 통신 모듈 (ESP-01 + 어댑터 모듈): 와이파이 통신을 위해 가장 저렴하고 대중적인 ESP-01 모듈을 선택했다. 하지만 이 녀석은 핀 배열이 브레드보드에 꽂을 수 없는 형태이고, 전압도 3.3V를 써서 5V인 아두이노 우노와 직접 연결하기 까다롭다. 그래서 5V 전원을 3.3V로 변환해주고 핀 배열도 일자로 펴주는 ESP-01 어댑터 모듈 을 함께 구매했다. 이 어댑터 덕분에 아두이노의 5V와 직접 연결할 수 있게 되었다.
1. 시작 전의 삽질: M0 보드 착각과 CH340 드라이버
아두이노 IDE를 이용해 코드를 업로드하려고 하는데, 계속 업로드 에러가 발생했다. 분명 이전에 아두이노를 이용해서 시리얼 통신 테스트도 했던 터라 이상하다고 생각했다. 아두이노를 산 홈페이지에서 Arduino Uno 호환 보드라고 해서 Arduino Uno로 보드를 맞추고 포트를 연결했었는데, 계속 코드 업로드를 실패했다.
그래서 제미나이에게 도움을 청하니 내 보드가 Arduino M0 (Zero) 계열일 수도 있다는 조언이 돌아왔다. Arduino M0는 “Programming Port”와 “Native USB Port”가 따로 있다고 해서 좀 이상하다고는 생각했지만(내 보드에는 실제 포트가 하나밖에 없었다.), 일단 어떻게든 인식시켜 보려고 리셋 버튼을 더블 클릭해 강제로 부트로더 모드에 진입하려 애썼지만, 보드는 묵묵부답이었다.
이건 좀 아닌 것 같아서 제미나이에게 이미지까지 보여주며 정말 M0가 맞냐고 물었는데, 제미나이는 이미지를 확인하더니 Arduino Uno 호환보드라는 명쾌한(…) 답을 내려줬다. 아니 계속 그렇게 작업했었는데 업로드가 안되었다니까…!
그래서 혹시나 하는 마음에 CH340 드라이버를 재설치 해봤다. 그랬더니 보드 설정을 Arduino Uno로 했을 때 정상적으로 코드가 업로드 되고 시리얼 모니터도 확인이 가능했다. 시작 전 부터 삽질을 했지만, 본격적인 삽질은 이제부터 시작이였다.🙃
2. ESP-01 통신 속도 변경 (115200 -> 9600)
코드 업로드는 완료했지만, 웹 서버를 여는 로직은 동작하지 않았다. 가장 처음에 맞딱드린 문제는 ESP-01 모듈과 우노 보드 가 서로 통신을 못했던 것이였다. ESP-01 의 공장 초기 속도는 115200 인데, 아두이노 우노의 SoftwareSerial 로 이 속도를 감당하려니 글자가 다 깨지는 현상이 발생했다.
안정적인 통신을 위해 모듈의 속도를 9600 으로 낮춰야 했다. 이를 위해 PC의 시리얼 모니터 입력을 그대로 ESP-01로 전달해 주는 ‘통신 중계용 코드’를 작성했다.
아두이노 통신 속도 변경용(Passthrough) 코드
#include <SoftwareSerial.h>
// 우노의 2번(RX), 3번(TX) 핀 사용
SoftwareSerial espSerial(2, 3);
void setup() {
Serial.begin(9600); // PC 시리얼 모니터 속도
espSerial.begin(115200); // 초기 ESP-01 모듈의 속도
Serial.println("AT 명령어 입력 대기 중...");
}
void loop() {
// ESP-01에서 오는 응답을 PC 모니터에 출력
if (espSerial.available()) {
Serial.write(espSerial.read());
}
// PC 모니터에서 입력한 명령어를 ESP-01로 전송
if (Serial.available()) {
espSerial.write(Serial.read());
}
}
이 코드를 올리고 시리얼 모니터에서 설정을 ‘Both NL & CR (새 줄 및 캐리지 리턴)’ 과 ‘9600 보드레이트’ 로 바꾸고 통신을 확인했다. 내 모듈은 구형 펌웨어(0.9.5)여서 통신 속도를 바꾸는 최신 명령어가 먹히지 않아 고생했지만, 아래의 명령어들을 활용해 무사히 9600으로 속도를 맞추고 수동 연결 테스트까지 진행할 수 있었다.
주요 AT 명령어 모음
AT: 모듈 정상 작동 확인 (응답: OK)AT+CIOBAUD=9600: 통신 속도를 9600으로 변경 (구형 펌웨어용)AT+CWMODE=1: 와이파이 클라이언트(Station) 모드로 설정AT+CWLAP: 주변 와이파이 목록 검색AT+CWJAP="와이파이이름","비밀번호": 수동으로 공유기 와이파이에 연결
3. 뼈아픈 가짜 성공: IP 주소 0.0.0.0 에러와 공유기 설정
이제 본격적으로 와이파이를 잡을 차례. 널리 쓰이는 WiFiEsp 라이브러리를 사용하려고 했지만, 내 모듈이 구형 펌웨어라 그런지 도무지 말을 듣지 않았다.
우선 ESP-01 모듈은 최신 5GHz 와이파이를 아예 인식하지 못하고, 오직 2.4GHz 와이파이 에만 연결할 수 있다고 하여서 처음엔 핸드폰 핫스팟 을 켜서 밴드를 2.4로 변경하고 설정을 변경하고 연결을 시도했는데, 연결이 아예 되지 않았다.
그래서 통신 속도를 변경했을 때 썼던 통신 속도 변경용 코드와 AT 명령어를 이용 해 주변 와이파이 목록을 검색해봤다. 그랬더니 우리 집 공유기와 핫스팟 빼고 다른 집 와이파이 목록은 다 나오는 것이였다…
와이파이 목록이 안나오는 문제는 와이파이 채널과 관련이 있다고 한다. 한국은 와이파이 채널을 1번 부터 13번 까지 사용하는데, 해외에서 생산된 ESP-01 은 북미 기준에 맞춰 1번 부터 11번 채널까지만 스캔하도록 펌웨어가 제한 된 경우가 아주 많다고 한다. 와이파이 목록을 반복해서 조회하니 휴대폰 핫스팟이 목록에 떴다 안떴다를 했는데, 아마 핫스팟을 껐다 켰다 하면서 채널이 바뀌어서 그랬던게 아닌지 짐작해본다.
채널을 수동으로 설정할 수 없는 휴대폰 핫스팟은 포기하고, 어차피 나중에는 공유기에 연결해야하니 공유기 관리자 페이지로 들어가서 집 공유기의 2.4GHz 채널을 1번으로 고정하기로 했다. 혹시 유선 인터넷이 끊길까봐 게임 중이던 남편에게 다급히 인터넷 끊기는게 괜찮냐는 허락을 받기도 했다(다행히 유선 인터넷은 끊기지 않았다). 공유기 채널을 바꾼 뒤 다시 시도하니 와이파이 목록에 집 공유기 와이파이가 떴고, AT+CWJAP 명령어를 이용해서 와이파이 연결도 성공했다(DHCP 할당 내역에 떴음!).
하지만 AT 명령어를 이용해서는 연결이 잘되었는데, WiFiEsp 라이브러리를 사용한 본 코드에서는 연결이 제대로 되지 않아, 할당받은 IP 주소가 계속 0.0.0.0 으로 나왔다.
4. 라이브러리를 버리다: 순정 AT 명령어 웹 서버 구축
결국 WiFiEsp 라이브러리 사용을 포기하기로 했다. 바닐라 자바스크립트를 쓰는 것처럼, 아두이노가 직접 AT 명령어 를 타이핑하여 와이파이에 붙고 웹 서버를 돌리도록 아래와 같이 최종 코드를 작성했다.
최종 아두이노 HTTP JSON 서버 코드
#include <SoftwareSerial.h>
#include <DHT.h>
// 핀 설정
SoftwareSerial espSerial(2, 3); // RX, TX
#define DHTPIN 4 // 온습도 센서 핀
#define DHTTYPE DHT22 // 센서 종류
DHT dht(DHTPIN, DHTTYPE);
String ssid = "와이파이이름";
String pass = "비밀번호";
void setup() {
Serial.begin(9600);
espSerial.begin(9600);
dht.begin();
Serial.println("🚀 순정 AT 명령어로 서버 시작...");
// 1. 와이파이 모드 설정
sendCommand("AT+CWMODE=1", 2000);
// 2. 와이파이 연결 시도
Serial.println("와이파이 연결 중...");
String connectCmd = "AT+CWJAP=\"" + ssid + "\",\"" + pass + "\"";
sendCommand(connectCmd, 15000);
// 3. IP 주소 확인 (진짜 IP 출력)
Serial.println("할당받은 IP 주소 확인:");
sendCommand("AT+CIFSR", 3000);
// 4. 웹 서버 모드 켜기 (포트 80)
sendCommand("AT+CIPMUX=1", 1000);
sendCommand("AT+CIPSERVER=1,80", 1000);
Serial.println("✅ 서버 세팅 완료! 브라우저 접속 대기 중...");
}
void loop() {
if (espSerial.available()) {
if (espSerial.find("+IPD,")) {
delay(500);
int connectionId = espSerial.read() - 48;
float t = dht.readTemperature();
float h = dht.readHumidity();
String json = "{\"temperature\": " + String(t) + ", \"humidity\": " + String(h) + "}";
String webpage = "HTTP/1.1 200 OK\r\n";
webpage += "Content-Type: application/json\r\n";
webpage += "Access-Control-Allow-Origin: *\r\n"; // CORS 에러 방지용
webpage += "Connection: close\r\n\r\n";
webpage += json;
String cipSend = "AT+CIPSEND=" + String(connectionId) + "," + String(webpage.length()) + "\r\n";
sendCommand(cipSend, 1000);
espSerial.print(webpage);
delay(1000);
String closeCommand = "AT+CIPCLOSE=" + String(connectionId) + "\r\n";
sendCommand(closeCommand, 1000);
}
}
}
void sendCommand(String command, const int timeout) {
espSerial.print(command + "\r\n");
long int time = millis();
while ((time + timeout) > millis()) {
while (espSerial.available()) {
Serial.print((char)espSerial.read());
}
}
}
5. 최종 보스 발발: NAN 데이터와 busy p… 에러
와이파이 연결에 성공하고 기뻐한 것도 잠시, DHT22 온습도 센서를 연결하자 브라우저에 출력되는 데이터가 NAN (Not a Number) 으로 떴다. 센서 선을 다시 꽂아보았더니 이번에는 시리얼 모니터에 busy p… 에러가 무한 반복되며 모듈이 완전히 뻗어버렸다.
원인은 전력 부족과 하드웨어 물리적 충돌 이었다. 아두이노 우노 보드에 있는 5V 구멍 두 곳에 와이파이 모듈과 온습도 센서를 각각 나누어 꽂은 것이 화근이었다. 보드에서 안정적으로 5V 출력을 내어주는 진짜 전원 핀은 1개뿐인데, 다른 핀을 잘못 사용하여 전력이 턱없이 부족해진 것이다. 센서가 전기를 다 빨아먹으니, 와이파이 통신을 해야 하는 ESP-01 은 기절해버리며 에러를 뱉어냈다.
해결 방법: 브레드보드 전원 버스 구성 (최종 회로도)
단일 스토어에서 상태 관리를 하듯, 하드웨어 전원도 통일된 분배가 필요했다. 아두이노의 ‘진짜 5V’ 핀 1개를 브레드보드로 뽑아 병렬로 나누어 주었다.
[ 최종 하드웨어 연결 도식 ]
[ 아두이노 UNO (CH340) ]
5V -------------------> [ 브레드보드 (+) 전원 라인 ]
GND -------------------> [ 브레드보드 (-) 전원 라인 ]
D2 (RX) <--------------- ESP-01 어댑터 TX
D3 (TX) ---------------> ESP-01 어댑터 RX
D4 (데이터) -------------> DHT22 센서 DATA (OUT)
[ 브레드보드 전원 버스(병렬 분배) ]
(+) 라인 ---------------> ESP-01 어댑터 VCC (5V)
(+) 라인 ---------------> DHT22 센서 VCC (+)
(-) 라인 ---------------> ESP-01 어댑터 GND
(-) 라인 ---------------> DHT22 센서 GND (-)
마무리하며
브레드보드 로 전원을 분배하고 나니, 통신하는 기기들의 0V 기준점(GND)이 하나로 통일되면서 전력 부족과 노이즈가 완벽히 사라졌다. 브라우저 주소창에 할당받은 IP를 입력하자, 사육장의 온도와 습도가 예쁜 JSON 포맷으로 출력되었다!
{"temperature": 27.5, "humidity": 45.0} 이 포스팅은 AI의 도움을 받아 초안을 작성하고, 직접 검수 및 편집한 글입니다.