본문 바로가기

ESP32

ESP32: Wi-Fi 관리자 만들기, AsyncWebServer 라이브러리

반응형

 

ESP32: Wi-Fi 관리자 만들기(AsyncWebServer 라이브러리)

 

이 가이드에서는 ESPAsyncWebServer 라이브러리를 사용하여 Wi-Fi 관리자를 만들고 설정하여 웹 서버 프로젝트나 Wi-Fi 네트워크에 연결해야 하는 모든 프로젝트에서 사용할 수 있도록 수정할 수 있습니다. Wi-Fi 관리자를 사용하면 네트워크 자격 증명(SSID 및 비밀번호)을 하드 코딩하지 않고도 ESP32 보드를 다른 액세스 포인트(네트워크)에 연결하고 보드에 새 코드를 업로드할 수 있습니다. ESP는 마지막으로 저장된 네트워크에 자동으로 가입하거나 네트워크 자격 증명을 구성하는 데 사용할 수 있는 액세스 포인트를 설정합니다.

 

ESP32: 간단한 Wi-Fi 관리자 만들기(AsyncWebServer 라이브러리)

 

이 프로젝트의 작동 방식을 더 잘 이해하려면 다음 튜토리얼을 살펴보는 것이 좋습니다.

  • Arduino IDE를 사용하여 HTML 양식 ESP32/ESP8266 웹 서버에 데이터 입력
  • 웹 서버에 ESP32 액세스 포인트(AP)를 설정하는 방법
  • ESP32 정적/고정 IP 주소

 

다음 프로젝트에서도 이 Wi-Fi 관리자 접근 방식을 사용합니다.

  • Wi-Fi 관리자가 있는 ESP32 Neopixel 상태 표시기 및 센서 PCB 실드

작동 방식

 

다음 다이어그램을 살펴보면 만들 Wi-Fi 관리자가 작동하는 방식을 알 수 있습니다.

 

 

ESP32 ESP8266 Wi-Fi 관리자 웹 서버 작동 방식

  • ESP가 처음 시작되면 ssid.txt, pass.txt 및 ip.txt 파일*을 읽으려고 시도합니다(1);
  • 파일이 비어 있으면(2)(처음 보드를 실행할 때 파일이 비어 있음) 보드가 액세스 포인트로 설정됩니다(3);
  • 브라우저가 있는 Wi-Fi 지원 기기를 사용하면 새로 만든 액세스 포인트(기본 이름 ESP-WIFI-MANAGER)에 연결할 수 있습니다.
  • ESP-WIFI-MANAGER와 연결을 설정한 후 기본 IP 주소 192.168.4.1로 이동하여 SSID와 비밀번호를 구성할 수 있는 웹 페이지를 열 수 있습니다(4);
  • 양식에 삽입된 SSID, 비밀번호, IP 주소는 해당 파일인 ssid.txt, pass.txt, ip.txt에 저장됩니다(5);
  • 그 후 ESP 보드가 재시작됩니다(6);
  • 이번에는 재시작 후 파일이 비어 있지 않으므로 ESP는 양식에 삽입한 설정을 사용하여 스테이션 모드에서 네트워크에 연결을 시도합니다(7);
  • 연결이 설정되면 프로세스가 성공적으로 완료되고 원하는 작업을 수행할 수 있는 기본 웹 서버 페이지에 액세스할 수 있습니다(센서 판독값 제어, 출력 제어, 텍스트 표시 등)(9). 그렇지 않으면 액세스 포인트(3)가 설정되고 기본 IP 주소(192.168.4.1)에 액세스하여 다른 SSID/암호 조합을 추가할 수 있습니다.

 

* 게이트웨이 필드와 gateway.txt 파일을 만들어 IP 주소 게이트웨이를 저장합니다(다이어그램에는 표시되지 않음).

 

Wi-Fi 관리자를 설정하는 방법을 보여드리기 위해 하나의 출력(GPIO2? 내장 LED)을 제어하는 웹 서버를 설정합니다. ESPAsyncWebServer 라이브러리로 빌드한 모든 웹 서버 프로젝트나 ESP를 Wi-Fi 네트워크에 연결해야 하는 모든 프로젝트에 Wi-Fi 관리자를 적용할 수 있습니다.

 

