아두이노/프로젝트

[아두이노] 웹으로 wifi 연결 설정하기

Eu4ng 2020. 7. 25. 07:06

* 선행 조건

- 이 블로그에서 사용하는 분할 코딩 규칙을 알고 있다.

- wifi 연결하는 법을 알고 있다.

- softap모드 설정과 web 서버 만드는 법을 알고 있다.

- EEPROM 읽기 및 쓰기 하는 법을 알고 있다.

 

* 구현 기능

(1) 와이파이 설정이 안 되어있거나 설정된 와이파이로 3번 이상 접속 실패 시 AP 모드로 전환되어 웹을 통해 와이파이 설정을 할 수 있다.

(2) 와이파이 접속 시도 중에는 LED가 계속해서 깜빡이고, AP 모드 전환 시 LED가 주기적으로 두 번씩 깜빡이게 하였다. 와이파이 연결에 성공하면 LED는 계속 켜져있는다.

(3) AP의 이름과 비밀번호 수정은 _wifi 탭의 ssid_AP, pass_AP를 변경하여 업로드하면 된다. 기본 이름과 비밀번호는 각각 test, testtest이다.

(4) 여기 작성된 코드로는 와이파이 설정 후 연결이 되면 AP 모드로 전환할 수단이 따로 없다. 다음 프로젝트에서는 Flash 버튼을 이용해 EEPROM을 초기화하거나 수동으로 AP 모드로 전환하는 방법도 추가할 것이다.

 

 

사용제품: NodeMCU V2 CP2102/ESP8266/ESP-12E

핀 연결: x

 

외부 라이브러리:

x

 

소스 코드:

- 프로젝트 이름: wifi_config_with_web

더보기

wifi_config_with_web 탭

boolean wifi_connection = true;

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  eeprom_setup();
  wifi_setup();
}

void loop() {
  if(!wifi_connection){
    web_start();
    blinking();
  }
}

 

_eeprom 탭

#include <EEPROM.h>

#define ssid_address 981
#define pass_address 1001
#define ssid_length_address 1021
#define pass_length_address 1022

char memory[21] = "";
String data("Test");2

 

_html 탭

// 공통 html
const char HTTP_HEADER[] PROGMEM = "<!DOCTYPE html><html lang=\"en\"><head>"
                                  "<meta charset=\"utf-8\" name=\"viewport\"content=\"width=device-width,initial-scale=1,user-scalable=no\"/><link rel=\"icon\" href=\"data:,\">"
                                  "<title> Test Web </title>"; 
const char HTTP_STYLE[] PROGMEM = "<style>" 
                                  ".c{text-align:center;}div," 
                                  "input{padding:5px;font-size:1em;}input{width:95%;}" 
                                  "body{text-align:center;font-family:verdana;}" // max-width:300px; 
                                  "button{border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%} " 
                                  "</style>"; 
const char HTTP_HEAD_END[] PROGMEM = "</head><body><div style=\"text-align:center;display:inline-block;min-width:260px;\">"; 
const char HTTP_END[] PROGMEM = "</div></body></html>\r\n";

// 메뉴 html
const char HTTP_PORTAL_OPTIONS[] PROGMEM = "<form action=\"/wifi\"method=\"get\"><button>Configure WiFi</button></form><br/>";

//기본 html
const char HTTP_MAIN[] PROGMEM = "<form action=\"/main\"method=\"get\"><button>Return to Main</button></form><br/>";
const char HTTP_REBOOT[] PROGMEM = "<form action=\"/r\"method=\"post\"><button>Reboot</button></form><br/>";

// wifi html
const char HTTP_FORM_SSID[] PROGMEM = "<input id='ssid'name='ssid'length=20 placeholder='SSID'><br/>"; 
const char HTTP_FORM_PASS[] PROGMEM = "<input id='pass'name='pass'length=20 type='password'placeholder='password'><br/>";
const char HTTP_SSID_ERROR[] PROGMEM = "<div>Please input SSID</div><br/>";
const char HTTP_PASS_ERROR[] PROGMEM = "<div>Password should be at least 8 characters</div><br/>";
const char HTTP_WIFI_CONFIG[] PROGMEM = "<form action=\"/wifi\"method=\"get\"><button>Return to WiFi Configuration</button></form><br/>";

// form html
const char HTTP_FORM_SAVE[] PROGMEM = "<form method='get'action='save'>";  
const char HTTP_FORM_END[] PROGMEM = "<br/><button type='submit'>save</button></form>";
const char HTTP_SAVED[] PROGMEM = "<div>Configuration Saved<br/>Please Reboot for completing setting</div><br/>";

 

_web 탭

#include <ESP8266WebServer.h>

IPAddress local_IP(192,168,4,1);
IPAddress gateway(192,168,4,1);
IPAddress subnet(255,255,255,0);
ESP8266WebServer server(80);

 

