ESP8266+STA+AP+OTA:无需外网,用ESP8266建立网页控制GPIO,并用ArduinoOTA无线更新实现可持续升级

开关

先看看最终效果,局域网内查询并控制ESP8266端口状态(以板载LED为例),无需远程服务器,路由器掉线,或外部网络垮掉都可以用,是不是很爽?

1. 原理:

在ESP8266上建立网页服务可以以两种方式实现,作为STA或者AP。
其中STA和AP的区别在于:STA是自己接入别人的网络,AP是自己建立网络让别人接入,所以在ESP8266上建立本地网页,不论是STA还是AP模式,除了开始接入方式不同,后面的建立server及对client应答的方式都是一样的。刚好ESP8266支持STA+AP,使用WiFi.mode(WIFI_AP_STA)调用就可以了。

2. 背景

为什么需要STA+AP+OTA?
首先STA+OTA是固定搭配,ESP8266作为客户端与刷写固件的电脑连入同一个路由器,通过mDNS被发现,是无线刷固件必须的组合。而AP模式的加入可以使网页不依赖路由器通道,即使外部网络出问题的时候,手机等设备仍然能够通过连接ESP8266自己发出的接入点打开网页,与GPIO口进行交互,当然,在路由器可以接上的情况下,直接IP访问还是很方便的。

一般来说是这样的,路由器DHCP会记忆这个IP第一次分配的对应设备,除非因为IP池不够用而进行了二次分配导致该IP被占用,否则会一直分配同一个IP给同一个设备,也就是说,不用特地设置固定ip也不麻烦的。

3. 功能A(查询演示)

采用OTA方式无线更新,间隔一秒翻转点亮一次wemos板载LED,通过
1.在板子由wifi自动接入路由器时,同路由器下的设备直接访问OTA端口所在的ip地址,
2.现有wifi连接失败时,或者就是喜欢通过热点直连时,连接esp8266创建的热点"espAccessPoint"并访问192.168.4.1(默认热点IP),
从网页读取LED的亮灭状态,其中网页每5秒自动刷新一次,可以看到“status:”后的数字0,1随时间变化。

ip访问


4.代码A

/*********
  by Ningh
  adapted from Arduino IDE example: Examples > Arduino OTA > BasicOTA.ino
  https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/server-examples.html

*********/

#include 
#include 
#include 
#include 


// Replace with your network credentials
const char* ssid = "your-ssid";//wifi接入
const char* password = "your-password";

const char* assid = "espAccessPoint";//创建AP热点
const char* asecret = "12345678";

WiFiServer server(80);//创建tcp server

const int ESP_BUILTIN_LED = 2;

int ledState = LOW;
unsigned long previousMillis = 0;
const long interval = 1000;
int connCount = 0;//连接失败计数

void setup() {
 
  WiFi.mode(WIFI_AP_STA);
  WiFi.softAP(assid, asecret);  
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    delay(500);
    if(connCount > 30) {//如果连续30次连不上,则跳出循环,执行后面的程序
      break;
    }
    connCount += 1;
   // ESP.restart();
  }


  // Hostname defaults to esp8266-[ChipID]
  //ArduinoOTA.setHostname("WemosEXP");

  ArduinoOTA.begin();
  
  server.begin();//启动tcp连接 
  pinMode(ESP_BUILTIN_LED, OUTPUT);
}



String prepareHtmlPage(){
  String htmlPage =
     String("HTTP/1.1 200 OK\r\n") +
            "Content-Type: text/html\r\n" +
            "Connection: close\r\n" +  // the connection will be closed after completion of the response
            "Refresh: 5\r\n" +  // refresh the page automatically every 5 sec
            "\r\n" +
            "" +
            "" +
            "Digital PIN2 status:  " + String(digitalRead(ESP_BUILTIN_LED)) +
            "" +
            "\r\n";
  return htmlPage;
}

void loop() {
  ArduinoOTA.handle();
  WiFiClient client = server.available();
  // wait for a client (web browser) to connect
  if (client){
    while (client.connected()){
      // 不断读取请求内容
      if (client.available()){
        String line = client.readStringUntil('\r');
        Serial.print(line);
        // wait for end of client's request, that is marked with an empty line
        if (line.length() == 1 && line[0] == '\n'){
          //返回响应内容
          client.println(prepareHtmlPage());
          break;
        }
      }
    }
    delay(1); // give the web browser time to receive the data
    // close the connection:
    client.flush();//注意这里必须用client.flush(),如果是client.close就会报错的
  } 
  
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    if (ledState == LOW) {
      ledState = HIGH;  // Note that this switches the LED *off*
    } else {
      ledState = LOW;  // Note that this switches the LED *on*
    }
    digitalWrite(ESP_BUILTIN_LED, ledState);
  }
}


5. 功能B(状态查询+控制演示)

开关

代码B

/*********
  Rui Santos
  Complete project details at http://randomnerdtutorials.com
  Arduino IDE example: Examples > Arduino OTA > BasicOTA.ino
*********/

#include 
#include 
#include 
#include 


// Replace with your network credentials
const char* ssid = "your-ssid";
const char* password = "your-password";

const char* assid = "espAccessPoint";
const char* asecret = "12345678";

WiFiServer server(80);//创建tcp server

const int ESP_BUILTIN_LED = 2;

int connCount = 0;//连接失败计数