필수 조건

 

Arduino IDE를 사용하여 ESP32 보드를 프로그래밍합니다. 따라서 ESP32 보드 애드온이 설치되어 있는지 확인하세요.

  • Arduino IDE에 ESP32 보드 설치(Windows, Mac OS X, Linux)

VS Code + PlatformIO를 사용하여 ESP32를 프로그래밍하려면 다음 튜토리얼을 따르세요.

  • ESP32 및 ESP8266용 VS Code 및 PlatformIO IDE 시작하기(Windows, Mac OS X, Linux Ubuntu)

 

라이브러리 설치(Arduino IDE)

 

다음 라이브러리를 사용하여 웹 서버를 빌드합니다.

  • ESPAsyncWebServer
  • AsyncTCP

 

Arduino Library Manager에서 이러한 라이브러리를 설치할 수 있습니다. 왼쪽 사이드바에서 라이브러리 아이콘을 클릭하여 Library Manager를 엽니다.

 

ESPAsyncWebServer를 검색하여 ESP32Async로 ESPAsyncWebServer를 설치합니다.

 

ESPAsyncWebServer ESP32 Arduino IDE 설치

 

그런 다음 AsyncTCP 라이브러리를 설치합니다. AsyncTCP를 검색하여 ESP32Async로 AsyncTCP를 설치합니다.

 

AsyncTCP ESP32 Arduino IDE 설치

 

파일 시스템 업로더

 

진행하기 전에 Arduino IDE에 ESP32 업로더 플러그인을 설치해야 합니다.

  • Arduino IDE 2: ESP32 LittleFS 업로더 설치(파일 시스템에 파일 업로드)

 

파일 구성

 

프로젝트를 구성하고 이해하기 쉽게 하기 위해 웹 서버를 빌드하기 위한 네 가지 다른 파일을 만들 것입니다.

웹 서버를 처리하는 Arduino 스케치;

index.html: 스테이션 모드에서 출력(또는 빌드하려는 다른 웹 페이지)을 제어하기 위한 웹 페이지의 콘텐츠를 정의합니다.

style.css: 웹 페이지의 스타일을 지정합니다.

wifimanager.html: ESP가 액세스 포인트 모드일 때 Wi-Fi 관리자를 표시하기 위한 웹 페이지의 콘텐츠를 정의합니다.

 

ESP32 ESP8266 Wi-Fi 관리자 프로젝트 파일

 

이전 다이어그램에 표시된 대로 HTML 및 CSS 파일은 Arduino 스케치 폴더 내의 data라는 폴더에 저장해야 합니다. 이 파일을 ESP32 파일 시스템(LittleFS)에 업로드하겠습니다.

 

모든 프로젝트 파일을 다운로드할 수 있습니다. 모든 Arduino 프로젝트 파일 다운로드

 

HTML 파일 만들기

 

이 프로젝트의 경우 HTML 파일 두 개가 필요합니다. 하나는 출력을 제어하는 메인 페이지(index.html)를 빌드하는 파일이고, 다른 하나는 Wi-Fi 관리자 페이지(wifimanager.html)를 빌드하는 파일입니다.

 

index.html

 

index.html 파일에 복사해야 하는 텍스트는 다음과 같습니다.

 

<!DOCTYPE html>
<html>
  <head>
    <title>ESP WEB SERVER</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="style.css">
    <link rel="icon" type="image/png" href="favicon.png">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  </head>
  <body>
    <div class="topnav">
      <h1>ESP WEB SERVER</h1>
    </div>
    <div class="content">
      <div class="card-grid">
        <div class="card">
          <p class="card-title"><i class="fas fa-lightbulb"></i> GPIO 2</p>
          <p>
            <a href="on"><button class="button-on">ON</button></a>
            <a href="off"><button class="button-off">OFF</button></a>
          </p>
          <p class="state">State: %STATE%</p>
        </div>
      </div>
    </div>
  </body>
</html>

 

