6주차 ‐ ESP32 Web Server 실습 - boguuu/SmartDevice_2025-1 GitHub Wiki

🖥️ESP32 웹서버


🛜ESP32 WiFi


ESP32는 내장 WiFi 기능을 통해 웹서버 기능을 구현 가능.

**STA 모드(Station 모드)**와 **AP 모드(Access Point 모드)**로 구분

WiFi 연결 모드:


  • STA 모드: 기존 WiFi 네트워크에 클라이언트로 접속
  • AP 모드: ESP32 자체가 WiFi 네트워크 제공 (Soft AP)

1. STA 모드 (Station Mode)


✅ 개념

  • **ESP32가 Wi-Fi 클라이언트(사용자 기기)**로 동작
  • 집이나 회사의 기존 Wi-Fi 공유기에 연결
  • 연결된 상태에서 인터넷 사용 가능하거나, 다른 장치들과 네트워크 내 통신 가능

✅ 특징

  • 일반 스마트폰이나 노트북처럼 기존 Wi-Fi 네트워크에 접속
  • 로컬 IP 주소를 공유기로부터 할당받음
  • 인터넷과 연결된 웹서버 구축 시 주로 사용

2. AP 모드 (Access Point Mode)


✅ 개념

  • ESP32가 Wi-Fi 공유기 역할을 함
  • 스마트폰, 태블릿 등 다른 기기가 ESP32에 직접 연결

✅ 특징

  • 인터넷 없이도 작동 가능 (로컬 통신 전용)
  • SSID 및 비밀번호 직접 설정 가능
  • 연결된 장치는 ESP32에서 제공하는 페이지/서비스에 접근 가능
💡

🔁 STA + AP 동시 모드 (혼합 모드) - ESP32는 STA + AP 모드 동시 지원도 가능

1. 간단한 웹서버 구축


실습코드


#include <WiFi.h>        // WiFi 라이브러리를 포함합니다.
#include <WebServer.h>   // WebServer 라이브러리를 포함합니다.

const char* ssid = "wifi-SSID";             // 연결할 Wi-Fi의 SSID를 입력합니다.
const char* password = "wifi-Password";     // Wi-Fi의 비밀번호를 입력합니다.

WebServer server(80);                          // 포트 번호 80을 사용하여 WebServer 객체를 생성합니다.

void handleRoot() {
  server.send(200, "text/plain", "Hello from ESP32!");   // "/" 경로에 대한 요청을 처리하는 핸들러 함수입니다.
}

void setup() {
  Serial.begin(115200);                         // 시리얼 통신을 초기화합니다.

  WiFi.begin(ssid, password);                    // Wi-Fi에 연결합니다.
  Serial.println("Connected to WiFi");           
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }

  server.on("/", handleRoot);                    // "/" 경로에 대한 요청을 handleRoot() 함수로 처리합니다.

  while (WiFi.status() != WL_CONNECTED) {        // Wi-Fi 연결이 완료될 때까지 대기합니다.
      delay(500);
      Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());                // Wi-Fi의 로컬 IP 주소를 출력합니다.

  server.begin();                                // 서버를 시작합니다.
}

void loop() {
  server.handleClient();                         // 클라이언트의 요청을 처리합니다.
}

코드 설명


  1. ESP32가 지정된 Wi-Fi 네트워크에 연결
  2. 연결이 성공하면 로컬 IP 주소가 시리얼 모니터에 출력
  3. 클라이언트가 브라우저에서 ESP32의 IP 주소로 접속하면, 루트 경로(/) 요청이 처리되고 "Hello from ESP32!" 메시지가 반환

실행방법


1. 코드 업로드


1

2. 시리얼 모니터에 출력된 ip주소 확인


2

👉 시리얼 모니터창을 열고 ESP32에 할당된 IP주소를 확인

3. ip주소로 이동 및 결과 확인


3

💡 같은 wifi에 접속된 아무 기기에서 확인 가능!!

2. 내장 LED를 제어하는 웹서버


  • HTML 파일에 %STATE%%BUTTON%을 사용해 LED 상태 실시간 표시
  • 상태에 따라 버튼과 텍스트 자동 갱신

필요 라이브러리


  • Async TCP by ESP32Async
  • ESP Async WebServer by Esp32Async

실습코드


// Import required libraries
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

// Replace with your network credentials
const char* ssid = "wifi-SSID";
const char* password = "wifi-Password";

// 현재 출력 상태를 저장하는 보조 변수
String outputState = "off";
// GPIO 핀에 출력 변수 할당
const int output = 2;
// 웹서버 생성(80)
AsyncWebServer server(80);

