7주 차 ESP32를 활용한 내장 LED 제어 및 DHT11 센서를 이용한 온습도 측정 웹 서버 구축 - park-02/2024-1_Smart-Devices GitHub Wiki

목차

  • 강의 요약
  • 실습[2]
  • 실습[2-1]
  • 실습[3]
  • 실습[4]
  • 후기

강의 요약

1. IP 주소

1-1 IP(Internet protocol) 주소의 개념

  • 인터넷 주소는 인터넷에서 국제 표준 방식에 의하여 일정한 통신규약에 따라 특정 정보시스템을 식별하여 접근할 수 있도록 하는 숫자, 문자, 부호 또는 이들의 조합으로 구성되는 정보체계입니다. IPv4와 IPv6가 공존합니다.

1-2 IPv4

IPv4 주소는 전 세계적으로 약 43억 개로 제한되어 관리되는 유한한 주소체계입니다. 32비트의 주소체계를 사용하며, 8비트씩.(점)으로 구분한 필드 4개를 10진수 형태로 나타냅니다.

1-3 IPv6

IPv4의 주소 자원 부족과 인터넷 보안의 강화를 위해 개발된 주소 체계입니다. IPv6 주소는 128비트로, 16비트씩 8부분으로 구분합니다. 각 필드는 :(콜론)으로 구분하고, 16진수를 이용하면 표현합니다.

사설 IP

  • 사설 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

  • 공인 IP는 인터넷 서비스 제공 업체(ISP)로부터 할당받은 고유한 인터넷 주소입니다.
  • 인터넷상에서 직접적으로 접속할 수 있는 유일한 주소입니다.
  • 공인 IP 주소는 전 세계적으로 고유하며, 모든 장치가 공유하고 있습니다.
  • 한 공인 IP 주소로 여러 대의 기기가 인터넷에 접속할 수 있도록 하는 기술인 네트워크 주소 변환(NAT)을 통해 사설 IP와 연결됩니다.

공유기와 IP 주소

  • 공유기를 통해 여러 기기를 하나의 인터넷 연결로 공유함으로써 통신료를 줄일 수 있습니다.
  • 공유기에 연결된 각 컴퓨터 또는 기기는 로컬 네트워크 내에서 고유한 사설 IP 주소를 가집니다.
  • 사용자가 공유기의 공인 IP 주소로 접속하면 실제로는 공유기에 접속하게 되어, 공유기가 이후에 요청을 처리하고 적절히 라우팅합니다.

공유기와 사설 IP

  • 공유기는 하나의 공인 IP 주소를 받아서 여러 개의 사설 IP 주소로 변환하여 로컬 네트워크 내의 여러 기기들이 인터넷을 사용할 수 있도록 합니다.
  • 공유기를 통해 연결된 각 컴퓨터나 기기는 사설 IP 주소를 할당받아서 인터넷을 사용합니다.
  • 외부에서 공유기에 할당된 공인 IP 주소로 접속하면 공유기에 접속되어, 공유기는 요청을 내부 네트워크에 있는 적절한 기기로 라우팅합니다.
  • 공유기를 통해 연결된 기기들은 외부에서 직접 접속할 수 없고, 공유기의 포트포워딩이나 DMZ 설정을 통해 특정 기기에만 접근할 수 있도록 구성할 수 있습니다.

포트포워딩을 통한 서버 접속

  • 포트포워딩은 공유기가 외부에서 접속한 사용자를 내부 네트워크에 있는 특정 서버로 연결하는 기능을 말합니다.
  • 포트는 클라이언트가 특정 서버에 접속할 때 사용되는 번호이며, 외부에서 접속하는 클라이언트가 어떤 서비스에 접근하는지를 지정합니다.
  • 웹 브라우저를 통해 서버에 접속할 때 사용되는 기본 포트는 HTTP의 경우 80번 포트이며, HTTPS의 경우 443번 포트입니다.

포트 포워딩 이해하기

  • 포트 포워딩은 데이터를 특정 포트를 통해 다른 컴퓨터로 전달하는 방법입니다.
  • 이를 통해 외부에서 특정 포트에 접속할 때, 공유기는 해당 포트를 사용하는 서버로 데이터를 전송합니다.
  • 포트 포워딩은 웹 서버, 데이터베이스 서버 등 다양한 용도로 사용되며, 외부에서 내부 네트워크에 있는 서버에 접속할 수 있도록 합니다.

DMZ