이 HTML 파일의 작동 방식은 이 튜토리얼의 목적이 아니므로 설명하지 않겠습니다. 

 

wifimanager.html


Wi-Fi 관리자 웹 페이지는 다음과 같습니다:

 

 

 

다음을 wifimanager.html 파일에 복사합니다. 그러면 입력 필드 3개와 제출 버튼이 있는 양식이 있는 웹 페이지가 만들어집니다.

 

<!DOCTYPE html>
<html>
<head>
  <title>ESP Wi-Fi Manager</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <div class="topnav">
    <h1>ESP Wi-Fi Manager</h1>
  </div>
  <div class="content">
    <div class="card-grid">
      <div class="card">
        <form action="/" method="POST">
          <p>
            <label for="ssid">SSID</label>
            <input type="text" id ="ssid" name="ssid"><br>
            <label for="pass">Password</label>
            <input type="text" id ="pass" name="pass"><br>
            <label for="ip">IP Address</label>
            <input type="text" id ="ip" name="ip" value="192.168.1.200"><br>
            <label for="gateway">Gateway Address</label>
            <input type="text" id ="gateway" name="gateway" value="192.168.1.1"><br>
            <input type ="submit" value ="Submit">
          </p>
        </form>
      </div>
    </div>
  </div>
</body>
</html>

 

이 HTML 파일에서 서버에 제출된 데이터로 HTTP POST 요청을 하는 HTML 양식을 만듭니다.

 

<form action="/" method="POST">

 

양식에는 세 개의 입력 필드와 해당 레이블(SSID, 비밀번호, IP 주소)이 있습니다.

 

다음은 SSID에 대한 입력 필드입니다.

 

<label for="ssid">SSID</label>
<input type="text" id ="ssid" name="ssid"><br>

 

 

다음은 비밀번호에 대한 입력 필드입니다.

 

<label for="pass">Password</label>
<input type="text" id ="pass" name="pass"><br>

 

 

스테이션 모드에서 ESP에 속성을 지정하려는 IP 주소에 대한 입력 필드가 있습니다. 기본적으로 192.168.1.200으로 설정합니다(다른 기본 IP 주소를 설정하거나 값 매개변수를 삭제할 수 있습니다. 기본값이 없습니다).

<input type="text" id ="ip" name="ip" value="192.168.1.200">

 

마지막으로 게이트웨이 주소에 대한 입력 필드가 있습니다. 기본 IP 주소가 192.168.1.200인 경우 게이트웨이는 기본적으로 192.168.1.1이 될 수 있습니다.

 

<input type="text" id ="gateway" name="gateway" value="192.168.1.1"><br>

 

CSS 파일

 

다음 스타일을 style.css 파일에 복사합니다. 이러한 스타일의 작동 방식은 설명하지 않습니다. 다른 ESP 웹 서버 프로젝트에서 유사한 스타일이 작동하는 방식은 이미 설명했습니다.

 

html {
  font-family: Arial, Helvetica, sans-serif; 
  display: inline-block; 
  text-align: center;
}

h1 {
  font-size: 1.8rem; 
  color: white;
}

p { 
  font-size: 1.4rem;
}

.topnav { 
  overflow: hidden; 
  background-color: #0A1128;
}

body {  
  margin: 0;
}

.content { 
  padding: 5%;
}

