65.9K
CodeProject 正在变化。 阅读更多。
Home

ESP8266 物联网设备的自动网络配置

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2020年11月5日

MIT

4分钟阅读

viewsIcon

10439

downloadIcon

291

关于自动配置 ESP8266 物联网设备的指南和源代码

引言

最近,我制作了一个设备,需要放在树林中间的一个小棚子里,用于报告传感器读数。由于它与世隔绝,设备不适合有用户界面,甚至连按钮都不需要。它需要向任何监听的机器广播传感器读数,并且需要在已知地址提供一个小网站。在创建这个设备的过程中,我编写了一些代码,用于自动配置ESP8266并在网络上发布。本文旨在解释这个过程,并提供代码以供您在自己的物联网项目中实现。虽然它专为ESP8266设计,但这些概念可以移植到其他平台。它应该适用于任何ESP8266模块,甚至是ESP-01。

必备组件

  • 本文假设您已经熟悉ESP8266的编程。
  • 本文假设您正在使用Arduino IDE。
  • 本文假设IDE已配置为编程基于ESP8266的模块。

概念化这个混乱的局面

我们的第一个任务是连接到WiFi。我们将从内部闪存中读取一个配置文件。其中将包含一个SSID和网络密码。

如果我们在过程中超时,我们将开始监听WPS信号。如果超时,我们将再次尝试连接到WiFi,该过程将重复进行。

如果成功使用了WPS,我们将SSID和网络密码写入闪存并重置设备。否则,我们将继续。

最后,我们将使用多播DNS (mDNS) 来发布我们的设备,以便其他人可以使用众所周知的本地域名找到它。

如果我们需要推送数据,理想情况下,我们将使用UDP多播,以便任何设备都可以在众所周知的本地多播地址上监听。UDP通常是传输传感器读数的理想选择。否则,如果设备要监听,它可以这样做,并且其他设备可以使用其众所周知的域名找到它。

编写这个混乱的程序

请务必将您的ESP8266模块设置为编程模式。我没有对这段代码进行分解,因为我想保持它易于复制和粘贴,而且在其中包含一堆函数原型会破坏它。我可能会在某个时候将其制作成一个库。

首先,我们将介绍我们的包含和全局变量

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <EEPROM.h>
//
// Your includes follow
//

// BEGIN only needed if using UDP:
#include <WiFiUdp.h>
WiFiUDP Udp;
#define UDPPORT 11011
#define UDPMULTICASTIP IPAddress(239,0,0,10)
// END only needed if using UDP

#define HOSTNAME "test"
char cfgssid[256];
char cfgpassword[256];
//
// Your globals follow
//

上面有三个必要的头文件。之后,您可以插入您自己的包含文件。然后,我们有一个可选的UDP部分。只有在使用UDP时才需要它。

之后,我们定义了将要发布的hostname,并且我们有SSID和网络密码的缓冲区。

setup() 方法非常复杂。您可能需要为自己的代码分解它。我们将分部分介绍它