_wifi 탭

#include <ESP8266WiFi.h>

//wifi
char ssid[21] = "NO SSID";
char pass[21] = "";

// ap
String ssid_AP = "test";
String pass_AP = "testtest";

// mac
uint8_t mac_array[6];
char _mac[17];
String mac;

 

eeprom 탭

void eeprom_setup(){
  EEPROM.begin(4096);
}

void eeprom_write(int address, String data){
  for (int i = 0; i < data.length(); i++) EEPROM.write(address + i, data[i]);
}

void eeprom_read(int address, int len_address, char* memory){
  uint8_t dataSize = EEPROM.read(len_address);
  if(dataSize < 21){
    for (int i = 0; i < dataSize; i++) memory[i] = EEPROM.read(address + i);
    memory[dataSize] = '\0';
  }
}

// wifi 설정
void eeprom_wifi_write(String ssidTemp,String passTemp) {  
  EEPROM.write(ssid_length_address,ssidTemp.length()); 
  eeprom_write(ssid_address,ssidTemp);
  EEPROM.write(pass_length_address,passTemp.length());
  eeprom_write(pass_address,passTemp);
  EEPROM.commit();
  Serial.println("WIFI 설정 완료");
}

void eeprom_wifi_read() {  // 아두이노 리셋 시 저장된 아이디 / 비밀번호 읽기 
  eeprom_read(ssid_address, ssid_length_address, ssid);
  eeprom_read(pass_address, pass_length_address, pass);
} 

 

function 탭

void blinking(){
  digitalWrite(LED_BUILTIN, HIGH);
  delay(125);
  digitalWrite(LED_BUILTIN, LOW);
  delay(125);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(125);
  digitalWrite(LED_BUILTIN, LOW);
  delay(125);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
}

 

handler 탭

void handleNotFound() {
  String message = "Page Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}

void handleRoot() {
  String page = Common_page();
  page+= (const __FlashStringHelper *)HTTP_PORTAL_OPTIONS;
  page+= (const __FlashStringHelper *)HTTP_REBOOT;
  page+= (const __FlashStringHelper *)HTTP_END;
  server.send(200, "text/html", page);
}

void handleWifi() {
  String page = Common_page();
  page+= (const __FlashStringHelper *)HTTP_MAIN;
  page+= (const __FlashStringHelper *)HTTP_FORM_SAVE;
  page+= (const __FlashStringHelper *)HTTP_FORM_SSID;
  page+= (const __FlashStringHelper *)HTTP_FORM_PASS;
  page+= (const __FlashStringHelper *)HTTP_FORM_END;
  page+= (const __FlashStringHelper *)HTTP_END;
  server.send(200, "text/html", page);
}

void handleSave() {
  String ssidTemp = server.arg("ssid");
  String passTemp = server.arg("pass");

  String page;
  
  if(ssidTemp == ""){
    page = Common_page();
    page+= (const __FlashStringHelper *)HTTP_SSID_ERROR;
    page+= (const __FlashStringHelper *)HTTP_WIFI_CONFIG;
  }
  else if(passTemp.length() < 8){
    page = Common_page();
    page+= (const __FlashStringHelper *)HTTP_PASS_ERROR;
    page+= (const __FlashStringHelper *)HTTP_WIFI_CONFIG;
  }
  else{
    eeprom_wifi_write(urldecode(ssidTemp),urldecode(passTemp));
    eeprom_wifi_read();
    
    page = Common_page();
    page+= (const __FlashStringHelper *)HTTP_SAVED;
    page+= (const __FlashStringHelper *)HTTP_MAIN;
    page+= (const __FlashStringHelper *)HTTP_REBOOT;
  }
  
  page+= (const __FlashStringHelper *)HTTP_END;

  server.send(200, "text/html", page);
}

void handleRst() {
  String page = Common_page();
  String tem = "<p> Device will be reboot.....</p>";
  page += tem;
  page+= (const __FlashStringHelper *)HTTP_END;
  server.send(200, "text/html", page);
  delay(100);
  ESP.restart();
}

 

page 탭

String Common_page(){
  String page;
  page = (const __FlashStringHelper *)HTTP_HEADER;
  page += (const __FlashStringHelper *)HTTP_STYLE;
  page += (const __FlashStringHelper *)HTTP_HEAD_END;

  String tem[3];
  tem[0] = "<p>SSID: ";
  tem[1] = ssid;
  tem[2] = "</p>";

  for(int i=0; i<3; i++){
    page += tem[i];
  }
  
  return page;
}

 

web 탭

void web_setup(){
  server.begin();
  Serial.println("Web Server started");
  server.onNotFound(handleNotFound);
  server.on("/", handleRoot);
  server.on("/main", handleRoot);
  server.on("/wifi", handleWifi);
  server.on("/save", handleSave);
  server.on("/r", handleRst);
}

void web_start(){
  server.handleClient();
}

String urldecode(String str)
{
    
    String encodedString="";
    char c;
    char code0;
    char code1;
    for (int i =0; i < str.length(); i++){
        c=str.charAt(i);
      if (c == '+'){
        encodedString+=' ';  
      }else if (c == '%') {
        i++;
        code0=str.charAt(i);
        i++;
        code1=str.charAt(i);
        c = (h2int(code0) << 4) | h2int(code1);
        encodedString+=c;
      } else{
        
        encodedString+=c;  
      }
      
      yield();
    }
    
   return encodedString;
}

unsigned char h2int(char c)
{
    if (c >= '0' && c <='9'){
        return((unsigned char)c - '0');
    }
    if (c >= 'a' && c <='f'){
        return((unsigned char)c - 'a' + 10);
    }
    if (c >= 'A' && c <='F'){
        return((unsigned char)c - 'A' + 10);
    }
    return(0);
}

 

wifi 탭

void wifi_setup(){
  eeprom_wifi_read();
  if((String) ssid == "NO SSID"){
    get_mac();
    softAp_setup();
    web_setup();
    wifi_connection = false;
  }
  else{
    if(!wifi_connect()){
    get_mac();
    softAp_setup();
    web_setup();
    wifi_connection = false;
    }
  }
}

// wifi 클라이언트
boolean wifi_connect(){
  delay(10);
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);
  
  int count = 0;
  int retry = 0;
  boolean connection = true;
  
  while (WiFi.status() != WL_CONNECTED) {
    if(count > 30){
      retry++;
      if(retry == 3){
        connection = false;
        break;
      }
      Serial.println();
      check_wifi();
      Serial.println("retry in 3 seconds");
      count = 0;
      delay(3000);
    }
    else{
      Serial.print(".");
      digitalWrite(LED_BUILTIN, HIGH);
      delay(250);
      digitalWrite(LED_BUILTIN, LOW);
      delay(250);
      count++;  
    }
  }
  if(connection){
    Serial.println();
    Serial.println("Connected");
    return true;
  }
  else{
    Serial.println();
    Serial.println("Failed");
    return false;
  }
}

