7주 차 ESP32를 활용한 내장 LED 제어 및 DHT11 센서를 이용한 온습도 측정 웹 서버 구축 - park-02/2024-1_Smart-Devices GitHub Wiki
- 강의 요약
- 실습[2]
- 실습[2-1]
- 실습[3]
- 실습[4]
- 후기
- 인터넷 주소는 인터넷에서 국제 표준 방식에 의하여 일정한 통신규약에 따라 특정 정보시스템을 식별하여 접근할 수 있도록 하는 숫자, 문자, 부호 또는 이들의 조합으로 구성되는 정보체계입니다. IPv4와 IPv6가 공존합니다.
![](https://private-user-images.githubusercontent.com/163229907/320819454-07397ef8-de9a-4dba-bc05-2a49be7fed05.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUxODU5NTMsIm5iZiI6MTcyNTE4NTY1MywicGF0aCI6Ii8xNjMyMjk5MDcvMzIwODE5NDU0LTA3Mzk3ZWY4LWRlOWEtNGRiYS1iYzA1LTJhNDliZTdmZWQwNS5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwOTAxJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDkwMVQxMDE0MTNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1hYjA1NzgzZThlZTU3Njk1OTRjYmVhZDRhM2JkNDJlZTVjY2MyYzkwMDRmOWQwMjY3YzQ2OGIxYTI2ZGEzZTY0JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.279ngfTNnOEM0_3f6rQ14rB2dyRR8IqgEFTnIsY-9l8)
IPv4 주소는 전 세계적으로 약 43억 개로 제한되어 관리되는 유한한 주소체계입니다. 32비트의 주소체계를 사용하며, 8비트씩.(점)으로 구분한 필드 4개를 10진수 형태로 나타냅니다.
IPv4의 주소 자원 부족과 인터넷 보안의 강화를 위해 개발된 주소 체계입니다. IPv6 주소는 128비트로, 16비트씩 8부분으로 구분합니다. 각 필드는 :(콜론)으로 구분하고, 16진수를 이용하면 표현합니다.
- 사설 IP는 개인이나 조직 내부에서 사용되는 IP입니다.
- 일반적으로 가정이나 사무실 내부 네트워크에서 사용됩니다.
- 사설 IP 주소는 인터넷상에서 직접적으로 접속할 수 없으며, 로컬 네트워크 내에서만 유효합니다.
- 사설 IP 주소 범위는 특정한 IP 주소 대역으로 할당되어 있으며, 가장 흔히 사용되는 범위는 다음과 같습니다: 10.0.0.0 ~ 10.255.255.255, 172.16.0.0 ~ 172.31.255.255, 192.168.0.0 ~ 192.168.255.255
- 공인 IP는 인터넷 서비스 제공 업체(ISP)로부터 할당받은 고유한 인터넷 주소입니다.
- 인터넷상에서 직접적으로 접속할 수 있는 유일한 주소입니다.
- 공인 IP 주소는 전 세계적으로 고유하며, 모든 장치가 공유하고 있습니다.
- 한 공인 IP 주소로 여러 대의 기기가 인터넷에 접속할 수 있도록 하는 기술인 네트워크 주소 변환(NAT)을 통해 사설 IP와 연결됩니다.
![](https://private-user-images.githubusercontent.com/163229907/323648657-ea68b254-f75a-4397-ad8e-5f668d86112f.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUxODU5NTMsIm5iZiI6MTcyNTE4NTY1MywicGF0aCI6Ii8xNjMyMjk5MDcvMzIzNjQ4NjU3LWVhNjhiMjU0LWY3NWEtNDM5Ny1hZDhlLTVmNjY4ZDg2MTEyZi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwOTAxJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDkwMVQxMDE0MTNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT03ODFhZDU0YzU2MDNkMGQxNmNjNDAwMDE1MmM3MjQ1MDZhZjI5NzkxYjNhMzZlNjRlOWQ5Mjc2YjYwNjE2NjVjJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.uL70kYTPBh-aClQ-xYwrZlC0d1N_CiKq7ZHw7GgBCAQ)
- 공유기를 통해 여러 기기를 하나의 인터넷 연결로 공유함으로써 통신료를 줄일 수 있습니다.
- 공유기에 연결된 각 컴퓨터 또는 기기는 로컬 네트워크 내에서 고유한 사설 IP 주소를 가집니다.
- 사용자가 공유기의 공인 IP 주소로 접속하면 실제로는 공유기에 접속하게 되어, 공유기가 이후에 요청을 처리하고 적절히 라우팅합니다.
- 공유기는 하나의 공인 IP 주소를 받아서 여러 개의 사설 IP 주소로 변환하여 로컬 네트워크 내의 여러 기기들이 인터넷을 사용할 수 있도록 합니다.
- 공유기를 통해 연결된 각 컴퓨터나 기기는 사설 IP 주소를 할당받아서 인터넷을 사용합니다.
- 외부에서 공유기에 할당된 공인 IP 주소로 접속하면 공유기에 접속되어, 공유기는 요청을 내부 네트워크에 있는 적절한 기기로 라우팅합니다.
- 공유기를 통해 연결된 기기들은 외부에서 직접 접속할 수 없고, 공유기의 포트포워딩이나 DMZ 설정을 통해 특정 기기에만 접근할 수 있도록 구성할 수 있습니다.
- 포트포워딩은 공유기가 외부에서 접속한 사용자를 내부 네트워크에 있는 특정 서버로 연결하는 기능을 말합니다.
- 포트는 클라이언트가 특정 서버에 접속할 때 사용되는 번호이며, 외부에서 접속하는 클라이언트가 어떤 서비스에 접근하는지를 지정합니다.
- 웹 브라우저를 통해 서버에 접속할 때 사용되는 기본 포트는 HTTP의 경우 80번 포트이며, HTTPS의 경우 443번 포트입니다.
- 포트 포워딩은 데이터를 특정 포트를 통해 다른 컴퓨터로 전달하는 방법입니다.
- 이를 통해 외부에서 특정 포트에 접속할 때, 공유기는 해당 포트를 사용하는 서버로 데이터를 전송합니다.
- 포트 포워딩은 웹 서버, 데이터베이스 서버 등 다양한 용도로 사용되며, 외부에서 내부 네트워크에 있는 서버에 접속할 수 있도록 합니다.
포트 포워딩 대신에 DMZ라는 설정을 사용하는 경우가 많은데, DMZ를 사용하게 되면 하나의 컴퓨터로만 모든 포트를 포워딩하는 것과 같습니다. 그렇기 때문에 여러 컴퓨터에서 서비스나 서버를 운영하게 된다면 DMZ는 큰 문제가 될 수 있습니다. 또한 DMZ 설정은 결코 보안에도 좋지가 않기 때문에 반드시 필요한 포트만 찾아서 포워딩하는 것이 안전합니다.
- 포트는 각각의 프로그램이나 서비스가 인터넷상에서 데이터 통신을 수행하기 위해 사용하는 고유한 번호입니다.
- 웹 서버는 일반적으로 80번 포트에 설치되어 있어 웹 브라우저가 서버에 HTTP 요청을 보낼 때 80번 포트를 사용합니다.
- 포트가 필요한 이유는 컴퓨터에는 여러 프로그램이 설치되어 있고, 각 프로그램은 인터넷에 연결되어 데이터를 주고받아야 합니다. 이를 위해 포트를 사용하여 각 프로그램이 서로 구분되고 특정 프로그램에 데이터가 전송됩니다.
- 인터넷에 연결된 여러 애플리케이션 중 어떤 애플리케이션이 응답해야 하는지 명시하기 위해 포트 번호를 사용합니다.
- 포트 번호는 IP 주소와 함께 사용되어 특정 애플리케이션에 요청을 보낼 수 있도록 합니다.
- 웹 브라우저를 사용하여 웹 사이트에 접속할 때는 80번 포트를 사용합니다.
- 공유기 설정에서 내부 IP 주소 확인 가능합니다.
- 윈도우 명령 프롬프트에서 'ipconfig' 명령어로 확인 가능합니다.
- 무선으로 연결된 경우 무선 어댑터의 IPv4 주소를 확인합니다.
- 외부 포트는 공유기에 연결된 외부 네트워크에서 들어오는 데이터의 포트를 의미합니다.
- 내부 포트는 공유기와 서버로 구동하려는 컴퓨터가 연결된 포트를 가리킵니다.
- 포트 포워딩을 설정하면 외부에서 해당 외부 포트로 접속하는 사용자의 요청이 내부 포트로 전달됩니다.
- 사설 IP는 직접 사용자들이 접속할 수 없기 때문에 포트폴리다를 통해 접속해야 합니다.
- 사설 IP를 사용하면 한정된 공유 IP만으로도 여러 대의 컴퓨터를 사용할 수 있습니다.
- 사설 IP를 사용하면 외부에서 불필요하게 컴퓨터에 접속하는 것을 막을 수 있습니다.
이번 실습에서는 ESP32를 사용하여 내장 LED를 제어하는 웹 서버를 구축하고 실행했습니다.
- 라이브러리 매니저에서 'ESPAsyncWebSrv'를 검색하고 설치한다.
// Import required libraries
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebSrv.h>
const char* ssid = "U+NetC8B4";
const char* password = "1157004727";
// 현재 출력 상태를 저장하는 보조 변수
String output2State = "off";
// GPIO 핀에 출력 변수 할당
const int output2 = 2;
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<style>
html { font-family: Helvetica; display: inline-block;
margin: 0px auto; text-align: center; }
.button { background-color: #4CAF50; border: none;
color: white; padding: 16px 40px; text-decoration: none;
font-size: 30px; margin: 2px; cursor: pointer; }
.button2 { background-color: #555555; }
</style>
</head>
<body>
<h1>ESP32 웹 서버</h1>
<p>GPIO 2 - 상태 %STATE%</p>
%BUTTON%
</body>
</html>
)rawliteral";
// Replaces placeholder with button section in your web page
String processor(const String& var){
//Serial.println(var);
if(var == "STATE")
{
return output2State;
}
if(var == "BUTTON"){
String buttons = "";
if (output2State == "off")
{
buttons += "<p><a href=\"/2/on\"> \
<button class=\"button\">ON</button></a></p>";
}
else
{
buttons += "<p><a href=\"/2/off\"> \
<button class=\"button button2\">OFF</button></a></p>";
}
return buttons;
}
return String();
}
void setup(){
// Serial port for debugging purposes
Serial.begin(115200);
delay(3000);
// 출력 변수를 출력으로 초기화
pinMode(output2, OUTPUT);
// 출력을 LOW로 설정
digitalWrite(output2, LOW);
// SSID와 비밀번호로 Wi-Fi 네트워크에 연결
Serial.print("연결 중: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// 로컬 IP 주소 출력 및 웹 서버 시작
Serial.println("");
Serial.println("Wi-Fi 연결됨.");
Serial.println("IP 주소: ");
Serial.println(WiFi.localIP());
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});
// Send a GET request to <ESP_IP>/2/on
server.on("/2/on", HTTP_GET, [] (AsyncWebServerRequest *request) {
Serial.println("GPIO 2 ON");
output2State = "on";
digitalWrite(output2, HIGH);
request->send_P(200, "text/html", index_html, processor);
});
// Send a GET request to <ESP_IP>/2/off
server.on("/2/off", HTTP_GET, [] (AsyncWebServerRequest *request) {
Serial.println("GPIO 2 OFF");
output2State = "off";
digitalWrite(output2, LOW);
request->send_P(200, "text/html", index_html, processor);
});
// Start server
server.begin();
}
void loop() {
}
이 코드는 index_html이라는 문자열 배열에 HTML 코드를 저장하고 있습니다. 이 HTML 페이지는 ESP32에 연결된 GPIO(General Purpose Input/Output) 핀의 상태를 보여주고, 사용자가 웹 페이지를 통해 핀의 상태를 제어할 수 있게 해줍니다.
- const char[]는 문자열을 저장하는 배열을 선언하는 것입니다.
- 'R"rawliteral(...) 'rawliteral"' : 이 부분은 C++의 원시 문자열 리터럴(raw string literal)을 사용하는 것을 나타냅니다. 이는 문자열 내에서 특수 문자나 이스케이프 시퀀스를 사용하지 않고도 문자열을 그대로 저장할 수 있게 합니다. "rawliteral"은 사용자가 이 구조를 식별하기 위해 지정한 사용자 정의 태그입니다.
위 코드는 ESP32 웹 서버에서 동적으로 웹 페이지 내용을 생성하는 함수의 일부입니다. 이 함수의 이름은 processor입니다. 이 함수는 특정 플레이스 홀더(placeholder)를 실제 버튼 섹션으로 교체하는 역할을 합니다. 따라서 이 함수는 웹 페이지에서 동적 내용을 생성할 때 사용됩니다. 이 함수를 통해 웹 페이지에서 사용자의 입력에 따라 동적으로 콘텐츠를 변경할 수 있으며, 특히 GPIO의 상태를 변경하는 버튼을 통해 사용자가 하드웨어를 원격으로 제어할 수 있습니다. 이는 IoT 장치에서 자주 사용되는 패턴으로, 사용자 인터페이스와 실제 장치 상태 간의 상호 작용을 원활하게 합니다.
- String processor (const string& var) : 이 함수는 String 타입의 var 변수를 매개변수로 받아들이며, 해당 변수에 따른 문자열 결과를 반환합니다
- 플레이스 홀더 처리 : 코드 내에서 if 문을 사용하여 var의 값에 따라 다양한 작업을 수행합니다.
- if(var == "STATE")는 var가 "STATE"인 경우를 체크합니다. 그리고 해당 조건이 참이면 "output2state" 변수의 값을 반환합니다. "output2state"는 GPIO 핀의 상태를 나타내는 변수로, 일반적으로 "on" 또는 "off" 값이 될 수 있습니다.
- if(var == "BUTTON")은 var가 "BUTTON"일 경우를 확인합니다. 이때, 버튼과 관련된 HTML 코드를 생성합니다. 코드는 빈 문자열 'String button ="";'로 시작하며, 이후에 동적으로 버튼을 추가합니다.
- GPIO 상태('output2State')가 "off"인 경우에는 "ON" 버튼을 생성하고, 사용자가 해당 버튼을 클릭하면 "/2/on" 경로로 요청이 전송됩니다. 이는 ESP32에게 해당 GPIO를 "on" 상태로 변경하도록 요청하는 것입니다.
- 반대로, GPIO 상태가 "on"인 경우에는 "OFF" 버튼을 생성하고, 사용자가 해당 버튼을 클릭하면 "/2/off" 경로로 요청이 전송됩니다. 이 버튼은 "button button2"라는 다른 CSS 클래스를 사용하여 스타일이 다르게 표시됩니다.
이 코드는 웹 서버를 통해 ESP32의 특정 GPIO 핀을 "ON"과 "OFF" 상태로 전환하는 웹 인터페이스를 제공합니다. 사용자는 웹 브라우저를 통해 각 경로로 접속하여 GPIO 핀의 상태를 제어할 수 있으며, 웹 페이지는 이 상태 변경을 반영하여 사용자에게 표시합니다.
- server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", index_html, processor); });클라이언트가 웹 서버의 루트 경로("/")로 HTTP GET 요청을 보내면, HTTP 상태 코드 200과 함께 'index_html'에 저장된 HTML 콘텐츠를 클라이언트에게 전송합니다. "processor" 함수는 HTML 내의 동적 내용을 처리하는 데 사용됩니다.
- server.on("/2/on", HTTP_GET, [] (AsyncWebServerRequest *request) { Serial.println("GPIO 2 ON"); output2State = "on"; digitalWrite(output2, HIGH); request->send_P(200, "text/html", index_html, processor); }); 클라이언트가 "/2/on" 경로로 HTTP GET 요청을 보내면, 시리얼 모니터에 "GPIO 2 ON"을 출력하고, output2State 변수를 "on"으로 설정합니다. "digitalWrite(output2, HIGH);"를 통해 GPIO 핀을 높은 전압(HIGH)으로 설정하며, 이는 핀을 "ON" 상태로 만듭니다. 그 후 HTML 콘텐츠를 클라이언트에게 전송합니다.
- server.on("/2/off", HTTP_GET, [] (AsyncWebServerRequest *request) { Serial.println("GPIO 2 OFF"); output2State = "off"; digitalWrite(output2, LOW); request->send_P(200, "text/html", index_html, processor); }); 클라이언트가 "/2/off" 경로로 HTTP GET 요청을 보내면, 시리얼 모니터에 "GPIO 2 OFF"을 출력하고 output2State 변수를 "off"로 설정합니다. "digitalWrite(output2, LOW);"를 통해 GPIO 핀을 낮은 전압(LOW)으로 설정하며, 이는 핀을 "OFF" 상태로 만듭니다. 그 후 HTML 콘텐츠를 클라이언트에게 전송합니다.
- ESP32 보드를 컴퓨터에 연결합니다.
- Arduino IDE에 소스코드를 컴파일하고 업로드합니다.
KakaoTalk_20240417_201108671.mp4
- ESP32 보드를 웹 서버로 설정한 후, 웹 브라우저에서 해당 서버에 접속하면 LED를 제어할 수 있는 웹 페이지가 나타납니다. 웹 페이지에서 LED를 켜고 끄는 등의 동작을 수행할 수 있으며, 실시간으로 LED의 상태를 확인할 수 있습니다.
- 이번 코드는 위 코드와 다르게 HTML, CSS 등 복잡한 요소를 제외하고, 간단하게 웹 브라우저를 통해 주소 뒤에 "/2/on"을 추가하면 GPIO 핀이 켜지고, "/2/off"를 추가하면 해당 핀이 꺼지는 기능을 제공하는 코드입니다.
- 라이브러리 매니저에서 'ESPAsyncWebServer'를 검색하고 설치한다.
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
const char* ssid = "U+NetC8B4"; // WiFi 네트워크 이름
const char* password = "1157004727"; // WiFi 비밀번호
String output2State = "off"; // GPIO 핀의 초기 상태
const int output2 = 2; // GPIO 핀 번호
AsyncWebServer server(80); // 서버 포트 설정
void setup() {
Serial.begin(115200);
pinMode(output2, OUTPUT); // GPIO 핀을 출력으로 설정
digitalWrite(output2, LOW); // 초기 상태를 LOW로 설정
// WiFi에 연결
WiFi.begin(ssid, password);
Serial.print("WiFi 연결 중");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("연결된 WiFi 네트워크: ");
Serial.println(WiFi.SSID());
Serial.print("IP 주소: ");
Serial.println(WiFi.localIP());
// 루트 경로 설정
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
String message = "GPIO 2 현재 상태: " + output2State;
request->send(200, "text/plain; charset=utf-8", message); // Content-Type에 charset=utf-8 추가
});
// GPIO 핀 ON 설정 경로
server.on("/2/on", HTTP_GET, [] (AsyncWebServerRequest *request) {
Serial.println("GPIO 2 ON");
output2State = "on";
digitalWrite(output2, HIGH);
String message = "GPIO 2 변경됨: ON";
request->send(200, "text/plain; charset=utf-8", message); // Content-Type에 charset=utf-8 추가
});
// GPIO 핀 OFF 설정 경로
server.on("/2/off", HTTP_GET, [] (AsyncWebServerRequest *request) {
Serial.println("GPIO 2 OFF");
output2State = "off";
digitalWrite(output2, LOW);
String message = "GPIO 2 변경됨: OFF";
request->send(200, "text/plain; charset=utf-8", message); // Content-Type에 charset=utf-8 추가
});
server.begin(); // 웹 서버 시작
}
void loop() {
}
![](https://private-user-images.githubusercontent.com/163229907/324750789-9839368a-660c-4e44-81e3-3bc67492475f.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUxODU5NTMsIm5iZiI6MTcyNTE4NTY1MywicGF0aCI6Ii8xNjMyMjk5MDcvMzI0NzUwNzg5LTk4MzkzNjhhLTY2MGMtNGU0NC04MWUzLTNiYzY3NDkyNDc1Zi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwOTAxJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDkwMVQxMDE0MTNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT03ZWI5NGE1YjM3ZjE0MzBkYWU1MmUxZTkwOGY3MTYwMjFjZDA3MjYzNDZjYzY5YjdhYTcxMGZjOGRjZmM0MWFjJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.HX0QIHaBPGyNs3luq18pe0rvi9Cyvtcj878UoAmEpkM)
코드를 컴파일하고 나오는 IP 주소는 192.168.123.103입니다. 이 주소를 웹 브라우저의 주소창에 붙여넣으면 웹 페이지가 표시됩니다. 이 웹 페이지에서 GPIO 핀을 켜거나 끄려면 다음과 같이 해야 합니다:
- GPIO 핀을 켜려면: 192.168.123.103/2/on
- GPIO 핀을 끄려면: 192.168.123.103/2/off
이렇게 하면 해당 GPIO 핀의 상태가 변경됩니다.
KakaoTalk_20240423_170514462.mp4
- 이 코드는 람다 함수를 사용하지 않는 버전입니다.
이번 실습에서는 DHT11 센서를 사용하여 온도와 상대습도를 측정하는 실습을 진행했습니다. 이 센서는 실내 및 외부 환경의 온도와 습도를 측정할 수 있습니다.
![](https://private-user-images.githubusercontent.com/163229907/314496720-bd28c4e3-1b89-4b0d-985d-176ae33d4fe7.jpg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUxODU5NTMsIm5iZiI6MTcyNTE4NTY1MywicGF0aCI6Ii8xNjMyMjk5MDcvMzE0NDk2NzIwLWJkMjhjNGUzLTFiODktNGIwZC05ODVkLTE3NmFlMzNkNGZlNy5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwOTAxJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDkwMVQxMDE0MTNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1lYzRhMmU3ZmU0ZTQ4NDQ4ZWE1MzAzMWU1ZGZjMDBmNjQ2ZWRjNjkyMWJkNTE3NzdmN2U5YTJlZjRkMzU2MWE1JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.S4KbKNlHbF8bXJKBSAdjUfm13MzG-yz93Z4gG2K2UnY)
![](https://private-user-images.githubusercontent.com/163229907/323207499-1c1ee7fe-4728-4b36-9395-2968d9cb9c4f.jpg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUxODU5NTMsIm5iZiI6MTcyNTE4NTY1MywicGF0aCI6Ii8xNjMyMjk5MDcvMzIzMjA3NDk5LTFjMWVlN2ZlLTQ3MjgtNGIzNi05Mzk1LTI5NjhkOWNiOWM0Zi5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwOTAxJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDkwMVQxMDE0MTNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1iMjdhMWM1MTM4ODI2Y2NmYTI0YjQ0MDY4YmFhMWJjYmE5NTNhMDdlNjUxYWNjNzc4NDYxYjg3NWI4YjI4YzljJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.EXV1h7Il3oYhkNtm277kT1caLIPierS2Zb0ZzSfChqY)
![](https://private-user-images.githubusercontent.com/163229907/314496046-7a5e9325-1635-47e8-b5da-4fb233fc5eda.jpg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUxODU5NTMsIm5iZiI6MTcyNTE4NTY1MywicGF0aCI6Ii8xNjMyMjk5MDcvMzE0NDk2MDQ2LTdhNWU5MzI1LTE2MzUtNDdlOC1iNWRhLTRmYjIzM2ZjNWVkYS5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwOTAxJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDkwMVQxMDE0MTNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1mMjIzZjhjYmZhMjc4NDdiZGM2YWQ0ZTg5ODhlZDZhYTBmZTdhZmQwMWE5MjJmNWRhYzI0YWQ0ZTZiZTA5ZjkzJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.Y2ljXoZ4rIGH8-be6SnD_bAGU9hznp9ufCHf-FCJRn8)
![](https://private-user-images.githubusercontent.com/163229907/323207743-6cf37d4d-89aa-46c3-80d4-4f23377bb662.jpg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUxODU5NTMsIm5iZiI6MTcyNTE4NTY1MywicGF0aCI6Ii8xNjMyMjk5MDcvMzIzMjA3NzQzLTZjZjM3ZDRkLTg5YWEtNDZjMy04MGQ0LTRmMjMzNzdiYjY2Mi5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwOTAxJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDkwMVQxMDE0MTNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0yZjFmN2I3NGFkNjgwNGI3NjhiYTljZDViZWQyMjcwYWQwNTRmNjY1YWMwNjFjZGRhOTZhYjY4YWU3MTUyZjVjJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.ncDCJnJgf04zw8-HQEmbwAttFjDirvSEJLUAlJcZcvE)
- 라이브러리 매니저에서 'DHT sensor library by Adafruit'를 검색하고 설치한다.
#include <DHT.h>
#define DHTPIN 18 // DHT11 센서에 연결된 핀 번호
#define DHTTYPE DHT11 // DHT 센서 유형
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(115200);
dht.begin();
}
void loop() {
delay(2000); // 2초마다 센서 값을 읽습니다.
// 온도 및 습도 값 읽기
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
// 센서 값 출력
Serial.print("온도: ");
Serial.print(temperature);
Serial.print(" °C, 습도: ");
Serial.print(humidity);
Serial.println(" %");
}
- ESP32 보드를 컴퓨터에 연결합니다.
- Arduino IDE에 소스코드를 컴파일하고 업로드합니다.
- DHT11 센서를 아두이노에 연결하고 코드를 실행했을 때, 시리얼 모니터에 온도와 습도 값이 출력되었습니다.
이번 실습에서는 DHT11 센서를 사용하여 온도와 습도를 측정하고, 이 값을 웹 서버를 통해 실시간으로 표시하는 예제를 실행하였습니다.
![](https://private-user-images.githubusercontent.com/163229907/314496720-bd28c4e3-1b89-4b0d-985d-176ae33d4fe7.jpg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUxODU5NTMsIm5iZiI6MTcyNTE4NTY1MywicGF0aCI6Ii8xNjMyMjk5MDcvMzE0NDk2NzIwLWJkMjhjNGUzLTFiODktNGIwZC05ODVkLTE3NmFlMzNkNGZlNy5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwOTAxJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDkwMVQxMDE0MTNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1lYzRhMmU3ZmU0ZTQ4NDQ4ZWE1MzAzMWU1ZGZjMDBmNjQ2ZWRjNjkyMWJkNTE3NzdmN2U5YTJlZjRkMzU2MWE1JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.S4KbKNlHbF8bXJKBSAdjUfm13MzG-yz93Z4gG2K2UnY)
![](https://private-user-images.githubusercontent.com/163229907/323207499-1c1ee7fe-4728-4b36-9395-2968d9cb9c4f.jpg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUxODU5NTMsIm5iZiI6MTcyNTE4NTY1MywicGF0aCI6Ii8xNjMyMjk5MDcvMzIzMjA3NDk5LTFjMWVlN2ZlLTQ3MjgtNGIzNi05Mzk1LTI5NjhkOWNiOWM0Zi5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwOTAxJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDkwMVQxMDE0MTNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1iMjdhMWM1MTM4ODI2Y2NmYTI0YjQ0MDY4YmFhMWJjYmE5NTNhMDdlNjUxYWNjNzc4NDYxYjg3NWI4YjI4YzljJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.EXV1h7Il3oYhkNtm277kT1caLIPierS2Zb0ZzSfChqY)
![](https://private-user-images.githubusercontent.com/163229907/314496046-7a5e9325-1635-47e8-b5da-4fb233fc5eda.jpg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUxODU5NTMsIm5iZiI6MTcyNTE4NTY1MywicGF0aCI6Ii8xNjMyMjk5MDcvMzE0NDk2MDQ2LTdhNWU5MzI1LTE2MzUtNDdlOC1iNWRhLTRmYjIzM2ZjNWVkYS5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwOTAxJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDkwMVQxMDE0MTNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1mMjIzZjhjYmZhMjc4NDdiZGM2YWQ0ZTg5ODhlZDZhYTBmZTdhZmQwMWE5MjJmNWRhYzI0YWQ0ZTZiZTA5ZjkzJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.Y2ljXoZ4rIGH8-be6SnD_bAGU9hznp9ufCHf-FCJRn8)
![](https://private-user-images.githubusercontent.com/163229907/323207743-6cf37d4d-89aa-46c3-80d4-4f23377bb662.jpg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjUxODU5NTMsIm5iZiI6MTcyNTE4NTY1MywicGF0aCI6Ii8xNjMyMjk5MDcvMzIzMjA3NzQzLTZjZjM3ZDRkLTg5YWEtNDZjMy04MGQ0LTRmMjMzNzdiYjY2Mi5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwOTAxJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDkwMVQxMDE0MTNaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0yZjFmN2I3NGFkNjgwNGI3NjhiYTljZDViZWQyMjcwYWQwNTRmNjY1YWMwNjFjZGRhOTZhYjY4YWU3MTUyZjVjJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.ncDCJnJgf04zw8-HQEmbwAttFjDirvSEJLUAlJcZcvE)
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebSrv.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
const char* ssid = "U+NetC8B4";
const char* password = "1157004727";
#define DHTPIN 18 // DHT 센서에 연결된 디지털 핀
#define DHTTYPE DHT11
// 온습도 센서 객체 생성
DHT dht(DHTPIN, DHTTYPE);
// 80번 포트에서 AsyncWebServer 객체 생성
AsyncWebServer server(80);
// DHT 온도 읽기 함수
String readDHTTemperature() {
// 센서 읽기는 최대 2초가 소요될 수 있습니다. (센서가 매우 느립니다.)
// 기본값으로 섭씨로 온도 읽기
float t = dht.readTemperature();
// 화씨로 온도 읽으려면 매개 변수를 true로 설정
//float t = dht.readTemperature(true);
// 읽기 실패 시 조기에 종료하고 다시 시도합니다.
if (isnan(t)) {
Serial.println("DHT 센서에서 읽기 실패!");
return "--";
}
else {
Serial.println(t);
return String(t);
}
}
// DHT 습도 읽기 함수
String readDHTHumidity() {
// 센서 읽기는 최대 2초가 소요될 수 있습니다. (센서가 매우 느립니다.)
float h = dht.readHumidity();
if (isnan(h)) {
Serial.println("DHT 센서에서 읽기 실패!");
return "--";
}
else {
Serial.println(h);
return String(h);
}
}
// 인덱스 HTML 페이지
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.7.2/css/all.css"
integrity=
"sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr"
crossorigin="anonymous">
<style>
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h2 { font-size: 3.0rem; }
p { font-size: 3.0rem; }
.units { font-size: 1.2rem; }
.dht-labels {
font-size: 1.5rem;
vertical-align: middle;
padding-bottom: 15px;
}
</style>
</head>
<body>
<h2>ESP32 DHT Server</h2>
<p>
<i class="fas fa-thermometer-half" style="color:#059e8a;"></i>
<span class="dht-labels">온도</span>
<span id="temperature">%TEMPERATURE%</span>
<sup class="units">°C</sup>
</p>
<p>
<i class="fas fa-tint" style="color:#00add6;"></i>
<span class="dht-labels">습도</span>
<span id="humidity">%HUMIDITY%</span>
<sup class="units">%</sup>
</p>
</body>
<script>
setInterval(function() {
// XMLHttpRequest 객체를 사용하여 "/temperature" 엔드포인트로 GET 요청을 전송
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// 요청이 완료되고 성공적인 응답을 받았을 때, 온도 값을 업데이트합니다.
document.getElementById("temperature").innerHTML = this.responseText;
}
};
xhttp.open("GET", "/temperature", true);
xhttp.send();
}, 2000); // 2초 간격으로 업데이트합니다.
setInterval(function() {
// XMLHttpRequest 객체를 사용하여 "/humidity" 엔드포인트로 GET 요청을 전송
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// 요청이 완료되고 성공적인 응답을 받았을 때, 습도 값을 업데이트합니다.
document.getElementById("humidity").innerHTML = this.responseText;
}
};
xhttp.open("GET", "/humidity", true);
xhttp.send();
}, 2000); //2초 간격으로 업데이트합니다.
</script>
</html>
)rawliteral";
// 플레이스홀더를 DHT 값으로 대체하는 함수
String processor(const String& var){
if(var == "TEMPERATURE"){
return readDHTTemperature();
}
else if(var == "HUMIDITY"){
return readDHTHumidity();
}
return String();
}
void setup(){
// 디버깅용 시리얼 포트 설정
Serial.begin(115200);
dht.begin();
// Wi-Fi 연결
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Wi-Fi에 연결 중...");
}
// ESP32 로컬 IP 주소 출력
Serial.println(WiFi.localIP());
// 루트(/) 웹 페이지 라우팅
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});
// "/temperature" 엔드포인트에 대한 GET 요청 처리
server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", readDHTTemperature().c_str());
});
// "/humidity" 엔드포인트에 대한 GET 요청 처리
server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", readDHTHumidity().c_str());
});
// 서버 시작
server.begin();
}
void loop(){
}
- ESP32 보드를 컴퓨터에 연결합니다.
- Arduino IDE에 소스코드를 컴파일하고 업로드합니다.
- 사용자가 웹 페이지에서 다른 동작을 하지 않는 한, 온도 및 습도 값은 계속해서 실시간으로 업데이트됩니다.
이번 실습을 통해 ESP32를 이용하여 웹페이지를 만들고, 해당 웹페이지에서 LED를 제어하고 ESP32와 DHT11 센서를 활용하여 온도와 습도를 측정하고, 이를 웹 서버를 통해 실시간으로 모니터링하는 과정을 배울 수 있었습니다. ESP32의 다양한 기능을 활용하여 웹 서버를 구축하는 과정에서 하드웨어와 소프트웨어 간의 상호작용에 대한 이해도가 높아졌습니다. 포트포워딩 및 사설 IP와 같은 네트워크 관련 개념을 이해하고 적용하는 과정에서 네트워크 구성 및 보안에 대한 중요성을 다시 한번 깨달을 수 있었습니다. 이러한 경험을 통해 더 다양한 시도해 보고 싶습니다.