포트 포워딩 대신에 DMZ라는 설정을 사용하는 경우가 많은데, DMZ를 사용하게 되면 하나의 컴퓨터로만 모든 포트를 포워딩하는 것과 같습니다. 그렇기 때문에 여러 컴퓨터에서 서비스나 서버를 운영하게 된다면 DMZ는 큰 문제가 될 수 있습니다. 또한 DMZ 설정은 결코 보안에도 좋지가 않기 때문에 반드시 필요한 포트만 찾아서 포워딩하는 것이 안전합니다.

포트란 무엇인가?

  • 포트는 각각의 프로그램이나 서비스가 인터넷상에서 데이터 통신을 수행하기 위해 사용하는 고유한 번호입니다.
  • 웹 서버는 일반적으로 80번 포트에 설치되어 있어 웹 브라우저가 서버에 HTTP 요청을 보낼 때 80번 포트를 사용합니다.
  • 포트가 필요한 이유는 컴퓨터에는 여러 프로그램이 설치되어 있고, 각 프로그램은 인터넷에 연결되어 데이터를 주고받아야 합니다. 이를 위해 포트를 사용하여 각 프로그램이 서로 구분되고 특정 프로그램에 데이터가 전송됩니다.

포트 번호의 역할

  • 인터넷에 연결된 여러 애플리케이션 중 어떤 애플리케이션이 응답해야 하는지 명시하기 위해 포트 번호를 사용합니다.
  • 포트 번호는 IP 주소와 함께 사용되어 특정 애플리케이션에 요청을 보낼 수 있도록 합니다.
  • 웹 브라우저를 사용하여 웹 사이트에 접속할 때는 80번 포트를 사용합니다.

공유기 내부 IP 주소 확인 방법

  • 공유기 설정에서 내부 IP 주소 확인 가능합니다.
  • 윈도우 명령 프롬프트에서 'ipconfig' 명령어로 확인 가능합니다.
  • 무선으로 연결된 경우 무선 어댑터의 IPv4 주소를 확인합니다.

공유기 포트 포워딩 설정 방법

  • 외부 포트는 공유기에 연결된 외부 네트워크에서 들어오는 데이터의 포트를 의미합니다.
  • 내부 포트는 공유기와 서버로 구동하려는 컴퓨터가 연결된 포트를 가리킵니다.
  • 포트 포워딩을 설정하면 외부에서 해당 외부 포트로 접속하는 사용자의 요청이 내부 포트로 전달됩니다.

사설 IP와 포트 포워딩

  • 사설 IP는 직접 사용자들이 접속할 수 없기 때문에 포트폴리다를 통해 접속해야 합니다.
  • 사설 IP를 사용하면 한정된 공유 IP만으로도 여러 대의 컴퓨터를 사용할 수 있습니다.
  • 사설 IP를 사용하면 외부에서 불필요하게 컴퓨터에 접속하는 것을 막을 수 있습니다.

실습[2]

실습 내용

이번 실습에서는 ESP32를 사용하여 내장 LED를 제어하는 웹 서버를 구축하고 실행했습니다.

라이브러리 설치

  • 라이브러리 매니저에서 'ESPAsyncWebSrv'를 검색하고 설치한다.

21

소스코드

// 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() {
}

소스코드 이해

zxczc

이 코드는 index_html이라는 문자열 배열에 HTML 코드를 저장하고 있습니다. 이 HTML 페이지는 ESP32에 연결된 GPIO(General Purpose Input/Output) 핀의 상태를 보여주고, 사용자가 웹 페이지를 통해 핀의 상태를 제어할 수 있게 해줍니다.

코드 설명

  • const char[]는 문자열을 저장하는 배열을 선언하는 것입니다.
  • 'R"rawliteral(...) 'rawliteral"' : 이 부분은 C++의 원시 문자열 리터럴(raw string literal)을 사용하는 것을 나타냅니다. 이는 문자열 내에서 특수 문자나 이스케이프 시퀀스를 사용하지 않고도 문자열을 그대로 저장할 수 있게 합니다. "rawliteral"은 사용자가 이 구조를 식별하기 위해 지정한 사용자 정의 태그입니다.

agagz

위 코드는 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 클래스를 사용하여 스타일이 다르게 표시됩니다.

zgagag

이 코드는 웹 서버를 통해 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 내의 동적 내용을 처리하는 데 사용됩니다.

