As a side project that folds in 3D printing, digital electronics and softwareI have been working on a series of Internet of Things projects based around Arduino and the ESP8266.
The ESP8266 is a great platform for tinkering with the Internet of Things. In its essence it is an Arduino with WiFi support and a number of open source libraries that make working with its hardware relatively simple using C and C++ code.
Over the last couple of years I’ve developed a number of prototype devices based on the ESP8266 and as part of that effort I’ve abstracted out a set of core IoT functionality that simplifies and accelerates development of devices on this platform. The ESP8266 has a relatively underpowered processor making it a poor choice on which to base commercial products but it is an excellent platform for home-brew projects and proof of concept devices.
Adafruit in particular produce an excellent prototyping board in their Feather range which includes a MicroUSB port for programming and power, as well as a socket for a LiPo battery as an independent power source (which is also charged when there is power available from the USB port).
Built on top of the Arduino library for the ESP8266 (https://github.com/esp8266/Arduino) it provides a simplified yet flexible API to some key features:
The initial reason for the library was the desire to not have to hard-code WiFi connection details into every Arduino application I wrote - not least because it made it very difficult to move devices between, say, my home test network and a temporary network run from my phone. This meant being able to implement a human-friendly way of configuration.
The library has been designed around two modes of operation, configuration mode and client mode. On power-up or a hard reset the library checks its configuration to decide which mode it starts up in: If there are no client WiFi network details specified, or the 'start in configuration mode' flag is set, it will start in configuration mode. Otherwise, it will start in client mode.
These modes refer primarily to the way the WiFi hardware is configured - in client mode the ESP8266 expects to connect to a client WiFI network, while in configuration mode the ESP8266 operates as a hotspot. Other than not being connected to a wider network (and not having internet access) there is no reason why a device using this library can’t perform its intended functions whilst in configuration mode as well as in client mode, within certain limits.
In configuration mode the ESP8266 configures the WiFi as a hotspot running WPA2 security. A default SSID and password are generated by a simple algorithm although these can be overridden. Connecting to the hotspot allows you to access a web page through which you can configure the WiFi network SSID and password that the device should use when in client mode, the network configuration (DHCP or user-supplied IP address, subnet mask and gateway), as well as any application-specific parameters that have been defined (see below). The web page is accessed by going to the address "http://configure" in your browser address bar (this gets resolved to the hotspot's IP address by an on-board DNS server). Once set you can reboot the device into client mode - either by power cycling the device, pressing reset or performing a soft reset through the web page.
In client mode the ESP8266 starts up as a WiFi client and joins a WiFi network using the previously configured SSID and password. Once connected it can then interact with other devices and services on the network using HTTP and, if necessary, allow other devices to interact with it via the built-in web server. It is also possible to edit some or all of the application-specific parameters that have been defined, by heading to the configuration page, the URL for which is simply the device’s IP address with a “/configure” suffix - for example http://192.168.0.21/configure - and any parameter marked as editable in client mode can be changed.
The way you write an Arduino application that makes use of this library is designed to be as simple as possible. A bare bones application looks like this:
#include <ESP8266IotCore.h> void setup() { // Don't call begin() until you have finished all your other configuration. iotCore.begin(); } void loop() { // These two lines are all that are required in the loop) method. Add your own code before the call to delay(). iotCore.handleRequest(); delay(10); }
The delay isn’t strictly necessary - but long delays should be avoided otherwise it will interfere with the asynchronous nature of some of the library’s functions, causing unreliable behaviour. In any event Arduino programming should be using an event based approach.
As already mentioned it is possible to configure one or more application parameters that can be set via a web page and then read by your own application code:
#include <ESP8266IotCore.h> #define EXAMPLE_PARAM_1 "colour" #define EXAMPLE_PARAM_1_LENGTH 5 #define EXAMPLE_PARAM_1_DEFAULT "red" #define EXAMPLE_PARAM_2 "size" #define EXAMPLE_PARAM_2_LENGTH 6 #define EXAMPLE_PARAM_2_DEFAULT "medium" #define EDITABLE true // This method is called every time you save changes to application parameters. void onConfigurationChange() { Serial.println("Configuration change:"); Serial.println(String(" parameter ") + EXAMPLE_PARAM_1 + ": " + iotCore.getApplicationParam(EXAMPLE_PARAM_1)); Serial.println(String(" parameter ") + EXAMPLE_PARAM_2 + ": " + iotCore.getApplicationParam(EXAMPLE_PARAM_2)); } void setup() { Serial.begin(115200); // Define two application parameters iotCore.addApplicationConfigParam(EXAMPLE_PARAM_1, PARAM_TYPE_STRING, INPUT_TYPE_STRING, EXAMPLE_PARAM_1_LENGTH, EXAMPLE_PARAM_1_DEFAULT, !EDITABLE); iotCore.addApplicationConfigParam(EXAMPLE_PARAM_2, PARAM_TYPE_STRING, INPUT_TYPE_STRING, EXAMPLE_PARAM_2_LENGTH, EXAMPLE_PARAM_2_DEFAULT, EDITABLE); // Register an onChange method so that changes to parameters can be picked up on-the-fly. iotCore.setOnConfigurationChange(onConfigurationChange); // Don't call begin() until you have finished all your other configuration. iotCore.begin(); Serial.println("Configuration on start-up:"); Serial.println(String(" parameter ") + EXAMPLE_PARAM_1 + ": " + iotCore.getApplicationParam(EXAMPLE_PARAM_1)); Serial.println(String(" parameter ") + EXAMPLE_PARAM_2 + ": " + iotCore.getApplicationParam(EXAMPLE_PARAM_2)); } void loop() { iotCore.handleRequest(); delay(10); }
Anyone with access to the configuration page can edit the value of EXAMPLE_PARAM_2 and when the change is saved there is the option to take immediate action by registering an onConfigurationChange handler as shown here.
On start-up the library checks the parameter storage space to see if the contents matches the collection of system and application space parameters that it is aware of, and will automatically initialise it if necessary. This neatly side-steps the “how do I populate the EEPROM parameters in the first place” headache.
I’ve also added a feature to the library that allows you to program a ‘spare’ ESP8266 with an automatic configuration program. After flashing and powering up a new node simply power up your spare ESP8266, it finds the new node, configures it for your network and then instructs it to re-boot. A great way to automate the rapid setting up of a network of devices.
There are other features not mentioned here (for example, helper code to generate HTTP responses) and I’ve taken the library quite far although there are still features I’d like to add before considering open sourcing it:
Equally, some features could be moved out into a separate library - for example all those relating to the web server, and some of the non-library code I’ve written (especially around network time servers and date and time calculation) could also be moved into other libraries for easier re-use.
Finally I’d like to find time to explore ways of automating the testing of the library.
Aside from writing the core library and a series of example Arduino programs to demonstrate its key features I have also developed a number of IoT gadgets:
I will shortly be publishing more details of these projects - I've been so busy with the day job that they have yet to be written up properly.