void check_wifi(){
  int c = WiFi.status();
  switch(c){
    case 0:
      Serial.println("Wi-Fi is in process of changing between statuses");
    break;
    case 1:
      Serial.println("configured SSID cannot be reached");
    break;
    case 3:
      Serial.println("WiFi connected");
    break;
    case 6:
      Serial.println("WiFi password is incorrect");
    break;
  }
}

// wifi AP 모드

void softAp_setup(){
  delay(10);
  Serial.println();
  Serial.print("AP mode start....");
  if(WiFi.mode(WIFI_AP)){
    Serial.println("Succeed");
  }
  else{        
    Serial.println("Failed");
  }
  Serial.print("Setting softAP....");
  if(WiFi.softAP(ssid_AP,pass_AP)){
    Serial.println("Succeed");
    Serial.println();
    Serial.print("AP SSID: ");
    Serial.println(ssid_AP);
  }
  else{
    Serial.println("Failed");
    Serial.println("Password should be at least 8 character long");
  }
  WiFi.softAPConfig(local_IP, gateway, subnet);
  Serial.print("Web Server Address: ");
  Serial.println(local_IP);
  Serial.println("MAC address: ");
  Serial.println(mac);
}

void get_mac(){
  WiFi.macAddress(mac_array);
  for (int i = 0; i < sizeof(mac_array); i++){
    if(i == sizeof(mac_array)-1){
      sprintf(_mac, "%s%02x", _mac, mac_array[i]);
    }
    else{
      sprintf(_mac, "%s%02x-", _mac, mac_array[i]);
    }
  }
  String result(_mac);
  mac = result;
}

 

시리얼 모니터 및 결과

더보기
최초 실행 시 시리얼 모니터
웹 서버 연결 및 wifi 설정 페이지 이동 (AP 이름: test, AP 비밀번호: testtest)

 

접속하고자 하는 WiFi 이름 및 비밀번호 입력 (이름을 입력하지 않거나 비밀번호가 8자리 이하면 에러문구 표시)
설정이 완료되면 설정된 SSID를 확인한 뒤 재부팅
설정이 완료된 후 재부팅되어 와이파이에 성공적으로 접속되는 것을 확인할 수 있다. 한번 저장된 정보는 계속 남아있다.

 

시연 영상

더보기

 

AP 모드 시 LED 

 

 

WiFi 접속 시도 후 연결 성공 LED

 

 

 

 

소스 파일

wifi_config_with_web.zip
0.01MB