본문 바로가기
  • 재미있는 펌웨어와 코딩
ESP32/펌웨어

ESP32 Captive Portal 사용하기

by 윤재윤호 2023. 10. 6.

ESP32 모듈을 와이파이에 접속 하려면 하드코딩을 하거나 WiFiManager와 같은 라이브러리를 이용합니다.
방법은 여러가지가 있을 수 있지만 이번에 소개할 방법은 Captive Portal를 이용 하는겁니다.
Captive Portal은 Wi-Fi 또는 유선 네트워크에 새로 연결된 사용자에게 네트워크 리소스에 대한
광범위한 액세스 권한이 부여되기 전에 웹 브라우저에서 액세스하는 웹 페이지입니다.
버스 정류장 또는 버스 안에서 무료 와이파이에 연결하려면 연결되기 전에 로그인 화면이 나오는것을 보셨을 것입니다.
바로 그런 화면을 띄워서, 연결하고자 하는 공유기의 SSID와 패스워드를 입력하게 하여 와이파이를 사용 하고자 합니다.
소스가 다소 길고 어려울 수 있습니다. 하지만 Captive Portal에 관련된 코드는 참고만 하시고 사용하시면 됩니다.

 

사용 방법은 이렇습니다.

Captive Portal용 푸쉬버튼을 이용하여 버튼을 누르고 있는 상태에서 리셋을 눌러 줍니다.

즉, 부팅 시에 버튼이 눌려 있으면 Captive Portal 모드로 동작하고 그렇지 않으면 일반 와이파이 연결을 하시면 됩니다.

 

회로도 입니다.

GPIO13번에 스위치 연결

 


전체 소스코드 입니다.

CaptivePortalTest.zip
0.08MB

 

압축을 풀면 data 폴더와 2개의 파일이 있습니다.

압축파일 내용

 

CaptivePortalTest.ino 파일을 더블클릭하여 IDE를 실행합니다.

아두이노 IDE

 

CaptivePortal.ino 파일에 Captive Portal을 사용하기위한 코드가 있습니다.

 

먼저, SPIFFS에 부트스트랩을 사용하기위한 파일을 업로드 합니다.

이전 강좌 참조

https://jooduino.tistory.com/6

 

ESP32 SPIFFS 사용하기

ESP32가 아닌 일반적으로 사용하던 ATMega328의 아두이노를 사용했을땐 데이터를 저장하기 위해서 내부의 EEPROM을 이용하였습니다. EEPROM에 간단한 데이터는 저장하기 편리하지만, 파일을 저장할 수

jooduino.tistory.com

 

일단, 코드 설명전에 동작을 보겠습니다.

1. 푸쉬 버튼을 누르고 있는 상태에서 모듈을 리셋합니다.

2. LED가 빠르게 점멸하면 버튼에서 손을 땝니다.

3. 스마트폰으로 와이파이를 켜서 설정으로 들어갑니다.

4. captive를 선택 합니다. (다른 이름으로 변경 가능)

5. 네트워크에 로그인하세요를 클릭합니다.

6. 사용할 공유기의 SSID패스워드를 입력하고 접속을 클릭 합니다.

7. 자동으로 ESP32 모듈이 재부팅 되면서 와이파이에 접속을 시도 합니다.

    정확한 정보를 입력 했다면 접속이 잘되는것을 확인할 수 있습니다.

 

이제는 소스 코드를 보겠습니다.

Captive Portal을 사용하기위해 DNS서버와 비동기 웹서버를 준비합니다.

#include <Arduino.h>
#include <SPIFFS.h>
#include <Preferences.h>
#include <DNSServer.h>
#include <ESPAsyncWebServer.h>
#include <esp_wifi.h>
#include <SPIFFS.h>

DNSServer dnsServer;
AsyncWebServer server(80);

 

와이파이 정보를 담기 위해서 Preferences를 사용합니다.

// INI 쓰기
void SetIni( String sNamespace, String sKey, String sValue ) {
  Preferences preferences;
  preferences.begin( sNamespace.c_str(), false );
  preferences.putString( sKey.c_str(), sValue.c_str() );
  preferences.end();
}

// INI 읽기
String GetIni( String sNamespace, String sKey, String sDefaultValue ) {
  Preferences preferences;
  String sValue;
  preferences.begin( sNamespace.c_str(), false );
  sValue = preferences.getString( sKey.c_str(), sDefaultValue.c_str() );
  preferences.end();
  return sValue;
}

이전 강좌 참조

https://jooduino.tistory.com/14

 

ESP32 환경설정 저장 - Preferences

ESP32에서 환경변수 저장은 EEPROM을 사용하지 않고 내부의 Flash 메모리에 저장합니다. EEPROM을 사용할 때 보다 자유롭고 더 많은 데이터를 저장할 수 있습니다. ESP32에는 아주 편한 Preferences 클래스

jooduino.tistory.com

 

 

ESP32 내부 LED를 출력으로, 푸쉬 스위치를 입력으로 선언합니다.