// 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="icon" href="data:,">
  <title>ESP 웹서버</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    h2 {font-size: 3.0rem;}
    p {font-size: 3.0rem;}
    body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
    .switch {position: relative; display: inline-block; width: 120px; height: 68px} 
    .switch input {display: none}
    .slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 34px}
    .slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; transition: .4s; border-radius: 68px}
    input:checked+.slider {background-color: #2196F3}
    input:checked+.slider:before {transform: translateX(52px);}
  </style>
</head>
<body>
  <h2>ESP 웹서버</h2>
  %BUTTONPLACEHOLDER%
<script>
function toggleCheckbox(element) {
  var xhr = new XMLHttpRequest();
  if(element.checked){ xhr.open("GET", "/update?state=1", true); }
  else { xhr.open("GET", "/update?state=0", true); }
  xhr.send();
}

setInterval(function () {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var inputChecked = (this.responseText == "1");
      document.getElementById("output").checked = inputChecked;
      document.getElementById("outputState").innerHTML = inputChecked ? "On" : "Off";
    }
  };
  xhttp.open("GET", "/state", true);
  xhttp.send();
}, 1000);
</script>
</body>
</html>
)rawliteral";

// HTML 내 placeholder를 처리하는 함수
String processor(const String& var){
  if(var == "BUTTONPLACEHOLDER"){
    String checked = digitalRead(output) ? "checked" : "";
    String html = "<h4>GPIO 2 - LED상태 <span id=\"outputState\">";
    html += digitalRead(output) ? "On" : "Off";
    html += "</span></h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"output\" ";
    html += checked + "><span class=\"slider\"></span></label>";
    return html;
  }
  return String();
}

void setup(){
  Serial.begin(115200);
  // 출력 변수를 출력으로 초기화
  pinMode(output, OUTPUT);
  // 출력을 LOW로 설정
  digitalWrite(output, LOW);

  // SSID와 비밀번호로 Wi-Fi 네트워크에 연결
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // 로컬 IP 주소 출력 및 웹 서버 시작
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  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>/on/off
  server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
    if (request->hasParam("state")) {
      String state = request->getParam("state")->value();
      if (state == "1") {
        digitalWrite(output, HIGH);
        outputState = "on";
        Serial.println("GPIO 2 ON");
      } else {
        digitalWrite(output, LOW);
        outputState = "off";
        Serial.println("GPIO 2 OFF");
      }
    }
    request->send(200, "text/plain", "OK");
  });

  server.on("/state", HTTP_GET, [](AsyncWebServerRequest *request){
    int state = digitalRead(output);
    request->send(200, "text/plain", String(state));
  });

  // Start server
  server.begin();
}

void loop() {
}

코드설명


ESP32 웹 서버를 이용해 GPIO 2번 핀을 ON/OFF 제어하는 웹 페이지를 만드는 코드

주요 기능 요약

  • ESP32가 Wi-Fi에 연결되고,
  • 경로에 접속하면 웹 페이지를 보여줌
  • 버튼 클릭으로 GPIO 2번 핀을 ON/OFF 제어 가능 (내장 LED도 해당됨)
  • 버튼 상태와 텍스트는 동적으로 바뀜

1. Wi-Fi 연결

  • **WiFi.begin(ssid, password)**를 통해 ESP 장치를 Wi-Fi 네트워크에 연결
  • 연결 상태를 **WiFi.status()**로 확인하며, 연결이 완료되면 로컬 IP 주소를 출력

2. 웹 서버 생성

  • AsyncWebServer 객체를 생성하여 포트 80에서 동작하는 비동기 웹 서버를 설정
  • 주요 라우트:
    • /: 기본 HTML 페이지 제공.
    • /update: GPIO 상태 업데이트.
    • /state: 현재 GPIO 상태 반환.

3. GPIO 핀 제어

  • GPIO 핀 2(output)가 LED 또는 기타 출력 장치로 설정
  • 초기화:
    • **pinMode(output, OUTPUT)**로 출력 모드 설정.
    • **digitalWrite(output, LOW)**로 초기 상태를 꺼짐(Low)으로 설정.
  • 상태 변경:
    • /update 요청에서 전달된 state 값에 따라 GPIO 핀을 켜거나 끔
    • 상태는 outputState 변수에 저장

4. HTML 및 JavaScript

  • HTML:
    • 사용자 인터페이스(UI)는 토글 스위치 형태로 구현되어 있으며, **%BUTTONPLACEHOLDER%**가 동적으로 대체됩니다.
  • JavaScript:
    • toggleCheckbox(element): 사용자가 스위치를 조작할 때 /update 경로로 AJAX 요청을 전송하여 GPIO 상태를 변경
    • setInterval: 매초 /state 경로로 AJAX 요청을 보내 현재 GPIO 상태를 확인하고 UI를 업데이트

5. HTML 템플릿 처리

  • processor() 함수:
    • HTML 템플릿 내 **%BUTTONPLACEHOLDER%**를 GPIO 상태에 따라 동적으로 대체
    • 현재 GPIO 상태(On 또는 Off)와 스위치의 초기 상태(checked)를 설정
💡 상태에 따라 **HTML 토글 스위치 애니메이션 변화**

실행방법


1.핵심 라이브러리 설치


4

2. 코드 업로드


5

3. 시리얼 모니터에서 ip주소 확인


6

4. ip주소로 이동 및 결과 확인


7

5. 실습 결과


Web.server.that.controls.the.built-in.LED.mp4

8

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