void setup() {
  // Initialize the serial port
  Serial.begin(115200);
  
  // commit 512 bytes of ESP8266 flash 
  // this step actually loads the content (512 bytes) of flash into 
  // a 512-byte-array cache in RAM
  EEPROM.begin(512);
  int i = 0;
  // Read the settings
  for(i = 0;i<256;++i) {
    cfgssid[i]=EEPROM.read(i);
    if(!cfgssid[i])
      break;
  }
  cfgssid[i]=0;
  i=0;
  for(i = 0;i<256;++i) {
    cfgpassword[i]=(char)EEPROM.read(i+256);
    if(!cfgpassword[i])
      break;
  }
  cfgpassword[i]=0;
...

上面,我们初始化EEPROM库并分配512字节的存储空间。然后,我们读取我们的SSID - 一个小于256个字符的字符串。接下来,我们在偏移量256处读取我们的密码,但其他方面几乎相同。

现在进入WiFi处理,这有点复杂

 // Initialize the WiFi and connect
  WiFi.mode(WIFI_STA);
  bool done = false;
  while (!done) {
    // Connect to Wi-Fi
    WiFi.begin(cfgssid, cfgpassword);
    Serial.print("Connecting to WiFi");
    // try this for 10 seconds, then check for WPS
    for (int i = 0; i < 20 && WL_CONNECTED != WiFi.status(); ++i) {
      Serial.print(".");
      delay(500);
    }
    Serial.println("");
    // If we're not connected, wait for a WPS signal
    if (WL_CONNECTED != WiFi.status()) {
      Serial.print("Connection to ");
      Serial.print(cfgssid);
      Serial.println(" failed. Entering auto-config mode");
      Serial.println("Press the WPS button on your router");
      bool ret = WiFi.beginWPSConfig();
      if (ret) {
        String newSSID = WiFi.SSID();
        if (0 < newSSID.length()) {
          Serial.println("Auto-configuration successful. Saving.");
          strcpy(cfgssid, newSSID.c_str());
          strcpy(cfgpassword, WiFi.psk().c_str());
          int c = strlen(cfgssid);
          for(int i = 0;i<c;++i)
            EEPROM.write(i,cfgssid[i]);
          EEPROM.write(c,0);
          c = strlen(cfgpassword);
          for(int i = 0;i<c;++i)
            EEPROM.write(i+256,cfgpassword[i]);
          EEPROM.write(c+256,0);
          EEPROM.end();
          Serial.println("Restarting...");
          ESP.restart();
        } else {
          ret = false;
        }
      }
    } else
      done = true;
    // if we didn't get connected, loop
  }
  // Display the status
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(WiFi.SSID());
  Serial.print("Host name: ");
  Serial.print(HOSTNAME);
  Serial.println(".local");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
...

这个逻辑有点笨拙。我们要做的第一件事是连接。如果我们在10秒内无法连接,我们开始寻找来自路由器的WPS信号。如果超时,我们会回到尝试连接,并且循环重复。如果我们设法通过WPS连接,那么我们将新的SSID和密码写入闪存并重新启动设备。重新启动比尝试让WiFi设备重新连接更可靠。

现在一个简单但重要的步骤

  // start the multicast DNS publishing
  if (MDNS.begin(HOSTNAME)) {
    Serial.println("MDNS responder started");
  }
...

上面,我们使用我们的主机名初始化mDNS响应器。它的工作方式是,如果 HOSTNAME 是 "test",那么域名将是 "test.local"。

只有在使用UDP时才需要执行以下操作。之后是您自己的设置代码。

  // initialize the UDP
  // only needed if using UDP:
  Udp.begin(UDPPORT);

  //
  // Your setup code follows
  //
...

最后,我们进入我们的 loop() 方法。首先,如果我们的WiFi连接断开,我们尝试重新连接,如果无法重新连接,我们将重新启动。我们还会通过调用 MDNS.update() 定期刷新我们的mDNS注册。该代码下方的所有内容都是可选的,并作为示例提供,每四分之一秒多播一次 "Hello World!"

void loop() {
  // reconnect to the WiFi if we
  // got disconnected
  if (WL_CONNECTED != WiFi.status()) {
    // Connect to Wi-Fi
    WiFi.begin(cfgssid, cfgpassword);
    Serial.print("Connecting to WiFi");
    for (int i = 0; i < 20 && WiFi.status() != WL_CONNECTED; ++i) {
      Serial.print(".");
      delay(500);
    }
    if (WL_CONNECTED != WiFi.status()) {
      Serial.println("Could not reconnect. Restarting.");
      ESP.restart();
    }
  }
  // update the DNS information
  MDNS.update();
  
  // BEGIN only applicable if using UDP:
  Udp.beginPacketMulticast(UDPMULTICASTIP, UDPPORT, WiFi.localIP());
  Udp.print("Hello World!");
  Udp.endPacket();
  // END only applicable if using UDP

  //
  // Your code goes here
  //
  
  // we only want to do this every
  // quarter second for the example
  delay(250); 
}

关注点

ESP-01 模块非常便宜,我遇到过一些闪存无法正常工作的问题。

历史

  • 2020年11月5日 - 初始提交
© . All rights reserved.