.card-grid { 
  max-width: 800px; 
  margin: 0 auto; 
  display: grid; 
  grid-gap: 2rem; 
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

.card { 
  background-color: white; 
  box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
}

.card-title { 
  font-size: 1.2rem;
  font-weight: bold;
  color: #034078
}

input[type=submit] {
  border: none;
  color: #FEFCFB;
  background-color: #034078;
  padding: 15px 15px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  width: 100px;
  margin-right: 10px;
  border-radius: 4px;
  transition-duration: 0.4s;
  }

input[type=submit]:hover {
  background-color: #1282A2;
}

input[type=text], input[type=number], select {
  width: 50%;
  padding: 12px 20px;
  margin: 18px;
  display: inline-block;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
}

label {
  font-size: 1.2rem; 
}
.value{
  font-size: 1.2rem;
  color: #1282A2;  
}
.state {
  font-size: 1.2rem;
  color: #1282A2;
}
button {
  border: none;
  color: #FEFCFB;
  padding: 15px 32px;
  text-align: center;
  font-size: 16px;
  width: 100px;
  border-radius: 4px;
  transition-duration: 0.4s;
}
.button-on {
  background-color: #034078;
}
.button-on:hover {
  background-color: #1282A2;
}
.button-off {
  background-color: #858585;
}
.button-off:hover {
  background-color: #252524;
}

 

웹 서버 설정

 

platformIO 확장 기능이 있는 VS Code를 사용하는 경우 platformio.ini 파일을 아래와 같이 편집해야 합니다. Arduino IDE를 사용하는 경우 이 작업은 무시해도 됩니다.

 

platformio.ini ESP32:

[env:esp32doit-devkit-v1]
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
monitor_speed=115200
lib_deps = ESP Async WebServer

 

 

코드

 

다음 코드를 Arduino IDE에 복사하거나 VS Code를 사용하는 경우 main.cpp 파일에 복사합니다.

 

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete instructions at https://RandomNerdTutorials.com/esp32-wi-fi-manager-asyncwebserver/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
#include <Arduino.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <AsyncTCP.h>
#include "LittleFS.h"

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

// Search for parameter in HTTP POST request
const char* PARAM_INPUT_1 = "ssid";
const char* PARAM_INPUT_2 = "pass";
const char* PARAM_INPUT_3 = "ip";
const char* PARAM_INPUT_4 = "gateway";

//Variables to save values from HTML form
String ssid;
String pass;
String ip;
String gateway;

// File paths to save input values permanently
const char* ssidPath = "/ssid.txt";
const char* passPath = "/pass.txt";
const char* ipPath = "/ip.txt";
const char* gatewayPath = "/gateway.txt";

IPAddress localIP;
//IPAddress localIP(192, 168, 1, 200); // hardcoded

// Set your Gateway IP address
IPAddress localGateway;
//IPAddress localGateway(192, 168, 1, 1); //hardcoded
IPAddress subnet(255, 255, 0, 0);

// Timer variables
unsigned long previousMillis = 0;
const long interval = 10000;  // interval to wait for Wi-Fi connection (milliseconds)

// Set LED GPIO
const int ledPin = 2;
// Stores LED state

String ledState;

// Initialize LittleFS
void initLittleFS() {
  if (!LittleFS.begin(true)) {
    Serial.println("An error has occurred while mounting LittleFS");
  }
  Serial.println("LittleFS mounted successfully");
}

// Read File from LittleFS
String readFile(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\r\n", path);

  File file = fs.open(path);
  if(!file || file.isDirectory()){
    Serial.println("- failed to open file for reading");
    return String();
  }
  
  String fileContent;
  while(file.available()){
    fileContent = file.readStringUntil('\n');
    break;     
  }
  return fileContent;
}

// Write file to LittleFS
void writeFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Writing file: %s\r\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("- failed to open file for writing");
    return;
  }
  if(file.print(message)){
    Serial.println("- file written");
  } else {
    Serial.println("- write failed");
  }
}

// Initialize WiFi
bool initWiFi() {
  if(ssid=="" || ip==""){
    Serial.println("Undefined SSID or IP address.");
    return false;
  }

  WiFi.mode(WIFI_STA);
  localIP.fromString(ip.c_str());
  localGateway.fromString(gateway.c_str());


  if (!WiFi.config(localIP, localGateway, subnet)){
    Serial.println("STA Failed to configure");
    return false;
  }
  WiFi.begin(ssid.c_str(), pass.c_str());
  Serial.println("Connecting to WiFi...");

  unsigned long currentMillis = millis();
  previousMillis = currentMillis;

  while(WiFi.status() != WL_CONNECTED) {
    currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
      Serial.println("Failed to connect.");
      return false;
    }
  }

  Serial.println(WiFi.localIP());
  return true;
}

