ESP8266 Device

An Arduino project for use with an ESP8266 device has been developed to act as an IoT device in a network. This project handles all of the protocols for you such as joining and discovery. The project only requires you to implement a few methods which determine the current status of a device as this will be unique to each product.

Installation

To begin, clone the repo for the ESP8266 project.

  • Download and install the Arduino IDE.

  • Download and install the Arduino ESP8266 filesystem uploader plugin for the Arduino IDE. This plugin is required to upload the device xml file onto the ESP8266 device. The xml must be stored in the file app/data/layout.xml.

  • Clone the repo and open it in the Arduino IDE. The only file that you should need to edit is app.ino.

Usage

The project requires you to implement the provided functions found in the DeviceStatus class. This class can be found in the main file app.ino and should be the only place where you will need to add your device specific code. This class will handle the state of your IoT device and will therefore be unique to each device. As an overview, the Status class contains several methods that must be overridden getStatus(), executeCommand(String elementID, String data), getMaxLeaseLength() and getMinUpdatePeriod(). All other functionality of the IoT device and DHAP protocol will be handled for you.

The implementation of these methods is up to you as it will depend on the functionality of your IoT device. For more information on how to structure status updates as well as the structure of IoT commands, see the status update packet and the command packet. This documentation describes the structure of the various network packets sent to and from you IoT device.

The user interface for your device is defined using the DHAP xml schema. This will be uploaded onto the ESP using Arduino ESP8266 filesystem uploader plugin and must be stored in the file found at app/data/layout.xml

DeviceStatus class

As this device is responsible for handling the state of your device, it will typically include several local variables and data structures to store an alter the devices state. For example, if the user interface of your device includes a stepper element, this would require a local int variable to increment and decrement the value when a new command is received.

The ESP8266 Repo contains several example devices in various branches. These devices can be used as a guide when developing your own device.

getStatus()

This method will be called each time the device sends a status update. The status update packet is formatted with the following syntax:

packetType|mac,lastUpdate,elementState,elementState,elementState...

The packetType, mac and lastupdate fields will be added for you in a status update. Therefore, your getStatus() method only needs to return the state of each UI element as a string of comma separated values. The status update packet page has information on how these values should be structured while the elements documentation specifies the state string of each element.

These comma separated values must be placed in the same order as the <status_location> tags specify in the devices xml. For example, for the following xml:

<group id="1" permisison="WR">
    <gui_element id="1">
        <type>switchtoggle</type>
        <disp_settings>On/Off</disp_settings>
        <status_location>1</status_location>
        <comment>Light Switch</comment>
    </gui_element>
</group>
<group id="2" permisison="WR">
    <gui_element id="1">
        <type>rangeinput</type>
        <disp_settings>Volume,Set,0,150</disp_settings>
        <status_location>2</status_location>
        <comment>Volume Slider</comment>
    </gui_element>
    <gui_element id="2">
        <type>selection</type>
        <disp_settings>Day of week,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday</disp_settings>
        <status_location>3</status_location>
        <comment>Door positions</comment>
    </gui_element>
</group>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

The initial state of the device might have the switchtoggle as false, the rangeinput with a value of 20 and the selection currently on option 3 (Thursday)

Therefore, if getStatus() is called while the device is in this state. It should return false,20,3.

executeCommand(String elementID, String data)

This function will be called each time a new IoT command is received from a control device. The two parameters relate to the Id of the UI element that the user interacted with on the control device and the new state of that element. The structure of the IoT commands can be seen in the Command Request packet.

From out previous example, if the selection element was changed from the option Thursday to Saturday, this function would be called with the parameters 2-2 and 5. This is because the Id of the selection element is 2-2 (groupID-elementID) and the index of the value Saturday is 5.

After this command is received, the local variable representing the state of the selection element should be changed to 5 so that the next time getStatus() is called, the value 5 will be used as the new state of the selection element.

getMaxLeaseLength()

This function should simply return the maximum lease length allowed by this device. Status updates in the DHAP protocol work off of leases of a limited duration. This method will be called when a control device requests a new lease. The value returned by this function will be used to cap how long a lease can exist for. This function should return a time in milliseconds.

getMinUpdatePeriod()

This function should simply return the minimum update period that the device will broadcast its current status. This function should return a time in milliseconds.

For example if this function returns the value 500 and a control device requests a status update lease with updates every 100ms. The lease will be granted but status updates will only be broadcast at a rate of 1 update every 500ms.

Arduino setup() and loop()

For a typical usage of this project, you should not need to alter the code in these methods. The standard usage of these functions only requires an instance of the IoTDevice class and the user defined DeviceStatus class. The IoTDevice class will handle all of the functions of the DHAP protocol such as joining, discovery and status updates. This class will simply use your user defined class when it calls the methods described above.

The IoTDevice class needs to be called in the setup() function to initialize the ESP into Access Point (AP) mode or to join a previously saved AP. The ioTDevice.setup(false, deviceStatus) takes two parameters which are a reference to the DeviceStatus instance and a boolean that forces the device into AP mode. If this is set to true, the device will enter AP mode on startup. If this is set to false, the device will attempt to join the last known network that it was joined to, If this fails the device will enter AP mode.

While in AP mode the device can be connected to from a control device and the joining procedure can be performed.

IoTDevice ioTDevice;
DeviceStatus deviceStatus;

void setup()
{
  Serial.begin(115200);
  Serial.println();
  
  ioTDevice.setup(deviceStatus, false);
}

void loop()
{
  ioTDevice.loop();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

XML file upload

Remember to upload the devices layout xml using the file system uploader plugin into a file named layout.xml onto the esp8266. This file is already included in the repo with an example device present. Instructions on how to upload a file to the ESP is found of the Arduino ESP8266 filesystem uploader plugin repo. It is also recommended to run any xml you have developed through an xml minifier to reduce the size of the file. It should be noted that when a new XML file is uploaded to the ESP, any current header or network credentials saved will be cleared. Therefore, the device will need to be rejoined to a network using the joining protocol.