GPIO 핀 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); }); 클라이언트가 "/2/on" 경로로 HTTP GET 요청을 보내면, 시리얼 모니터에 "GPIO 2 ON"을 출력하고, output2State 변수를 "on"으로 설정합니다. "digitalWrite(output2, HIGH);"를 통해 GPIO 핀을 높은 전압(HIGH)으로 설정하며, 이는 핀을 "ON" 상태로 만듭니다. 그 후 HTML 콘텐츠를 클라이언트에게 전송합니다.

GPIO 핀 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); }); 클라이언트가 "/2/off" 경로로 HTTP GET 요청을 보내면, 시리얼 모니터에 "GPIO 2 OFF"을 출력하고 output2State 변수를 "off"로 설정합니다. "digitalWrite(output2, LOW);"를 통해 GPIO 핀을 낮은 전압(LOW)으로 설정하며, 이는 핀을 "OFF" 상태로 만듭니다. 그 후 HTML 콘텐츠를 클라이언트에게 전송합니다.

실행

  • ESP32 보드를 컴퓨터에 연결합니다.
  • Arduino IDE에 소스코드를 컴파일하고 업로드합니다.

KakaoTalk_20240417_204652441

KakaoTalk_20240417_201108671.mp4

결과

  • ESP32 보드를 웹 서버로 설정한 후, 웹 브라우저에서 해당 서버에 접속하면 LED를 제어할 수 있는 웹 페이지가 나타납니다. 웹 페이지에서 LED를 켜고 끄는 등의 동작을 수행할 수 있으며, 실시간으로 LED의 상태를 확인할 수 있습니다.

실습[2-1]

실습내용

  • 이번 코드는 위 코드와 다르게 HTML, CSS 등 복잡한 요소를 제외하고, 간단하게 웹 브라우저를 통해 주소 뒤에 "/2/on"을 추가하면 GPIO 핀이 켜지고, "/2/off"를 추가하면 해당 핀이 꺼지는 기능을 제공하는 코드입니다.

라이브러리 설치

  • 라이브러리 매니저에서 'ESPAsyncWebServer'를 검색하고 설치한다.

agag

소스코드

#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() {
}

실행 결과

코드를 컴파일하고 나오는 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

  • 이 코드는 람다 함수를 사용하지 않는 버전입니다.

343434

45454


실습[3]

실습 내용

이번 실습에서는 DHT11 센서를 사용하여 온도와 상대습도를 측정하는 실습을 진행했습니다. 이 센서는 실내 및 외부 환경의 온도와 습도를 측정할 수 있습니다.

준비물

회로 연결

라이브러리 설치

  • 라이브러리 매니저에서 'DHT sensor library by Adafruit'를 검색하고 설치한다.

232323

소스코드

#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에 소스코드를 컴파일하고 업로드합니다.

KakaoTalk_20240417_204652441_02

결과

  • DHT11 센서를 아두이노에 연결하고 코드를 실행했을 때, 시리얼 모니터에 온도와 습도 값이 출력되었습니다.

실습[4]

실습 내용

이번 실습에서는 DHT11 센서를 사용하여 온도와 습도를 측정하고, 이 값을 웹 서버를 통해 실시간으로 표시하는 예제를 실행하였습니다.

준비물

회로 연결

소스코드

#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">&deg;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">&percnt;</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에 소스코드를 컴파일하고 업로드합니다.

KakaoTalk_20240417_204652441_03

결과

  • 사용자가 웹 페이지에서 다른 동작을 하지 않는 한, 온도 및 습도 값은 계속해서 실시간으로 업데이트됩니다.

후기

이번 실습을 통해 ESP32를 이용하여 웹페이지를 만들고, 해당 웹페이지에서 LED를 제어하고 ESP32와 DHT11 센서를 활용하여 온도와 습도를 측정하고, 이를 웹 서버를 통해 실시간으로 모니터링하는 과정을 배울 수 있었습니다. ESP32의 다양한 기능을 활용하여 웹 서버를 구축하는 과정에서 하드웨어와 소프트웨어 간의 상호작용에 대한 이해도가 높아졌습니다. 포트포워딩 및 사설 IP와 같은 네트워크 관련 개념을 이해하고 적용하는 과정에서 네트워크 구성 및 보안에 대한 중요성을 다시 한번 깨달을 수 있었습니다. 이러한 경험을 통해 더 다양한 시도해 보고 싶습니다.

⚠️ **GitHub.com Fallback** ⚠️