// Replaces placeholder with LED state value
String processor(const String& var) {
  if(var == "STATE") {
    if(digitalRead(ledPin)) {
      ledState = "ON";
    }
    else {
      ledState = "OFF";
    }
    return ledState;
  }
  return String();
}

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

  initLittleFS();

  // Set GPIO 2 as an OUTPUT
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  
  // Load values saved in LittleFS
  ssid = readFile(LittleFS, ssidPath);
  pass = readFile(LittleFS, passPath);
  ip = readFile(LittleFS, ipPath);
  gateway = readFile (LittleFS, gatewayPath);
  Serial.println(ssid);
  Serial.println(pass);
  Serial.println(ip);
  Serial.println(gateway);

  if(initWiFi()) {
    // Route for root / web page
    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
      request->send(LittleFS, "/index.html", "text/html", false, processor);
    });
    server.serveStatic("/", LittleFS, "/");
    
    // Route to set GPIO state to HIGH
    server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request) {
      digitalWrite(ledPin, HIGH);
      request->send(LittleFS, "/index.html", "text/html", false, processor);
    });

    // Route to set GPIO state to LOW
    server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request) {
      digitalWrite(ledPin, LOW);
      request->send(LittleFS, "/index.html", "text/html", false, processor);
    });
    server.begin();
  }
  else {
    // Connect to Wi-Fi network with SSID and password
    Serial.println("Setting AP (Access Point)");
    // NULL sets an open Access Point
    WiFi.softAP("ESP-WIFI-MANAGER", NULL);

    IPAddress IP = WiFi.softAPIP();
    Serial.print("AP IP address: ");
    Serial.println(IP); 

    // Web Server Root URL
    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
      request->send(LittleFS, "/wifimanager.html", "text/html");
    });
    
    server.serveStatic("/", LittleFS, "/");
    
    server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) {
      int params = request->params();
      for(int i=0;i<params;i++){
        const AsyncWebParameter* p = request->getParam(i);
        if(p->isPost()){
          // HTTP POST ssid value
          if (p->name() == PARAM_INPUT_1) {
            ssid = p->value().c_str();
            Serial.print("SSID set to: ");
            Serial.println(ssid);
            // Write file to save value
            writeFile(LittleFS, ssidPath, ssid.c_str());
          }
          // HTTP POST pass value
          if (p->name() == PARAM_INPUT_2) {
            pass = p->value().c_str();
            Serial.print("Password set to: ");
            Serial.println(pass);
            // Write file to save value
            writeFile(LittleFS, passPath, pass.c_str());
          }
          // HTTP POST ip value
          if (p->name() == PARAM_INPUT_3) {
            ip = p->value().c_str();
            Serial.print("IP Address set to: ");
            Serial.println(ip);
            // Write file to save value
            writeFile(LittleFS, ipPath, ip.c_str());
          }
          // HTTP POST gateway value
          if (p->name() == PARAM_INPUT_4) {
            gateway = p->value().c_str();
            Serial.print("Gateway set to: ");
            Serial.println(gateway);
            // Write file to save value
            writeFile(LittleFS, gatewayPath, gateway.c_str());
          }
          //Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
        }
      }
      request->send(200, "text/plain", "Done. ESP will restart, connect to your router and go to IP address: " + ip);
      delay(3000);
      ESP.restart();
    });
    server.begin();
  }
}

void loop() {

}

 

코드 작동 방식

 

코드를 살펴보고 Wi-Fi 관리자가 어떻게 작동하는지 살펴보겠습니다.

 

다음 변수는 양식이 제출될 때 수행된 HTTP POST 요청에서 SSID, 비밀번호, IP 주소 및 게이트웨이를 검색하는 데 사용됩니다.

// Search for parameter in HTTP POST request
const char* PARAM_INPUT_1 = "ssid";
const char* PARAM_INPUT_2 = "pass";
const char* PARAM_INPUT_3 = "ip";
const char* PARAM_INPUT_4 = "gateway";

 

ssid, pass, ip 및 gateway 변수는 양식에 제출된 SSID, 비밀번호, IP 주소 및 게이트웨이 값을 저장합니다.

//Variables to save values from HTML form
String ssid;
String pass;
String ip;
String gateway;

 