void setup() {
 
  WiFi.mode(WIFI_AP_STA);
  WiFi.softAP(assid, asecret);  
  WiFi.begin(ssid, password);
  
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    delay(500);
    if(connCount > 30) {//如果连续30次连不上,则跳出循环,执行后面的程序
      break;
    }
    connCount += 1;
    //ESP.restart();
  }

  
  // Hostname defaults to esp8266-[ChipID]
  //ArduinoOTA.setHostname("WemosEXP");

  ArduinoOTA.begin();
  
  server.begin();//启动tcp连接 
  pinMode(ESP_BUILTIN_LED, OUTPUT);

 
}



String prepareHtmlPage(){
  String htmlPage =
     String("HTTP/1.1 200 OK\r\n") +
            "Content-Type: text/html\r\n" +
            "Connection: close\r\n" +  // the connection will be closed after completion of the response
           // "Refresh: 5\r\n" +  // refresh the page automatically every 5 sec
            "\r\n" +
            " \n"+      

            "

Digital PIN2 status: "+String(digitalRead(ESP_BUILTIN_LED))+"

\n"+ "

"+ "

"+ "" + "\r\n"; return htmlPage; } void loop() { ArduinoOTA.handle(); WiFiClient client = server.available(); // wait for a client (web browser) to connect if (client){ while (client.connected()){ // 不断读取请求内容 if (client.available()){ String line = client.readStringUntil('\r'); if ( line.indexOf("LEDON") > 0 ) { digitalWrite(ESP_BUILTIN_LED, LOW); } else if ( line.indexOf("LEDOFF") > 0 ) { digitalWrite(ESP_BUILTIN_LED, HIGH); } // wait for end of client's request, that is marked with an empty line if (line.length() == 1 && line[0] == '\n'){ //返回响应内容 client.println(prepareHtmlPage()); break; } } } delay(1); // give the web browser time to receive the data // close the connection: client.flush();//注意这里必须用client.flush(),如果是client.close就会报错的 } }

OK,功能搞定!但是这个好像有那里不对~~虽然为了可复用性,我一向推崇bare minimum,但是什么都不加,这页面实在是外表有些low啊,根本没法混。算了,必须承认极简不是简陋,拿不出手咋办呢?好办!只要在页面加入一些css样式表就可以了,这样即不影响代码结构,又不需要加载js等其它控件,很节约资源了。(js等如果需要断网使用则要放在本地,涉及到spiffs存储,it's another story,and getting more and more complicated,先打住,如果以后实在要用到ajax再说吧)

" \n"+      

后面加上

"\n"+
            "LED Control\n"+
            "\n"+
            "\n"+
            "\n"+
开关

6.代码C

/*********
  By Ningh
  adapted from  Arduino IDE example: Examples > Arduino OTA > BasicOTA.ino
  with reference to https://lastminuteengineers.com/creating-esp8266-web-server-arduino-ide/
*********/

#include 
#include 
#include 
#include 


// Replace with your network credentials
const char* ssid = "your-ssid";
const char* password = "your-password";

const char* assid = "espAccessPoint";
const char* asecret = "12345678";

WiFiServer server(80);//创建tcp server

const int ESP_BUILTIN_LED = 2;

int connCount = 0;//连接失败计数

void setup() {
 
  WiFi.mode(WIFI_AP_STA);
  WiFi.softAP(assid, asecret);  
  WiFi.begin(ssid, password);
  

  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    delay(500);
    if(connCount > 30) {//如果连续30次连不上,则跳出循环,执行后面的程序
      break;
    }
    connCount += 1;
    //ESP.restart();
  }

  
  // Hostname defaults to esp8266-[ChipID]
  //ArduinoOTA.setHostname("WemosEXP");

  ArduinoOTA.begin();
  
  server.begin();//启动tcp连接 
  pinMode(ESP_BUILTIN_LED, OUTPUT);

 
}



String prepareHtmlPage(){
  String htmlPage =
     String("HTTP/1.1 200 OK\r\n") +
            "Content-Type: text/html\r\n" +
            "Connection: close\r\n" +  // the connection will be closed after completion of the response
            "\r\n" +

            " \n"+
            "\n"+
            "LED Control\n"+
            "\n"+
            "\n"+
            "\n"+
            "

Digital PIN2 status: "+String(digitalRead(ESP_BUILTIN_LED))+"

\n"+ "

"+ "

"+ "" + "\r\n"; return htmlPage; } void loop() { ArduinoOTA.handle(); WiFiClient client = server.available(); // wait for a client (web browser) to connect if (client){ while (client.connected()){ // 不断读取请求内容 if (client.available()){ String line = client.readStringUntil('\r'); if ( line.indexOf("LEDON") > 0 ) { digitalWrite(ESP_BUILTIN_LED, LOW); } else if ( line.indexOf("LEDOFF") > 0 ) { digitalWrite(ESP_BUILTIN_LED, HIGH); } // Serial.print(line); // wait for end of client's request, that is marked with an empty line if (line.length() == 1 && line[0] == '\n'){ //返回响应内容 client.println(prepareHtmlPage()); break; } } } delay(1); // give the web browser time to receive the data // close the connection: client.flush();//注意这里必须用client.flush(),如果是client.close会报错的 } }

补充要点:esp8266建立AP密码至少要8位,不然虽然不会报错(对,小坑),实际上是不会成功建立热点的,注意!

你可能感兴趣的:(ESP8266+STA+AP+OTA:无需外网,用ESP8266建立网页控制GPIO,并用ArduinoOTA无线更新实现可持续升级)