Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Automatic Network Configuration of ESP8266 IoT Devices

0.00/5 (No votes)
5 Nov 2020 1  
A walkthrough and source code for automatically configuring your ESP8266 IoT devices
This article presents a technique for creating IoT devices with no physical interface or devices that can otherwise automatically hop on a network and publish their services without user intervention. All that the device needs is for the user to press the WPS button on the router.

Introduction

Recently, I made a device that needed to sit in a small shed in the middle of the woods and report sensor readings. Due to the fact that it was isolated, it was not appropriate for the device to have a user interface, not even a button. It needed to broadcast a sensor reading to any listening machine and it needed to provide a small website at a known address. In the process of creating this, I wrote some code for automatically configuring an ESP8266 and publishing it on the network. This article endeavors to explain the process and provide code for implementing this in your own IoT projects. While it's designed for the ESP8266, the concepts are transferable to other platforms. It should work on any ESP8266 module, even an ESP-01.

Prerequisites

  • This article assumes you are already familiar with programming the ESP8266.
  • This article assumes you are using the Arduino IDE.
  • This article assumes the IDE is configured for programming ESP8266 based modules.

Conceptualizing this Mess

The first task we have is connecting to the WiFi. We will read a configuration file from the internal flash memory. This will contain an SSID and a network password.

If we time out in the process, we will start listening for a WPS signal. If that times out, we will try to connect to WiFi again and the process will repeat.

If we successfully used WPS, we write the SSID and network password to the flash and reset the device. Otherwise, we will simply continue.

Finally, we will use Multicast DNS (mDNS) to publish our device such that others can locate it using a well known local domain name.

If we need to push data, ideally we will use UDP multicast so that any device can listen in on a well known local multicast address. UDP is often ideal for transmitting sensor readings. Otherwise, if the device is to listen, it can do so and other devices can find it using its well known domain.

Coding this Mess

Be sure to set your ESP8266 module into program mode. I haven't factored this code because I wanted to keep it easy to copy and paste and having a bunch of function prototypes in there undermines it. I may make this into a library at some point.

First, we'll cover our includes and globals:

#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
//

Above has three necessary header files. After that, you can insert your own includes. Then, we have a UDP section that is optional. You only need it if you will be using UDP.

After that, we define the host name we'll publish and we have buffers for the SSID and network password.

The setup() method is pretty complicated. You may want to factor it some for your own code. We're going to cover it in sections:

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;
...

Above, we initialize the EEPROM library and allocate 512 bytes of storage. Then, we read our SSID - a string that's less than 256 characters. Next, we read our password at offset 256 but otherwise it's pretty much the same.

Now on to the WiFi handling which is somewhat complicated:

 // 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());
...

The logic for this is somewhat awkward. The first thing we try to do is connect. If we can't connect within 10 seconds, we start looking for a WPS signal from the router. If that times out, we go back to trying to connect and the cycle repeats. If we manage to connect via WPS, then we write the new SSID and password to flash and reboot the device. Rebooting is more reliable than trying to just get the WiFi gadget to reconnect.

Now a simple but important step:

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

Above, we initialize the mDNS responder with our hostname. This works such that if HOSTNAME is "test" the domain will be "test.local".

The following only needs to be done if there is UDP. After that comes your own setup code.

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

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

Finally, we get to our loop() method. First we try to reconnect if our WiFi connection got dropped and we restart if we couldn't reconnect. We also refresh our mDNS registration periodically by calling MDNS.update(). Everything below that code is optional and provided as a sample, which multicasts "Hello World!" once ever quarter of a second:

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); 
}

Points of Interest

The ESP-01 modules are very cheap and I've had problems with the flash not working properly on some of them.

History

  • 5th November, 2020 - Initial submission

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here