본문 바로가기

ESP32

PWM32 비동기 웹 서버 ESPAsyncWebServer 라이브러리

반응형

 

ESP32 비동기 웹 서버 - Arduino IDE로 출력 제어(ESPAsyncWebServer 라이브러리)

 

이 튜토리얼에서는 ESP32 보드로 비동기 웹 서버를 빌드하여 출력을 제어하는 방법을 알아봅니다. 보드는 Arduino IDE를 사용하여 프로그래밍되며 ESPAsyncWebServer 라이브러리를 사용합니다.

 

 

ESP32 비동기 웹 서버 Arduino IDE로 출력 제어

 

다음도 마음에 드실 수 있습니다. ESP8266 NodeMCU 비동기 웹 서버 - Arduino IDE로 출력 제어(ESPAsyncWebServer 라이브러리)

 

비동기 웹 서버

 

웹 서버를 빌드하기 위해 ESPAsyncWebServer 라이브러리를 사용하겠습니다. 이 라이브러리는 비동기 웹 서버를 쉽게 빌드할 수 있는 방법을 제공합니다. 비동기 웹 서버를 빌드하는 데는 라이브러리 GitHub 페이지에 언급된 것처럼 다음과 같은 여러 가지 이점이 있습니다.

 

  • "동시에 두 개 이상의 연결 처리";
  • "응답을 보내면 서버가 백그라운드에서 응답을 보내는 동안 다른 연결을 즉시 처리할 준비가 됩니다";
  • "템플릿을 처리하는 간단한 템플릿 처리 엔진";
  • 그리고 훨씬 더 많은 것들.

 

GitHub 페이지에서 라이브러리 문서를 살펴보세요.

 

필요한 부품

이 튜토리얼에서는 세 개의 출력을 제어합니다. 예를 들어 LED를 제어합니다. 따라서 다음 부품이 필요합니다.

 

  • ESP32(최고의 ESP32 개발 보드 참조)
  • LED 3개
  • 220옴 저항 3개
  • 브레드보드
  • 점퍼 와이어

 

회로도

 

코드로 넘어가기 전에 3개의 LED를 ESP32에 연결합니다. LED를 GPIO 2, 4, 33에 연결하지만 다른 GPIO를 사용할 수도 있습니다(ESP32 GPIO 참조 가이드 참조).

 

 

ESP32 제어 3개 LED 출력 웹 서버 배선 회로도

 

라이브러리 설치 - ESP 비동기 웹 서버

 

웹 서버를 빌드하려면 다음 라이브러리를 설치해야 합니다. 아래 링크를 클릭하여 라이브러리를 다운로드하세요.

 

  • ESPAsyncWebServer
  • AsyncTCP

 

이러한 라이브러리는 Arduino 라이브러리 관리자를 통해 설치할 수 없으므로 라이브러리 파일을 Arduino 설치 라이브러리 폴더에 복사해야 합니다. 또는 Arduino IDE에서 스케치 > 라이브러리 포함 > .zip 라이브러리 추가로 이동하여 방금 다운로드한 라이브러리를 선택할 수 있습니다.

 

프로젝트 개요

 

코드를 더 잘 이해하기 위해 웹 서버가 어떻게 작동하는지 살펴보겠습니다.

 

ESP32 비동기 웹 서버 제어 Arduino IDE 모바일 반응형 웹 페이지를 사용한 출력

 

  • 웹 서버에는 "ESP 웹 서버"라는 제목 하나와 3개의 출력을 제어하는 3개의 버튼(토글 스위치)이 있습니다. 각 슬라이더 버튼에는 GPIO 출력 핀을 나타내는 레이블이 있습니다. 더 많은 출력을 쉽게 제거/추가할 수 있습니다.
  • 슬라이더가 빨간색이면 출력이 켜져 있음을 의미합니다(상태가 HIGH임). 슬라이더를 토글하면 출력이 꺼집니다(상태가 LOW로 변경됨).
  • 슬라이더가 회색이면 출력이 꺼져 있음을 의미합니다(상태가 LOW임). 슬라이더를 토글하면 출력이 켜집니다(상태가 HIGH로 변경됨).

 

