본문 바로가기
코딩/아두이노

ESP32 wifi 접속을 스마트폰으로 설정하는 방법 wifi manager

by DIYver 2022. 9. 7.

 

ESP32 를 사용하는 이유는 보통 IoT 제품을 개발하거나
WIFI로 인터넷 접속이 필요하기 때문이다.

그런데 문제는 개발이 끝난후 미리 설정한 WiFi 정보가 아니면 WiFi 를 접속할 수 없어서
코드를 다시 열고 WiFi 정보를 수정한 뒤에 다시 업로드를 했어야 했다.

이런 방식을 하드코딩 방식이라고 한다.

개발이 쉽지만 사용성이 너무 떨어지는 장단점이 있는 것이다.

 

이번에 소개할 것은
ESP32 에서 코딩을 따로 수정할 필요 없이
스마트폰으로 ESP32의 wifi 접속 정보를 수정해서 wifi접속을 할 수 있는 기능이다.

필자도 아래의 글을 통해 참고한 내용으로
원문으로 읽어볼 수 있다면 읽어보는 것을 추천한다.

https://randomnerdtutorials.com/esp32-wi-fi-manager-asyncwebserver/

 

ESP32: Create a Wi-Fi Manager (AsyncWebServer library) | Random Nerd Tutorials

The Wi-Fi Manager allows you to connect the ESP32 board to different Access Points (networks) without hard-coding network credentials (SSID and password) and upload new code to your board.

randomnerdtutorials.com

 

- 작동 원리

공식 홈페이지에서 설명하는 WiFi manager 라는 기능의 원리는 위의 순서도와 같다.

1. ESP32 내부에 ssid.txt , pass.txt, ip.txt 파일이 존재하는 경우와 존재하지 않은 경우로 나눈다.

2. 위의 세 파일이 존재하지 않으면 자체적으로 ESP32 가 공유기가 되면서 스마트폰으로 ESP32 wifi에 접속할 수 있게된다.

3. ESP32 wifi 에 연결되었다면 스마트폰에서 웹주소 192.168.4.1 로 접속하여 ESP32 설정페이지를 연다.

4-5. SSID와 PASSWORD 와 IP ADDRESS 를 입력해주고 데이터를 제출(저장)하면 ESP32 내부에 ssid.txt , pass.txt , ip.txt 세 파일이 생성되게 된다.

6. 재부팅을 하고 1번으로 돌아간다.

7. 재부팅하면 이제는 세 txt 파일이 있기 때문에, 해당 정보로 wifi에 접속을 시도한다.

8. wifi 정보가 올바르게 입력되었다면 해당 wifi에 접속을 하고, 아니면 다시 스마트폰에서 정보를 입력하는 모드로 들어가게 된다.

위의 순서에 따라서 작동되기 때문에
문제가 생기면 위의 경우에서 내가 어느 단계에 있고, 어떤 문제가 있을지 찾아보면 된다.

 

이제 위의 기능을 실제로 ESP32에 넣어보도록 하자.

우선 관련 라이브러리를 다 설치해주어야 한다.

아래의 라이브러리를 설치하면 된다.

AsyncTCP-master.zip
0.02MB
ESPAsyncWebServer-master.zip
0.27MB

 

그리고 여기에 추가로 필요한게 있다.

https://randomnerdtutorials.com/install-esp32-filesystem-uploader-arduino-ide/

 

Install ESP32 Filesystem Uploader in Arduino IDE | Random Nerd Tutorials

In this article we’'ll show you how to upload files to ESP32 filesystem (SPIFFS) using a plugin for the Arduino IDE: the ESP32 filesystem uploader.

randomnerdtutorials.com

바로 Serial Peripheral Interface Flash File System (SPIFFS)관련 세팅을 해주어야 한다는 것이다.

아마 이미 되어있으신 분들도 계실 것이고, 아닌 분들도 계실것인데,
잘 모르겠다면 그냥 위의 링크로 들어가서 하라는 대로 세팅을 따라하면 된다.

SPIFFS 가 뭐냐면 아두이노 코드 뿐만이 아니라 기타 파일들을 ESP32 플래시 메모리에 옮겨 써주게 하는 기능이다.
이게 왜 필요하냐면 WiFi manager 로 데이터를 받아올 페이지 화면이 html 과 css로 구성되어 있는데,
그 파일을 같이 ESP32에 넣어주어야 하기 때문이다.

 

 

ESP32FS-1.0.zip
0.01MB

위의 링크조차 들어가서 원문으로 보기 싫어하실 분들을 위해 진짜 간단하게 설명해보겠다.

위의 파일을 받고,

컴퓨터에 아두이노가 설치된 폴더위치로 들어가서 tools 폴더에 ESP32FS 폴더를 압축해제해주면 된다.

 

그리고 아두이노IDE를 재시작하면 적용이 된다.

 

 

ESP32_WiFi_Manager.zip
0.00MB

이제는 위의 압축파일을 다운받고
압축을 푼 후에

아두이노 파일을 실행시켜준다.

 

 

 

이제 세팅이 다 끝났고, 코드 업로드를 하면 된다.

/*********
  Rui Santos
  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 "SPIFFS.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 SPIFFS
void initSPIFFS() {
  if (!SPIFFS.begin(true)) {
    Serial.println("An error has occurred while mounting SPIFFS");
  }
  Serial.println("SPIFFS mounted successfully");
}

// Read File from SPIFFS
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 SPIFFS
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("- frite 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);

  initSPIFFS();

  // Set GPIO 2 as an OUTPUT
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  
  // Load values saved in SPIFFS
  ssid = readFile(SPIFFS, ssidPath);
  pass = readFile(SPIFFS, passPath);
  ip = readFile(SPIFFS, ipPath);
  gateway = readFile (SPIFFS, 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(SPIFFS, "/index.html", "text/html", false, processor);
    });
    server.serveStatic("/", SPIFFS, "/");
    
    // Route to set GPIO state to HIGH
    server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request) {
      digitalWrite(ledPin, HIGH);
      request->send(SPIFFS, "/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(SPIFFS, "/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(SPIFFS, "/wifimanager.html", "text/html");
    });
    
    server.serveStatic("/", SPIFFS, "/");
    
    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(SPIFFS, 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(SPIFFS, 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(SPIFFS, 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(SPIFFS, 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() {

}

 

단순히 코드를 업로드 하는것 뿐만 아니라
SPIFFS 기능으로 html과 css 파일도 업로드 해주어야 한다.

ESP32 Sketch Data Upload 를 통해 html과 css 파일을 업로드 해주면 된다.

데이터 파일을 업로드 하지 않으면 스마트폰에서 WiFi 세팅을 할 수가 없다.

 

 

ESP32에 코드가 다 업로드 되었다면 이제 스마트폰에서 ESP32 wifi를 접속해주고
192.168.4.1 주소로 접속하면 위의 화면이 나온다.

SSID 와 Password 에 wifi 이름이랑 비번을 입력하면 된다.

IP Address 는 고유 IP 주소로, ESP32가 공유기에 접속했을때 부여 받을 IP주소를 입력하는 것이고
Gateway Address 는 공유기 내부 주소 규칙을 입력하면 된다.

쉽게 iptime 공유기라면 192.168.0.1 이 되고,

netis 공유기라면 192.168.1.1 이 된다.

 

정보를 제대로 입력하면 자동으로 ESP32가 wifi 접속하게 된다.

 

 

 

 

댓글