07주차 ‐ ESP32 Web Server 실습 2 - gitjs523/SmartDevice2025 GitHub Wiki

1. IP 주소 (Internet Protocol Address)

(1) IP 주소란?

  • 컴퓨터 네트워크에서 기기(컴퓨터, 스마트폰 등)를 식별하기 위한 고유의 주소
  • IP 주소를 통해 인터넷에서 데이터를 올바른 장치로 전송 가능
  • IP 주소로 특정 기기 식별 가능
  • IP 주소는 숫자로 이루어지고, IPv4와 IPv6 두 가지 형식 존재
  • IPv4: 4개의 숫자 그룹, 각각 0부터 255 사이의 숫자
  • IPv6: 16진수로 된 8개의 그룹

(2) IP 주소의 종류

공인 IP 주소(Public IP Address)

  • 인터넷 서비스 제공업체(ISP)가 부여하는 주소, 전 세계적으로 고유함
  • 인터넷 상에서 데이터 주고받기 위해 사용

사설 IP 주소(Private IP Address)

  • 가정이나 회사 등 특정 네트워크 내부에서 사용하는 주소, 외부 접근 불가

2. ESP32 Web Server 실습2

(1) DHT 온습도센서 제어

  • 실시간으로 온도와 습도 측정해서 시리얼 모니터로 출력하기
  • DH11 센서 모듈 이용

사용한 코드

#include <DHT.h>

#define DHTPIN 18

#define DHTTYPE DHT11

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(115200);
  dht.begin();
}

void loop() {
  delay(2000);

  float temperature = dht.readTemperature();
  float humidity = dht.readHumidity();

  Serial.print("온도: ");
  Serial.print(temperature);
  Serial.print(" °C, 습도: ");
  Serial.print(humidity);
  Serial.println(" %");
}

실행 결과

image

(2) DHT11 온습도 표시 웹서버(TEXT 모드)

  • 방금 측정한 온도와 습도를 웹서버에서 TEXT 모드로 표시하기
  • 주의 사항
    • 필요 시 'charset=utf-8' 이용하기
    • 웹 서버에서 글자 깨짐 현상 발생 시 코드 'request->send(200, "text/plain", msg)'를 'request->send(200, "text/plain; charset=utf-8", msg)' 로 바꾸기

사용한 코드

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>

const char* ssid = "네트워크_SSID";
const char* password = "네트워크_비밀번호";

#define DHTPIN 18
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

AsyncWebServer server(80);

String readDHTTemperature() {
  float t = dht.readTemperature();
  if (isnan(t)) {
    Serial.println("온도 읽기 실패");
    return "--";
  } else {
    Serial.print("온도: ");
    Serial.println(t);
    return String(t);
  }
}

String readDHTHumidity() {
  float h = dht.readHumidity();
  if (isnan(h)) {
    Serial.println("습도 읽기 실패");
    return "--";
  } else {
    Serial.print("습도: ");
    Serial.println(h);
    return String(h);
  }
}

void setup() {
  Serial.begin(115200);
  dht.begin();

  Serial.print("Wi-Fi 연결 중: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("\nWi-Fi 연결됨. IP 주소:");
  Serial.println(WiFi.localIP());

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    String msg = "=== ESP32 온습도 정보 ===\n";
    msg += "온도: " + readDHTTemperature() + " °C\n";
    msg += "습도: " + readDHTHumidity() + " %\n";
    msg += "\n개별 확인: /temperature 또는 /humidity";
    request->send(200, "text/plain; charset=utf-8", msg);
  });

  server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain; charset=utf-8", readDHTTemperature());
  });

  server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain; charset=utf-8", readDHTHumidity());
  });

  server.begin();
}

void loop() {
  // 비동기 웹서버: loop 비워둠
}

실행 결과

image

(3) DHT11 온습도 표시 웹서버(html, CSS 적용)

  • 이번엔 온도와 습도를 웹서버에서 HTML과 CSS를 이용하여 표시하기

사용한 코드

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>

const char* ssid = "YourSSID";
const char* password = "YourPassword";

#define DHTPIN 18

#define DHTTYPE    DHT11
DHT dht(DHTPIN, DHTTYPE);

AsyncWebServer server(80);

String readDHTTemperature() {
  float t = dht.readTemperature();
  if (isnan(t)) {
    Serial.println("DHT 센서에서 읽기 실패!");
    return "--";
  }
  else {
    Serial.println(t);
    return String(t);
  }
}

String readDHTHumidity() {
  float h = dht.readHumidity();
  if (isnan(h)) {
    Serial.println("DHT 센서에서 읽기 실패!");
    return "--";
  }
  else {
    Serial.println(h);
    return String(h);
  }
}

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";

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();

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Wi-Fi에 연결 중...");
}

Serial.println(WiFi.localIP());

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/html", index_html, processor);
});

server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readDHTTemperature().c_str());
});

server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readDHTHumidity().c_str());
});

server.begin();
}

void loop(){
}

실행 결과

  • 웹 서버

192 168 196 157 - Chrome 2025-04-20 오후 9_57_37

  • 시리얼 모니터(추가)

image

(4) Ngrok을 이용하여 서버 외부 접속

  1. Ngrok 사이트에 가입 후 다운로드
  2. Ngrok 인증키 복사

image

  1. 프롬프트 창에서 ngrok config add-authtoken $YOUR_AUTHTOKEN으로 인증키 값 저장

image

  1. Ngrok 실행 후 ngrok tcp http "ESP32에서 할당되는 IP주소" :80 입력 후 URL 생성

C__Users_JiSungOh_Downloads_ngrok-v3-stable-windows-amd64_ngrok exe - ngrok  http 192 168 196 157_80 2025-04-20 오후 10_52_12

  1. 다른 기기(스마트폰)에서 생성된 URL을 통해 웹서버 접속 실행 결과

KakaoTalk_20250420_225319227

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