작동 원리?

 

ESP32 비동기 웹 서버 제어 출력 작동 원리

 

버튼을 토글하면 어떤 일이 일어나는지 살펴보겠습니다. GPIO 2의 예를 살펴보겠습니다. 다른 버튼도 비슷하게 작동합니다.

 

1. 첫 번째 시나리오에서는 버튼을 토글하여 GPIO 2를 켭니다. 그러면 브라우저가 /update?output=2&state=1 URL에서 HTTP GET 요청을 합니다. 해당 URL을 기반으로 ESP는 GPIO 2의 상태를 1(HIGH)로 변경하고 LED를 켭니다.

 

2. 두 번째 예에서는 버튼을 토글하여 GPIO 2를 끕니다. 그러면 브라우저가 /update?output=2&state=0 URL에서 HTTP GET 요청을 합니다. 해당 URL을 기반으로 GPIO 2의 상태를 0(LOW)으로 변경하고 LED를 끕니다.

 

ESP 비동기 웹 서버 코드

 

다음 코드를 Arduino IDE에 복사합니다.

 

/*********
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-async-web-server-espasyncwebserver-library/
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

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

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

const char* PARAM_INPUT_1 = "output";
const char* PARAM_INPUT_2 = "state";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <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: 6px}
    .slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 3px}
    input:checked+.slider {background-color: #b30000}
    input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}
  </style>
</head>
<body>
  <h2>ESP Web Server</h2>
  %BUTTONPLACEHOLDER%
<script>function toggleCheckbox(element) {
  var xhr = new XMLHttpRequest();
  if(element.checked){ xhr.open("GET", "/update?output="+element.id+"&state=1", true); }
  else { xhr.open("GET", "/update?output="+element.id+"&state=0", true); }
  xhr.send();
}
</script>
</body>
</html>
)rawliteral";

// Replaces placeholder with button section in your web page
String processor(const String& var){
  //Serial.println(var);
  if(var == "BUTTONPLACEHOLDER"){
    String buttons = "";
    buttons += "<h4>Output - GPIO 2</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"2\" " + outputState(2) + "><span class=\"slider\"></span></label>";
    buttons += "<h4>Output - GPIO 4</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"4\" " + outputState(4) + "><span class=\"slider\"></span></label>";
    buttons += "<h4>Output - GPIO 33</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"33\" " + outputState(33) + "><span class=\"slider\"></span></label>";
    return buttons;
  }
  return String();
}

String outputState(int output){
  if(digitalRead(output)){
    return "checked";
  }
  else {
    return "";
  }
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  pinMode(2, OUTPUT);
  digitalWrite(2, LOW);
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);
  pinMode(33, OUTPUT);
  digitalWrite(33, LOW);
  
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP Local 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>/update?output=<inputMessage1>&state=<inputMessage2>
  server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage1;
    String inputMessage2;
    // GET input1 value on <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>
    if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
      inputMessage1 = request->getParam(PARAM_INPUT_1)->value();
      inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
      digitalWrite(inputMessage1.toInt(), inputMessage2.toInt());
    }
    else {
      inputMessage1 = "No message sent";
      inputMessage2 = "No message sent";
    }
    Serial.print("GPIO: ");
    Serial.print(inputMessage1);
    Serial.print(" - Set to: ");
    Serial.println(inputMessage2);
    request->send(200, "text/plain", "OK");
  });

  // Start server
  server.begin();
}

void loop() {

}

 

코드 작동 방식

 

이 섹션에서는 코드 작동 방식을 설명합니다. 자세히 알아보려면 계속 읽거나 데모 섹션으로 이동하여 최종 결과를 확인하세요.

 

라이브러리 가져오기

 

먼저 필요한 라이브러리를 가져옵니다. WiFi, ESPAsyncWebserver 및 AsyncTCP 라이브러리를 포함해야 합니다.

 

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

 

네트워크 자격 증명 설정

 

다음 변수에 네트워크 자격 증명을 삽입하여 ESP32가 로컬 네트워크에 연결할 수 있도록 합니다.

 

const char* ssid = "REPLACE_WITH_YOUR_SSID";

const char* password = "REPLACE_WITH_YOUR_PASSWORD";

 

입력 매개변수

 

URL에 전달된 매개변수(GPIO 번호 및 상태)를 확인하기 위해 출력용 변수 하나와 상태용 변수 하나, 총 두 개의 변수를 만듭니다.

 

const char* PARAM_INPUT_1 = "output";
const char* PARAM_INPUT_2 = "state";

 

ESP32는 다음과 같은 요청을 수신한다는 점을 기억하세요. /update?output=2&state=0

 

AsyncWebServer 객체

 

포트 80에서 AsyncWebServer 객체를 만듭니다.

 

AsyncWebServer server(80);

 

웹 페이지 빌드

 

스타일과 JavaScript가 포함된 모든 HTML 텍스트는 index_html 변수에 저장됩니다. 이제 HTML 텍스트를 살펴보고 각 부분이 무엇을 하는지 살펴보겠습니다.

 

제목은 <title> 및 </tile> 태그 안에 들어갑니다. 제목은 말 그대로 웹 브라우저의 제목 표시줄에 표시되는 문서의 제목입니다. 이 경우에는 "ESP 웹 서버"입니다.

 

<title>ESP Web Server</title>

 

 

ESP32 비동기 웹 서버 웹 페이지 제목 HTML

 

다음태그는 모든 브라우저(노트북, 태블릿 또는 스마트폰)에서 웹 페이지를 반응형으로 만듭니다.

 

<meta name="viewport" content="width=device-width, initial-scale=1">

 

다음 줄은 파비콘에 대한 요청을 방지합니다. 이 경우 파비콘이 없습니다. 파비콘은 웹 브라우저 탭에서 제목 옆에 표시되는 웹사이트 아이콘입니다. 다음 줄을 추가하지 않으면 ESP32는 웹 서버에 액세스할 때마다 파비콘에 대한 요청을 받습니다.

 

<link rel="icon" href="data:,">

 

<스타일></스타일> 태그 사이에 웹 페이지의 스타일을 지정하는 CSS를 추가합니다. 이 CSS 스타일이 어떻게 작동하는지는 자세히 설명하지 않겠습니다.

 

 

<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: 6px}
    .slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 3px}
    input:checked+.slider {background-color: #b30000}
    input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}
</style>

 

HTML 본문

 

<body> 내용 </body>

 

 

<h2></h2> 태그는 웹 페이지에 제목을 추가합니다. 이 경우 "ESP 웹 서버" 텍스트이지만 다른 텍스트를 추가할 수 있습니다.

<h2>ESP Web Server</h2>

 

 

태그는 웹 페이지에 제목을 추가합니다. 이 경우 "ESP 웹 서버" 텍스트이지만 다른 텍스트를 추가할 수 있습니다.

 

제목 뒤에 버튼이 있습니다. 버튼이 웹 페이지에 표시되는 방식(빨간색: GPIO가 켜져 있는 경우; 회색: GPIO가 꺼져 있는 경우)은 현재 GPIO 상태에 따라 다릅니다.

 

웹 서버 페이지에 액세스할 때 올바른 현재 GPIO 상태를 표시하도록 하려고 합니다. 따라서 HTML 텍스트를 추가하여 버튼을 빌드하는 대신 플레이스홀더 %BUTTONPLACEHOLDER%를 추가합니다. 이 플레이스홀더는 웹 페이지가 로드될 때 올바른 상태로 버튼을 빌드하는 실제 HTML 텍스트로 대체됩니다.

 

%BUTTONPLACEHOLDER%

 

JavaScript

 

그런 다음 이전에 설명한 대로 버튼을 토글할 때 HTTP GET 요청을 하는 JavaScript가 있습니다.

 

<script>function toggleCheckbox(element) {
  var xhr = new XMLHttpRequest();
  if(element.checked){ xhr.open("GET", "/update?output="+element.id+"&state=1", true); }
  else { xhr.open("GET", "/update?output="+element.id+"&state=0", true); }
  xhr.send();
}
</script>

 

요청을 하는 줄은 다음과 같습니다.

 

if(element.checked){ xhr.open("GET", "/update?output="+element.id+"&state=1", true); }

 

element.id는 HTML 요소의 ID를 반환합니다. 각 버튼의 ID는 다음 섹션에서 볼 수 있듯이 GPIO가 제어합니다.

 

GPIO 2 button » element.id = 2
GPIO 4 button » element.id = 4
GPIO 33 button » element.id = 33

 

Processor

 

이제 HTML 텍스트의 플레이스홀더를 정의한 것으로 대체하는 Processor() 함수를 만들어야 합니다.

 

웹 페이지가 요청되면 HTML에 플레이스홀더가 있는지 확인합니다. %BUTTONPLACEHOLDER% 플레이스홀더를 찾으면 HTML 텍스트를 반환하여 버튼을 만듭니다.

 

String processor(const String& var){
  //Serial.println(var);
  if(var == "BUTTONPLACEHOLDER"){
    String buttons = "";
    buttons += "<h4>Output - GPIO 2</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"2\" " + outputState(2) + "><span class=\"slider\"></span></label>";
    buttons += "<h4>Output - GPIO 4</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"4\" " + outputState(4) + "><span class=\"slider\"></span></label>";
    buttons += "<h4>Output - GPIO 33</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"33\" " + outputState(33) + "><span class=\"slider\"></span></label>";
    return buttons;
  }
  return String();
}

 

줄을 쉽게 삭제하거나 추가하여 더 많은 버튼을 만들 수 있습니다.

 

 

 

버튼이 어떻게 생성되는지 살펴보겠습니다. 버튼을 빌드하기 위해 HTML 텍스트가 포함된 buttons라는 문자열 변수를 만듭니다. 토글 버튼이 회색 또는 빨간색이 되도록 HTML 텍스트를 현재 출력 상태와 연결합니다. 현재 출력 상태는 outputState() 함수에서 반환됩니다(GPIO 번호를 인수로 허용). 아래를 참조하세요.

 

buttons += "<h4>Output - GPIO 2</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"2\" " + outputState(2) + "><span class=\"slider\"></span></label>";

 

\는 문자열 내부에 ""를 전달할 수 있도록 사용됩니다.

 

outputState() 함수는 GPIO가 켜져 있으면 "checked"를 반환하고 GPIO가 꺼져 있으면 빈 필드 ""를 반환합니다.

 

따라서 GPIO 2가 켜져 있을 때의 HTML 텍스트는 다음과 같습니다.

 

String outputState(int output){
  if(digitalRead(output)){
    return "checked";
  }
  else {
    return "";
  }
}

 

작동 방식을 이해하기 위해 더 작은 섹션으로 나누어 보겠습니다.

 

<h4>Output - GPIO 2</h4>
<label class="switch">
<input type="checkbox" onchange="toggleCheckbox(this)" id="2" checked><span class="slider"></span>
</label>

 

 

HTML에서 토글 스위치는 입력 유형입니다. <input> 태그는 사용자가 데이터를 입력할 수 있는 입력 필드를 지정합니다. 토글 스위치는 체크박스 유형의 입력 필드입니다. 다른 많은 입력 필드 유형이 있습니다.

<input type="checkbox">
체크박스는 체크하거나 체크하지 않을 수 있습니다. 체크한 경우 다음과 같습니다.

<input type="checkbox" checked>
onchange는 요소(체크박스)의 값을 변경할 때 발생하는 이벤트 속성입니다. 토글 스위치를 체크하거나 체크 해제할 때마다 해당 특정 요소 ID(this)에 대해 JavaScript 함수인 topicCheckbox()를 호출합니다.

id는 해당 HTML 요소에 대한 고유한 ID를 지정합니다. id를 사용하면 JavaScript 또는 CSS를 사용하여 요소를 조작할 수 있습니다.

<input type="checkbox" onchange="toggleCheckbox(this)" id="2" checked>

 

 

setup()

 

setup()에서 디버깅 목적으로 직렬 모니터를 초기화합니다.

 

Serial.begin(115200);

 

pinMode() 함수를 사용하여 제어하려는 GPIO를 출력으로 설정하고 ESP32가 처음 시작될 때 LOW로 설정합니다. GPIO를 더 추가한 경우 동일한 절차를 수행합니다.

 

pinMode(2, OUTPUT);
digitalWrite(2, LOW);
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
pinMode(33, OUTPUT);
digitalWrite(33, LOW);

 

로컬 네트워크에 연결하고 ESP32 IP 주소를 인쇄합니다.

 

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Connecting to WiFi..");
}

// Print ESP Local IP Address
Serial.println(WiFi.localIP());

 

setup()에서 ESP32가 요청을 수신할 때 발생하는 일을 처리해야 합니다. 이전에 살펴본 것처럼, 다음과 같은 유형의 요청을 받습니다.

 

<ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>

 

그러므로 요청에 PARAM_INPUT1 변수 값(출력)과 PARAM_INPUT2(상태)가 포함되어 있는지 확인하고 해당 값을 input1Message 및 input2Message 변수에 저장합니다.

 

if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
  inputMessage1 = request->getParam(PARAM_INPUT_1)->value();
  inputMessage2 = request->getParam(PARAM_INPUT_2)->value();

 

그런 다음 해당 상태로 해당 GPIO를 제어합니다(inputMessage1 변수는 GPIO 번호를 저장하고 inputMessage2는 상태를 저장합니다. 0 또는 1)

 

digitalWrite(inputMessage1.toInt(), inputMessage2.toInt());

 

HTTP GET /update 요청을 처리하는 전체 코드는 다음과 같습니다.

 

server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
  String inputMessage1;
  String inputMessage2;
  // GET input1 value on <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>
  if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
    inputMessage1 = request->getParam(PARAM_INPUT_1)->value();
    inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
    digitalWrite(inputMessage1.toInt(), inputMessage2.toInt());
  }
  else {
    inputMessage1 = "No message sent";
    inputMessage2 = "No message sent";
  }
  Serial.print("GPIO: ");
  Serial.print(inputMessage1);
  Serial.print(" - Set to: ");
  Serial.println(inputMessage2);
  request->send(200, "text/plain", "OK");
});

 

마지막으로 서버를 시작합니다.

 

server.begin();

 

데모

 

ESP32에 코드를 업로드한 후 115200의 전송 속도로 직렬 모니터를 엽니다. 온보드 RST/EN 버튼을 누릅니다. IP 주소를 얻어야 합니다.

 

브라우저를 열고 ESP IP 주소를 입력합니다. 비슷한 웹 페이지에 액세스할 수 있습니다.

 

ESP32 비동기 웹 서버 제어 출력 웹 브라우저 데모 GPIO

 

토글 버튼을 눌러 ESP32 GPIO를 제어합니다. 동시에 직렬 모니터에 다음 메시지가 표시되어 코드를 디버깅하는 데 도움이 됩니다.

 

ESP32 비동기 웹 서버 제어 출력 데모 모바일

 

스마트폰의 브라우저에서 웹 서버에 액세스할 수도 있습니다. 웹 서버를 열 때마다 현재 GPIO 상태가 표시됩니다. 빨간색은 GPIO가 켜져 있음을 나타내고 회색은 GPIO가 꺼져 있음을 나타냅니다.

 

웹 서버

 

마무리

 

이 튜토리얼에서는 토글 스위치를 사용하여 ESP32의 출력을 제어하는 비동기 웹 서버를 만드는 방법을 알아보았습니다. 웹 페이지를 열 때마다 업데이트된 GPIO 상태가 표시됩니다.

 

ESPAsyncWebServer 라이브러리를 사용하는 다른 웹 서버 예제도 마음에 드실 수 있습니다.

 

  • ESP32 웹 서버: DHT11 또는 DHT22 온도 및 습도
  • ESP32 웹 서버: 순간 스위치로 출력 제어
  • ESP32 웹 서버: 타이머로 출력 제어
  • ESP32 웹 서버: 물리적 버튼으로 출력 제어

 

이 튜토리얼이 도움이 되었기를 바랍니다. 질문이 있으면 댓글을 남겨주시면 답변해 드리겠습니다.

 

읽어주셔서 감사합니다. 배움을 머추지 마세요. 절대 포기하지 마세요. 

 

 

반응형

더욱 좋은 정보를 제공하겠습니다.~ ^^