제출 시 SSID, 비밀번호, IP 주소 및 게이트웨이는 ESP 파일 시스템의 파일에 저장됩니다. 다음 변수는 해당 파일의 경로를 참조합니다.

// File paths to save input values permanently
const char* ssidPath = "/ssid.txt";
const char* passPath = "/pass.txt";
const char* ipPath = "/ip.txt";
const char* gatewayPath = "/gateway.txt";

 

스테이션 IP 주소와 게이트웨이는 Wi-Fi 관리자 양식에 제출됩니다. 서브넷은 하드코딩되어 있지만 필요한 경우 다른 필드로 이 프로젝트를 쉽게 수정하여 서브넷을 포함할 수 있습니다.

IPAddress localIP;
//IPAddress localIP(192, 168, 1, 200); // hardcoded

// Set your Gateway IP address
IPAddress localGateway;
//IPAddress localGateway(192, 168, 1, 1); //hardcoded
IPAddress subnet(255, 255, 0, 0);

 

initWiFi()

 

initWiFi() 함수는 ESP 보드가 네트워크에 성공적으로 연결되었는지 여부를 나타내는 부울 값(참 또는 거짓)을 반환합니다.

bool initWiFi() {
  if(ssid=="" || ip==""){
    Serial.println("Undefined SSID or IP address.");
    return false;
  }

  WiFi.mode(WIFI_STA);
  localIP.fromString(ip.c_str());

  if (!WiFi.config(localIP, gateway, subnet)){
    Serial.println("STA Failed to configure");
    return false;
  }
  WiFi.begin(ssid.c_str(), pass.c_str());
  Serial.println("Connecting to WiFi...");

  unsigned long currentMillis = millis();
  previousMillis = currentMillis;

  while(WiFi.status() != WL_CONNECTED) {
    currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
      Serial.println("Failed to connect.");
      return false;
    }
  }

  Serial.println(WiFi.localIP());
  return true;
}

 

먼저 ssid 및 ip 변수가 비어 있는지 확인합니다. 비어 있으면 네트워크에 연결할 수 없으므로 false를 반환합니다.

if(ssid=="" || ip==""){

 

그렇지 않은 경우 SSID 및 패스 변수에 저장된 SSID와 비밀번호를 사용하여 네트워크에 연결하고 IP 주소를 설정해 보겠습니다.

WiFi.mode(WIFI_STA);
localIP.fromString(ip.c_str());

if (!WiFi.config(localIP, gateway, subnet)){
  Serial.println("STA Failed to configure");
  return false;
}
WiFi.begin(ssid.c_str(), pass.c_str());
Serial.println("Connecting to WiFi...");

 

10초(간격 변수) 후에도 Wi-Fi에 연결할 수 없으면 false를 반환합니다.

unsigned long currentMillis = millis();
previousMillis = currentMillis;

while(WiFi.status() != WL_CONNECTED) {
  currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    Serial.println("Failed to connect.");
    return false;
  }

 

이전 조건 중 어느 것도 충족되지 않으면 ESP가 스테이션 모드에서 네트워크에 성공적으로 연결되었음을 의미합니다(true를 반환합니다).

 

return true;

 

setup()

 

setup()에서 이전에 저장된 SSID, 비밀번호, IP 주소 및 게이트웨이를 가져오기 위해 파일을 읽기 시작합니다.

ssid = readFile(LittleFS, ssidPath);
pass = readFile(LittleFS, passPath);
ip = readFile(LittleFS, ipPath);
gateway = readFile (LittleFS, gatewayPath);

 

 

ESP가 스테이션 모드에서 성공적으로 연결되면(initWiFi() 함수가 true를 반환함), 웹 서버 요청(또는 ESP가 인터넷에 연결되어야 하는 다른 코드)을 처리하도록 명령을 설정할 수 있습니다.

if(initWiFi()) {
  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(LittleFS, "/index.html", "text/html", false, processor);
  });
  server.serveStatic("/", LittleFS, "/");
    
  // Route to set GPIO state to HIGH
  server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request) {
    digitalWrite(ledPin, HIGH);
    request->send(LittleFS, "/index.html", "text/html", false, processor);
  });

  // Route to set GPIO state to LOW
  server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request) {
    digitalWrite(ledPin, LOW);
    request->send(LittleFS, "/index.html", "text/html", false, processor);
  });
  server.begin();
}

 

