美文网首页arduino
ESP8266+STA+AP+OTA:无需外网,用ESP8266

ESP8266+STA+AP+OTA:无需外网,用ESP8266

作者: 蒸汽bot | 来源:发表于2019-04-12 00:25 被阅读160次
    开关

    先看看最终效果,局域网内查询并控制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 <ESP8266WiFi.h>
    #include <ESP8266mDNS.h>
    #include <WiFiUdp.h>
    #include <ArduinoOTA.h>
    
    
    // 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" +
                "<!DOCTYPE HTML>" +
                "<html>" +
                "Digital PIN2 status:  " + String(digitalRead(ESP_BUILTIN_LED)) +
                "</html>" +
                "\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 <ESP8266WiFi.h>
    #include <ESP8266mDNS.h>
    #include <WiFiUdp.h>
    #include <ArduinoOTA.h>
    
    
    // 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" +
                "<!DOCTYPE html> <html>\n"+      
    
                "<h1>Digital PIN2 status: "+String(digitalRead(ESP_BUILTIN_LED))+"</h1>\n"+            
                "<form id='F1' action='LEDON'><input class='button button-on' type='submit' value='ON' ></form><br>"+
                "<form id='F2' action='LEDOFF'><input class='button button-off' type='submit' value='OFF' ></form><br>"+
                "</html>" +
                "\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再说吧)

    "<!DOCTYPE html> <html>\n"+      
    

    后面加上

    "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n"+
                "<title>LED Control</title>\n"+
                "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n"+
                "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n"+
                ".button {display: block;width: 120px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n"+
                ".button-on {background-color: #5AD2FF;}\n"+
                ".button-on:active {background-color: #DE341E;}\n"+
               
                ".button-off {background-color: #34495e;}\n"+
                ".button-off:active {background-color: #DE341E;}\n"+
                "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n"+
                "</style>\n"+
                "</head>\n"+
                "<body>\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 <ESP8266WiFi.h>
    #include <ESP8266mDNS.h>
    #include <WiFiUdp.h>
    #include <ArduinoOTA.h>
    
    
    // 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" +
    
                "<!DOCTYPE html> <html>\n"+
                "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n"+
                "<title>LED Control</title>\n"+
                "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n"+
                "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n"+
                ".button {display: block;width: 120px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n"+
                ".button-on {background-color: #5AD2FF;}\n"+
                ".button-on:active {background-color: #DE341E;}\n"+
               
                ".button-off {background-color: #34495e;}\n"+
                ".button-off:active {background-color: #DE341E;}\n"+
                "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n"+
                "</style>\n"+
                "</head>\n"+
                "<body>\n"+
                "<h1>Digital PIN2 status: "+String(digitalRead(ESP_BUILTIN_LED))+"</h1>\n"+
                
                "<form id='F1' action='LEDON'><input class='button button-on' type='submit' value='ON' ></form><br>"+
                "<form id='F2' action='LEDOFF'><input class='button button-off' type='submit' value='OFF' ></form><br>"+
    
                "</html>" +
                "\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

        本文链接:https://www.haomeiwen.com/subject/gijaiqtx.html