void setup() {
  Serial.begin( 115200 );
  pinMode( LED, OUTPUT );
  pinMode( WIFI_SETUP_PIN, INPUT_PULLUP );
  digitalWrite( LED, HIGH );

 

부팅시 버튼이 눌러져 있으면 Captive Portal을 사용하기 위한 설정을 합니다.

만약, 버튼이 눌러져 있지 않으면 와이파이 정보를 읽어와서 와이파이에 연결 합니다.

  // 와이파이 설정 버튼을 누르면..
  if( digitalRead( WIFI_SETUP_PIN ) == LOW ) {
    Serial.println( "Captive portal mode..." );
    bCaptivePortal = true;
    CaptiveSetup();
  }
  else {
    Serial.println( "WiFi connecting...." );
    bCaptivePortal = false;
    String ssid = GetIni( "wifi_config", "ssid", "none" );
    String password = GetIni( "wifi_config", "pw", "none" );
    
    WiFi.mode( WIFI_STA );
    WiFi.begin( ssid.c_str(), password.c_str() );
    
    // 와이파이가 연결될 때까지 기다린다.
    if( WiFi.waitForConnectResult() != WL_CONNECTED ) {
        Serial.printf( "WiFi Failed!\n" );
    }
    else {
      Serial.print( "IP Address: " );
      Serial.println( WiFi.localIP() );
      WIFI_LED_DELAY = 1000;
    }
  }

 

loop() 함수에서 Captive Portal 모드일 때 주기적으로 DNS 서버 요청을 확인한다.

  // 와이파이 설정 버튼을 눌렀을 경우
  if( bCaptivePortal ) {
    dnsServer.processNextRequest();
    delay( 30 ); // DNS 요청을 처리하는 사이의 간격.(밀리초)
  }

 

와이파이가 연결 되었을 때와 연결되지 않았을 때 LED 점멸 속도를 조절한다.

  // 와이파이가 연결 되면 LED 점멸 속도가 1초에 한 번 깜박임.
  // 와이파이가 연결 되지 않았으면 LED 점멸 속도가 빠르게 깜박임.
  static uint32_t ledDelay = 0;
  if( millis()-ledDelay > WIFI_LED_DELAY ) {
    ledDelay = millis();
    digitalWrite( LED, !digitalRead( LED ) );
  }

 

CaptivePortal.ino 파일을 보면 다른 코드는 참고만 하고 CaptiveSetup 함수를 살펴보면

ESP32 모듈을 AP모드로 설정하고 DNS 서버를 설정합니다.

// CaptivePortal 설정
void CaptiveSetup() {
  startSoftAccessPoint(ssid, password, localIP, gatewayIP);
  setUpDNSServer(dnsServer, localIP);

 

부트스트랩을 사용하기위한 코드를 넣습니다.

이전 강좌 참고

https://jooduino.tistory.com/13

 

ESP32 웹서버 만들기 - 부트스트랩 넣기

웹서버 페이지를 구성하려면 html, css 가지고 노가다 작업을 많이 해야합니다. 그래서 저는 w3.css를 사용 했었는데 부트스트랩(Bootstrap)을 많이 사용한다고 해서 ESP32 모듈에 부트스트랩을 넣어서

jooduino.tistory.com

  // SPIFFS의 CSS 연결
  server.on( "/src/bootstrap.min.css", HTTP_GET, [] (AsyncWebServerRequest *request ) {
    request->send( SPIFFS, "/src/bootstrap.min.css", "text/css" );
  });

  // SPIFFS의 jquery 연결
  server.on("/src/jquery-3.4.1.min.js", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(SPIFFS, "/src/jquery-3.4.1.min.js", "text/javascript");
  });

  // SPIFFS의 js 연결
  server.on("/src/bootstrap.bundle.min.js", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/src/bootstrap.bundle.min.js", "text/javascript");
  });

 

이제, 이 부분이 중요합니다.

HTML 코드에서 SSID패스워드를 입력하고 접속 버튼을 클릭하면 /login 경로로 SSID와 패스워드 값이 전달 됩니다.

전달된 값을 환경설정에 저장 합니다. 그리고 ESP32모듈이 자동 재부팅 됩니다.

이 때, 푸쉬 버튼은 누르지 않습니다. 푸쉬 버튼으로 하는 이유는 부팅시에 설정 모드로 들어가기 위해서 잠깐만 누르면

되기 때문 입니다.

  server.on("/login", HTTP_GET, [](AsyncWebServerRequest *request){
    String ssid;
    String pw;
    if( request->hasParam( "ssid" ) ) ssid = request->getParam( "ssid" )->value();
    else ssid = "None";
    if( request->hasParam( "pw" ) ) pw = request->getParam( "pw" )->value();
    else pw = "None";
    SetIni( "wifi_config", "ssid", ssid );
    SetIni( "wifi_config", "pw", pw );
    Serial.print( ssid );
    Serial.print( " / " );
    Serial.println( pw );
    ESP.restart();
    request->send(200, "text/plain", "OK");
  });

 

HTML 코드는 부트스트랩을 사용하여 만들었는데 자신의 입맛에 맞게 디자인 하면 됩니다.

이미지를 넣어서 좀더 멋지게 만들어 보시기 바랍니다.