그렇지 않으면 initWiFi() 함수가 false를 반환합니다. ESP는 액세스 포인트를 설정합니다.

else {
  // Connect to Wi-Fi network with SSID and password
  Serial.println("Setting AP (Access Point)");
  // NULL sets an open Access Point
  WiFi.softAP("ESP-WIFI-MANAGER", NULL);

  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);

 

액세스 포인트를 설정하려면 softAP() 메서드를 사용하고 액세스 포인트 이름과 비밀번호를 인수로 전달합니다. 액세스 포인트가 열려 있기를 원하므로 비밀번호를 NULL로 설정합니다. 원하는 경우 비밀번호를 추가할 수 있습니다. 액세스 포인트 설정에 대해 자세히 알아보려면 다음 자습서 중 하나를 읽어보세요.

  • 웹 서버 정적 IP 주소에 대한 ESP32 액세스 포인트(AP) 설정 방법

액세스 포인트에 액세스하면 양식에 네트워크 자격 증명을 입력하는 웹 페이지가 표시됩니다. 따라서 ESP는 루트/URL에서 요청을 받으면 wifimanager.html 파일을 보내야 합니다.

// Web Server Root URL
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(LittleFS, "/wifimanager.html", "text/html");
});

 

HTTP POST 요청을 통해 양식이 제출될 때 발생하는 일도 처리해야 합니다. 다음 줄은 제출된 값을 ssid, pass 및 ip 변수에 저장하고 해당 변수를 해당 파일에 저장합니다.

