* 선행 조건
- 이 블로그에서 사용하는 분할 코딩 규칙을 알고 있다.
- 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;
}
시리얼 모니터 및 결과
시연 영상
AP 모드 시 LED
WiFi 접속 시도 후 연결 성공 LED
소스 파일