Routing WiFi-enabled IoT Devices

While testing various projects using the ESP8266 WiFi chip I found myself wondering about the practicalities of manufacturing and distributing pre-programmed IoT devices to business and consumers alike. Sitting at a laptop connected to your IoT device of choice is relatively painless. You can sketch something up in C that reads sensors and does all kinds of neat stuff, sending that data into the cloud for consumption and analysis, but that all works fine while you’ve got your home or lab WiFi router SSID and Password coded into your sketch.

Once your project goes into manufacturing, that SSID and Password no longer applies, and it has to be configured somehow by the end-user to of course match whatever AP and security they have on-site. Chances are if your consumer IoT device was bulk-manufactured and pre-programmed in say China, there is a less than zero chance it will work out of the box. To complicate matters even more, customers tend to regularly change AP configurations, rendering your IoT device useless unless reconfigured or reprogrammed.

You of course have several options available to bypass this problem including:

  • Require the user to have an open AP for your device to connect to
  • Require the user to have a secure AP with a specific SSID and password available to match what is hardcoded in your device
  • Give the user the ability to reconfigure the device using their own machine and a USB cable, or maybe via Bluetooth
  • Give them a utility for configuration onto SD card, which is then installed in the device if supported

All of the above solutions creates a new set of problems:

  • Security needs to be dumbed down or
  • Painful reconfiguration is required by a specialist during installation or
  • Your IoT manufacturing cost now needs to include SD card and/or Bluetooth, when it will end up using WiFi and already include that in the manufacturing cost
  • Add an LCD screen with configuration buttons and a user manual in multiple languages – as simple as programming a VCR was back in the 80’s

An alternative approach is offered with the IoTLink platform (www.iotlink.net) which I shamelessly promote as it is my platform. With IoTLink, you can utilize a temporary AP (iPhone, Android, pocket WiFi router) on-site, either by a technician or through simple instructions to the end user. There are numerous apps available for both iPhone and Android which allows you to use them as a temporary AP where you can define the SSID and Password.

This “required” SSID and Password can be documented with your shipped device, allowing the user to find a temporary Internet connection, via temporary AP (phone), which then connects to IoTLink where both the device vendor as well as the end user are given the ability to whitelist and configure the IoT device to then link to whatever secure AP is available at the user site, whether at home or in a business scenario. It also allows reconfiguration and can collect a reasonable amount of diagnostic information for both end user and the device vendor.

There is a lot more information available at the IoTLink site, with a number of examples which shows how IoTLink can be incorporated into your project. As a quick example in this post, I will show how to program an ESP8266 hooked up to an Arduino Mega to:

  • Find the first available open AP and connect to it
  • Use it to look up proper secure customer AP information from the IoTLink directory
  • Disconnect from temporary AP and establish a secure connection to the customer AP. This example does not use SSL but IoTLink supports both SSL and open connections (for extremely limited devices or testing scenarios).

 

#include <ESP8266.h>

String SSID;
bool routed = false;

ESP8266 wifi(Serial2);

void setup(void)
{
  Serial2.begin(115200);
  Serial.begin(9600);  
     
  wifi.setOprToStationSoftAP();
}

void loop(void)
{
  if (!routed)
  {
    // find an open AP
    String apList = wifi.getAPList();

    bool haveOpenAP = false;
    if (apList.indexOf("+CWLAP:(0,\"") >=0) // find first open AP
    {
      haveOpenAP = true;
      apList = apList.substring(apList.indexOf("+CWLAP:(0,\"") + 11, apList.length() - (apList.indexOf("+CWLAP:(0,\"") + 11));
      apList = apList.substring(0, apList.indexOf("\""));
      if (wifi.joinAP(apList))
      {
        wifi.disableMUX();
        route();
      }
    }
  }
  
  // your code here

}

// IoTLink
void route(void)
{
  uint8_t buffer[1024] = {0};
  wifi.createTCP("www.iotlink.net", 80);
  char *hello = "GET /route?deviceid=DEV12345 HTTP/1.1\r\nHost: www.iotlink.net\r\nConnection: close\r\n\r\n";
  wifi.send((const uint8_t*)hello, strlen(hello));

  uint32_t len = wifi.recv(buffer, sizeof(buffer), 10000);
  if (len > 0)
  {
    char ssid_new[20]={0};
    char pass_new[20]={0};
    char *content = strstr((char *)buffer, "\r\n\r\n");
    if (content != NULL) content += 4;
    String sr(content);
    String ssidx = getValue(sr,',',0);
    String passx = getValue(sr,',',1);

    ssidx.toCharArray(ssid_new,20);
    passx.toCharArray(pass_new,20);
    wifi.leaveAP();
    delay(1000);
    wifi.joinAP(ssid_new, pass_new);
    routed = true;
  }
  wifi.releaseTCP();
}

// Helper
String getValue(String data, char separator, int index)
{
  int found = 0;
  int strIndex[] = {0, -1};
  int maxIndex = data.length()-1;

  for(int i=0; i<=maxIndex && found<=index; i++)
  {
    if(data.charAt(i)==separator || i==maxIndex)
    {
        found++;
        strIndex[0] = strIndex[1]+1;
        strIndex[1] = (i == maxIndex) ? i+1 : i;
    }
  }
  return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}
Advertisements