arduino

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

2019-04-12  本文已影响160人  蒸汽bot
开关

先看看最终效果,局域网内查询并控制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位,不然虽然不会报错(对,小坑),实际上是不会成功建立热点的,注意!

上一篇 下一篇

猜你喜欢

热点阅读