server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) {
  int params = request->params();
  for(int i=0;i<params;i++){
    AsyncWebParameter* p = request->getParam(i);
    if(p->isPost()){
      // HTTP POST ssid value
      if (p->name() == PARAM_INPUT_1) {
        ssid = p->value().c_str();
        Serial.print("SSID set to: ");
        Serial.println(ssid);
        // Write file to save value
        writeFile(LittleFS, ssidPath, ssid.c_str());
      }
      // HTTP POST pass value
      if (p->name() == PARAM_INPUT_2) {
        pass = p->value().c_str();
        Serial.print("Password set to: ");
        Serial.println(pass);
        // Write file to save value
        writeFile(LittleFS, passPath, pass.c_str());
      }
      // HTTP POST ip value
      if (p->name() == PARAM_INPUT_3) {
        ip = p->value().c_str();
        Serial.print("IP Address set to: ");
        Serial.println(ip);
        // Write file to save value
        writeFile(LittleFS, ipPath, ip.c_str());
      }
     // HTTP POST gateway value
      if (p->name() == PARAM_INPUT_4) {
        gateway = p->value().c_str();
        Serial.print("Gateway set to: ");
        Serial.println(gateway);
        // Write file to save value
        writeFile(LittleFS, gatewayPath, gateway.c_str());
      }
      //Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
    }
  }

 

양식을 제출한 후 ESP가 양식 세부 정보를 수신했는지 알 수 있도록 텍스트가 포함된 응답을 보냅니다.

request->send(200, "text/plain", "Done. ESP will restart, connect to your router and go to IP address: " + ip);

 

3초 후 ESP.restart()로 ESP 보드를 다시 시작합니다.

delay(3000);
ESP.restart();

 

코드 작동 방식에 대한 간략한 요약입니다.

이 아이디어를 ESPAsyncWebServer 라이브러리로 빌드된 다른 웹 서버 프로젝트에 적용할 수 있습니다.

 

코드 및 파일 업로드

 

data 폴더에 있는 파일을 ESP32에 업로드합니다. Arduino IDE에서 Windows에서는 [Ctrl] + [Shift] + [P]를 누르고 MacOS에서는 [?] + [Shift] + [P]를 눌러 명령 팔레트를 엽니다. Upload LittleFS to Pico/ESP8266/ESP32 명령을 검색하여 클릭합니다.

 

 

Upload LittleFS to Pico ESP8266 ESP32 Arduino IDE

 

이 옵션이 없는 경우 파일 시스템 업로더 플러그인을 설치하지 않았기 때문입니다. 이 튜토리얼을 확인하세요.

 

중요: 파일 시스템에 업로드하기 전에 직렬 모니터가 닫혔는지 확인하세요. 그렇지 않으면 업로드가 실패합니다.

 

PlatformIO 확장 프로그램과 함께 VS Code를 사용하는 경우 다음 튜토리얼 중 하나를 따라 보드에 파일을 업로드하는 방법을 알아보세요.

  • VS Code 및 PlatformIO를 사용한 ESP32: LittleFS 파일 시스템에 파일 업로드

파일을 성공적으로 업로드한 후 보드에 코드를 업로드합니다.

 

Arduino IDE 2 업로드 버튼

 

데모

 

모든 파일과 스케치를 성공적으로 업로드한 후 직렬 모니터를 열 수 있습니다. 처음으로 코드를 실행하는 경우 ssid.txt, pass.txt 및 ip.txt 파일을 읽으려고 하지만 해당 파일이 아직 생성되지 않았기 때문에 성공하지 못합니다. 따라서 액세스 포인트를 시작합니다.

 

ESP-Wi-Fi 관리자가 액세스 포인트 직렬 모니터 설정

 

컴퓨터나 스마트폰에서 네트워크 설정으로 이동하여 ESP-WIFI-MANAGER 액세스 포인트에 연결합니다.

 

ESP32 ESP8266 액세스 포인트 Wi-Fi 관리자에 연결합니다.

 

그런 다음 브라우저를 열고 192.168.4.1로 이동합니다. Wi-Fi 관리자 웹 페이지가 열립니다.

 

ESP32 ESP8266 Wi-Fi 관리자 필드

 

네트워크 자격 증명(SSID 및 비밀번호)과 로컬 네트워크에서 사용 가능한 IP 주소를 입력합니다.

그 후, 다음 페이지로 리디렉션됩니다.

 

ESP32 ESP8266이 스테이션에 성공적으로 연결됨 Wi-Fi 관리자

 

동시에 ESP는 삽입한 매개변수가 해당 파일에 성공적으로 저장되었음을 나타내는 내용을 직렬 모니터에 인쇄해야 합니다.

 

몇 초 후에 ESP가 다시 시작됩니다. 그리고 올바른 SSID와 비밀번호를 삽입했다면 스테이션 모드에서 시작됩니다.

 

ESP32 ESP8266이 Wi-Fi 스테이션에 성공적으로 연결됨

 

이번에는 로컬 네트워크에서 브라우저를 열고 ESP IP 주소를 입력합니다. 출력을 제어할 수 있는 웹 페이지에 액세스할 수 있어야 합니다.

 

ESP32 ESP8266 제어 출력 웹 서버

 

마무리

 

이 튜토리얼에서는 웹 서버 프로젝트나 ESP를 인터넷에 연결해야 하는 다른 프로젝트에 대한 Wi-Fi 관리자를 설정하는 방법을 알아보았습니다. Wi-Fi 관리자를 사용하면 네트워크 자격 증명을 하드 코딩할 필요 없이 ESP 보드를 다른 네트워크에 쉽게 연결할 수 있습니다. ESPAsyncWebServer 라이브러리로 빌드한 모든 웹 서버 프로젝트에 Wi-Fi 관리자를 적용할 수 있습니다.

 

독자 중 한 명이 더 많은 필드와 기능이 있는 이 프로젝트의 고급 버전을 만들었는데, 여기에서 그의 프로젝트를 확인할 수 있습니다.

 

ESP32 및 ESP8266 보드로 웹 서버를 빌드하는 방법에 대해 자세히 알아보려면 전자책을 꼭 살펴보세요.

  • ESP32 및 ESP8266으로 웹 서버 빌드 전자책(2판)

이 튜토리얼이 도움이 되었기를 바랍니다.

 

읽어주셔서 감사합니다. 배움을 멈추지 마세요. 결코 포기하지 마세요. 문서 참고 출처는 이곳을 따라가세요.

 

 

반응형

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