Toby Eggitt
Toby Eggitt

Reputation: 1872

Bootstrap WiFi credentials into ESP8266

I suspect this is a problem that already has a "stock" solution; I need to deploy an ESP8266 device such that it will be able to connect to a secured local WiFi, but I can't know the credentials at the time the software is embedded on the device. Somehow, I'll need to be able to have the end user, with a regular computer (i.e. one that has a keyboard and screen!) "push" this information into the device. For preference, I'd like them to be able to do this over wireless, but a USB/Serial connection would be possible if it has to be done that way.

I guess I could just "bundle" the entire toolchain and source and have them run a script that prompts for this info, builds and uploads the software over USB. But given the ESP's ability to act as an AP, I can't help thinking there's a better way, and that someone has probably already packaged this!

(I suppose a side question worth asking is whether there might be some kind of peer-peer configuration that would be preferable. I want the ESP to have bidirectional, reliable, (i.e. TCP socket-like) communication with the computer host (it doesn't really need Internet access). But I don't really want anything that might compromise the security of that computer, so sticking with the end-user's regular WiFi seems likely the best option.)

What should I investigate? Or what suggestions does anyone have? Or what keyword search would have found me the answer!?

TIA!

Upvotes: 2

Views: 1177

Answers (2)

Luiz Menezes
Luiz Menezes

Reputation: 819

I use the following solution. Assuming you are using the arduino API to program your ESP8266

  • Set the wifi mode to AP_STA

    WiFi.mode(WIFI_AP_STA);

  • Config the softAP (you can either hardcode the SSID and password or use an config file with a default value)

    IPAddress apIP(192, 168, 1, 1); WiFi.mode(WIFI_AP); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); WiFi.softAP(apname, password);

  • (optional) Using the DNSServer library, you set an DNS server

  • Using the ESP8266WebServer library, you set an HTTP server

  • Call the onNotFound method of your server object, passing a closure that searches for the requested file, either on SPIFFs or on SD (or both, should you work with both interfaces) and streams it.

    server->onNotFound
      (
        [&]()
        {
          if(!handleFileRead(server->uri()))
            server->send(404, "text/plain", "FileNotFound");
        }
      ); 
    
    • Call the on method so that it expects for an file named networks.json (you can give another name). This methods streams an json string with all available networks SSIDs, it signal levels and security modes. To use this code, you should include the ArduinoJson library

    server->on("/networks.json", & { String json; int numNetworks, i, j, rssi; StaticJsonBuffer<4096> jsonBuffer; JsonArray& networks = jsonBuffer.createArray(); for(j = 0; j < 10; j++) { numNetworks = WiFi.scanNetworks(); Serial.print(numNetworks); Serial.println(" networks"); for (j=0; j

          if (WiFi.encryptionType(j) != 7)
            network["auth"] = 0;
          else
            network["auth"] = 1;
    
          rssi = WiFi.RSSI(j);
    
          if (rssi < -90)
            network["signal"] = 1;
          else if (rssi < -80)
            network["signal"] = 2;
          else if (rssi < -70)
            network["signal"] = 3;
          else if (rssi < -67)
            network["signal"] = 4;
          else if (rssi < -30)
            network["signal"] = 5;
        }
      }
      networks.printTo(json);
      server->send(200, "application/json", json);
      Serial.println("handleFileRead: /networks.json");
    }
    

    );

  • Call the on method again to expect the configurations:

    server->on("/configure", & { //Handler //Add a new line File config; File file = SPIFFS.open("/resp.htm", "r"); SPIFFS.remove(configFile); StaticJsonBuffer<512> jsonBuffer; JsonObject& root = jsonBuffer.createObject(); config = SPIFFS.open(configFile, "w"); // add some backup logic later if (!file) { server->send(404, "text/plain", "File Not Found"); return; }

    for (int i = 0; i < server->args(); i++) { root[server->argName(i)] = server->arg(i); if(server->argName(i).equals("ssid")) { root["wifissid"] = server->arg(i); }

    if(server->argName(i).equals("password"))
    {
      root["wifipassword"] = server->arg(i);
    }
    

    }

    server->streamFile(file, "text/html");

    root.printTo(config); file.close(); config.close(); }

    • Create an webpage that reads a file called /networks.json to populate a list of available networks within a form. When the user selects one network, make the page ask for a password (I shall not provide code for this, since there are literally millions of ways of doing this). You should use javascript on this page. If you want to use any fancy javascript addon, such as jquery, bootstrap or angular, you can use it, but remember that the ESP8266HttpServer library may misbehave for larger files (I managed to compress jquery to 27kb and got no issue on this size). The form should GET to "/config".
  • You will get a neat configuration file with a JSON that you can use to configure the STA SSID and Password.

    configWifi(){ File config; int filelenght; int taglen; int result; unsigned char mac[6]; char * buffer = NULL; StaticJsonBuffer<1024> jsonBuffer; wifiConnection = new WiFiClient(); WiFi.mode(WIFI_STA); WiFi.macAddress(mac); hex2str(this->clientid, (char *)mac, 6); config = SPIFFS.open(configFile, "r"); if (!config) { config.close(); return; } if (config.size() < 10) { config.close(); return; }
    buffer = (char *) calloc(config.size() + 1, 1); config.readBytes(buffer, config.size()); config.close(); Serial.println(buffer); JsonObject& root = jsonBuffer.parseObject(buffer); if (!root.success()) { config.close(); free(buffer); return; } strcpy(wifi_ssid, root["wifissid"]); strcpy(wifi_password, root["wifipassword"]); Serial.print("SSID = "); Serial.print(wifi_ssid); Serial.print(" Pass = "); Serial.println(wifi_password); WiFi.begin(wifi_ssid, wifi_password); free(buffer); Serial.println("initialized");
    return; }

That is it. The user will get an captive page to configure his wifi connection.

Upvotes: 0

MatteKarla
MatteKarla

Reputation: 2737

The easy way is to use a third party library like WiFiManager: https://github.com/tzapu/WiFiManager

example usage: http://www.instructables.com/id/Avoid-Hard-Coding-WiFi-Credentials-on-Your-ESP8266/

Upvotes: 5

Related Questions