Contents

Dash IoT Guide (ESP8266)

1 October 2024

This guide demonstrates how to make TCP and MQTT connections to an ESP8266 IoT device using the Arduino or PlatformIO IDE and the Dash Adruino library. It also shows how to load user controls (widgets) into your mobile device to monitor and control an IoT device.

The MQTT connection discussed in this guide is SSL/TLS enabled and we highly recommend that you only use SSL/TLS connections. The Dash MQTT server only accepts SSL connections. However, the ESP8266 does not include hardware for creating trust with certificates. Therefore, the MQTT connection is encrypted, but trust is not established.

Getting Started

For the big picture on DashIO, take a look at our website: dashio.io

For the Dash arduino library: github.com/dashio-connect/arduino-dashio

Requirements

Grab an ESP8266 board, Arduino IDE and follow this guide. This example should work on ESP8266 development boards available for the Arduino IDE.

You will need to install the Dash app on your mobile phone or tablet.

If you'd like to connect to your IoT devices through the Dash MQTT broker, setup an account on the dashio.io website.

Install

Arduino IDE

If you haven't yet installed the ESP8266 Arduino IDE support from espressif, please check the following link for instructions: https://github.com/esp8266/Arduino#installing-with-boards-manager

You will need to add the DashESP library into your project. It is included in the Arduino IDE Library manager. Search the library manager for the library titled "DashioESP" and install. The Arduino IDE may also ask you to install the following libraries (dependencies). Please make sure they are all installed from the Arduino IDE Library Manager.

  • Dashio is the core messageing library used to manage messages.
  • MQTT by Joël Gähwiler is required for MQTT messaging.
  • Preferences by Volodymyr Shymanskyy is used storing WiFi and login credentials etc.
  • NimBLE-Arduino by h2zero is required for BLE messaging for the ESP32. It is not required for the ESP8266.

The arduino-timer library is also used for the WiFi and MQTT connection and is available in the Arduino IDE Library Manager. Search the library manager for the library titled "arduino-timer" by Michael Contreras and install.

PlatformIO

Using Dash with the PlatformIO IDE is easy.

  1. Install the Dash library into your project. The easiest way to do this is to download the arduino-dashio library into the lib directory of your project.
  2. Install the libraries listed above using the PlatformIO Libraries manager.
  3. Edit the platformio.ini file for you project by adding lib_ldf_mode = deep to enable all the required files to be found and also add monitor_speed = 115200 to set the required serial monitor speed.

Your finished platformio.ini file should look something like this:

[env:huzzah]
platform = espressif8266
board = huzzah
framework = arduino
lib_deps = 
    contrem/arduino-timer@^2.3.1
    256dpi/MQTT@^2.5.0
    vshymanskyy/Preferences@^2.0.0
lib_ldf_mode = deep
monitor_speed = 115200

Guide

TCP Basics

Lets have a look at a TCP connection.

#include "DashioESP.h"

char WIFI_SSID[] = "yourWiFiSSID";
char WIFI_PASSWORD[] = "yourWiFiPassword";

DashDevice dashDevice("ESP8266_Type");
DashTCP    tcp_con(&dashDevice, true);
DashWiFi   wifi;

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    dashDevice.setup(wifi.macAddress(), "Jim Name"); // unique deviceID, and device name
    wifi.attachConnection(&tcp_con);
    wifi.begin(WIFI_SSID, WIFI_PASSWORD);
}

void loop() {
    wifi.run();
}

After the #include "DashioESP.h" we create a device with the device_type as its only attribute. We also create a TCP connection with DashTCP tcp_con(&dashDevice, true);, with the newly created device being an attribute to the connection. The second attribute of the TCP connection enables the received and transmitted messages to be printed to the serial monitor to make debugging easier.

By default, TCP port 5650 is used and only one TCP client can connect at a time. These values can be changed when initialising the TCP connection as follows:

DashTCP    tcp_con(&dashDevice, true, 5650, 5);

The third attribute specifies the port (5650 in this example) and the fourth is the number of TCP clients allowed (5 in this case). Increasing the number of TCP clients affects memory usage and the maximum value will depend on the capabilities of the processor you are using and how much memory your code uses.

In the setup() function we call the device setup with dashDevice.setup(wifi.macAddress(), "Jim Name");. The two parameters describe the device to the Dash app to allow it uniquely identify each device. The wifi object supplies the mac address to the device, to be used as a unique device_ID.

We then attach the TCP connection to the wifi with wifi.attachConnection(&tcp_con);. This enables the wifi object to manage the TCP connection from here on in.

We start the WiFi wifi.begin(WIFI_SSID, WIFI_PASSWORD); with the WiFi SSID and password. This starts the wifi and TCP connection and sets up the the mDNS (also called Bonjour or Zeroconf) to make the device discoverable on the Dash app.

wifi.run(); is used to regularly check that the WiFi is connected and manage the attached connection.

This device is discoverable by the Dash app. You can also discover your IoT device using a third party Bonjour/Zeroconf discovery tool. The mDNS service will be "_DashIO._tcp." and individual IoT devices on this service are identified by their mac address (device_ID).

In the code above, make sure you replace yourWiFiSSID with your WiFi SSID and yourWiFiPassword with your WiFI password. Setup the Arduino IDE serial monitor with 115200 baud and run the code. You will see messages on the Arduino serial monitor, and after a few attempts, your IoT device should connect to your WiFi and start the mDNS service. Then run the Dash app on your mobile device and you will see connection "WHO" messages on the Arduino serial monitor.

Lets add Dial control messages that are sent to the Dash app every second. We use a timer and send a Dial value message from the loop every second.

#include "DashioESP.h"
#include <arduino-timer.h>

char WIFI_SSID[] = "yourWiFiSSID";
char WIFI_PASSWORD[] = "yourWiFiPassword";

DashDevice dashDevice("ESP8266_Type");
DashTCP    tcp_con(&dashDevice, true);
DashWiFi   wifi;

auto timer = timer_create_default();
bool oneSecond = false; // Set by timer every second.

static bool onTimerCallback(void *argument) {
  oneSecond = true;
  return true; // to repeat the timer action - false to stop
}

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    dashDevice.setup(wifi.macAddress(), "Jim Name"); // unique deviceID, and device name
    wifi.attachConnection(&tcp_con);
    wifi.begin(WIFI_SSID, WIFI_PASSWORD);

    timer.every(1000, onTimerCallback); // 1000ms
}

void loop() {
    timer.tick();
    wifi.run();

    if (oneSecond) { // Tasks to occur every second
        oneSecond = false;
        tcp_con.sendMessage(dashDevice.getDialMessage("D01", int(random(0, 100))));
    }
}

The line tcp_con.sendMessage(dashDevice.getDialMessage("D01", int(random(0, 100)))); creates the message with two parameters. The first parameter is the control_ID which identifies the specific Dial control in the Dash app and the second attribute is simply the Dial value.

Once again, run the above code and the Dash app. This time, a new "DIAL" messages will be seen on the serial monitor every second.

The next step is to show the Dial values from the messages on a control on the Dash app.

In the Dash app, tap the All Devices button, followed by the Find New Device button. Then select the TCP Discovery option to show a list of new IoT devices that are TCP mDNS enabled. Your IoT device should be shown in the list. Select your device and from the next menu select Create Device View. This will create an empty Device View for your new IoT deivce. You can now add controls to the Device View:

Adding Controls to Dash App

Once you have discovered your IoT device in the Dash app and have a Device View available for editing, you can add controls to the Device View:

  • Start Editing: Tap the Edit button (it not already in editing mode).
  • Add Dial Control: Tap the Add Control button and select the Dial control from the list.
  • Edit Controls: Tap the Dial control to select it. The Control Settings Menu button will appear in the middle of the Control. The Control can then be dragged and resized (pinch). Tapping the button allows you to edit the Control settings where you can setup the style, colors and other characteristics of your Control. Make sure the Control_ID is set to the same value that is used in the Dial messages (in this case it should be set to "D01").
  • Quit editing: Tap the Edit button again.

The Dial on the Dash app will now show the random Dial values as they arrive.

The next piece of the puzzle to consider is how your IoT device can receive data from the Dash app. Lets add a Knob and connect it to the Dial.

In the Dash app you will need to add a Knob control onto your Device View, next to your Dial control. Edit the Knob to make sure the Control ID of the Knob matches what you have used in your Knob messages (in this case it should be "KB01"), then quit edit mode.

Continuing with the TCP example, in the Arduino code we need to respond to messages coming in from a Knob control that we just added to the Dash app. To make the changes to your IoT device we add a callback, processIncomingMessage, into the TCP connection with the setCallback function.

#include "DashioESP.h"

char WIFI_SSID[] = "yourWiFiSSID";
char WIFI_PASSWORD[] = "yourWiFiPassword";

DashDevice dashDevice("ESP8266_Type");
DashTCP    tcp_con(&dashDevice, true);
DashWiFi   wifi;

int dialValue = 0;

void processIncomingMessage(MessageData *messageData) {
    switch (messageData->control) {
    case knob:
        if (messageData->idStr == "KB01") {
            dialValue = messageData->payloadStr.toFloat();
            String message = dashDevice.getDialMessage("D01", dialValue);
            tcp_con.sendMessage(message);
        }
        break;
    }
}

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    tcp_con.setCallback(&processIncomingMessage);
    dashDevice.setup(wifi.macAddress(), "Jim Name"); // unique deviceID, and device name
    wifi.attachConnection(&tcp_con);
    wifi.begin(WIFI_SSID, WIFI_PASSWORD);
}

void loop() {
    wifi.run();
}

We obtain the Knob value from the message data payload that we receive in the processIncomingMessage function. We then create a Dial message with the value from the Knob and send this back to the Dash app. Remember to remove the timer and the associated Dial tcp_con.sendMessage from the loop() function.

When you adjust the Knob on the Dash app, a message with the Knob value is sent your IoT device, which returns the Knob value into the Dial control, which you will see on the Dash app.

Finally, we should respond to the STATUS message from the Dash app. STATUS messages allows the IoT device to send initial conditions for each control to the Dash app as soon as a connection becomes active. Once again, we do this from the processIncomingMessage function and our complete code looks like this:

#include "DashioESP.h"

char WIFI_SSID[] = "yourWiFiSSID";
char WIFI_PASSWORD[] = "yourWiFiPassword";

DashDevice dashDevice("ESP8266_Type");
DashTCP    tcp_con(&dashDevice, true);
DashWiFi   wifi;

int dialValue = 0;

void processStatus(ConnectionType connectionType) {
    String message((char *)0);
    message.reserve(1024);

    message = dashDevice.getKnobMessage("KB01", dialValue);
    message += dashDevice.getDialMessage("D01", dialValue);

    tcp_con.sendMessage(message);
}

void processIncomingMessage(MessageData *messageData) {
    switch (messageData->control) {
    case status:
        processStatus(messageData->connectionType);
        break;
    case knob:
        if (messageData->idStr == "KB01") {
            dialValue = messageData->payloadStr.toFloat();
            String message = dashDevice.getDialMessage("D01", dialValue);
            tcp_con.sendMessage(message);
        }
        break;
    }
}

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    tcp_con.setCallback(&processIncomingMessage);
    dashDevice.setup(wifi.macAddress(), "Jim Name"); // unique deviceID, and device name
    wifi.attachConnection(&tcp_con);
    wifi.begin(WIFI_SSID, WIFI_PASSWORD);
}

void loop() {
    wifi.run();
}

MQTT Basics

Now lets look at a MQTT connection. We also need the WiFi for MQTT.

#include "DashioESP.h"

// WiFi
char WIFI_SSID[] = "yourWiFiSSID";
char WIFI_PASSWORD[] = "yourWiFiPassword";

// MQTT
char MQTT_USER[] = "yourMQTTuserName";
char MQTT_PASSWORD[] = "yourMQTTpassword";

DashDevice dashDevice("ESP8266_Type");
DashMQTT mqtt_con(&dashDevice, true, true);
DashWiFi wifi;

void setup() {
    Serial.begin(115200);

    dashDevice.setup(wifi.macAddress(), "Bob Name"); // unique deviceID, and device name
    mqtt_con.setup(MQTT_USER, MQTT_PASSWORD);
    wifi.attachConnection(&mqtt_con);
    wifi.begin(WIFI_SSID, WIFI_PASSWORD);
}

void loop() {
    wifi.run();
}

Again we create a device and this time a MQTT connection, with the device being an attribute to the connection. The second attribute of the MQTT connection enables a push notification to be sent to your mobile phone when the IoT device reboots (Dash MQTT broker only). The final attribute enables the received and transmitted messages to be printed to the serial monitor.

In the setup() function we setup the device with two parameters. These parameters describe the device to the Dash app to allow it uniquely identify each device. The wifi object supplies the mac address to the device, to be used as a unique device_ID.

For MQTT connections, the device must be setup before we begin the wifi.

The MQTT connection setup requires the MQTT broker username and password, supplied in the mqtt_con.setup(MQTT_USER, MQTT_PASSWORD); method.

We then attach the MQTT connection to the wifi with wifi.attachConnection(&mqtt_con);. This enables the wifi object to control the MQTT connection from here on in.

Finally, we start the WiFi with wifi.begin(WIFI_SSID, WIFI_PASSWORD);, including parameters for the SSID and password.

wifi.run(); is used to regularly check that the WiFi is connected and to manage the attached connection.

This device is discoverable by the Dash app.

In the code above, make sure you replace yourWiFiSSID with your WiFi SSID, yourWiFiPassword with your WiFI password, yourMQTTuserName with you dash account username and yourMQTTpassword with your dash account password. Setup the Arduino IDE serial monitor with 115200 baud and run the code. You will see messages on the Arduino serial monitor, and after a few attempts, your IoT device should connect to your WiFi and then connect to the dash MQTT server.

Message processing for a MQTT connection is done exactly the same way as for the TCP connection, by adding the processIncomingMessage callback into the MQTT connection during setup() function. Our finished code, with Dial and Knob added is:

#include "DashioESP.h"

// WiFi
char WIFI_SSID[] = "yourWiFiSSID";
char WIFI_PASSWORD[] = "yourWiFiPassword";

// MQTT
char MQTT_USER[] = "yourMQTTuserName";
char MQTT_PASSWORD[] = "yourMQTTpassword";

DashDevice dashDevice("ESP8266_Type");
DashMQTT   mqtt_con(&dashDevice, true, true);
DashWiFi   wifi;

int dialValue = 0;

void processStatus(ConnectionType connectionType) {
    String message((char *)0);
    message.reserve(1024);

    message = dashDevice.getKnobMessage("KB01", dialValue);
    message += dashDevice.getDialMessage("D01", dialValue);

    mqtt_con.sendMessage(message);
}

void processIncomingMessage(MessageData *messageData) {
    switch (messageData->control) {
    case status:
        processStatus(messageData->connectionType);
        break;
    case knob:
        if (messageData->idStr == "KB01") {
            dialValue = messageData->payloadStr.toFloat();
            String message = dashDevice.getDialMessage("D01", dialValue);
            mqtt_con.sendMessage(message);
        }
        break;
    }
}

void setup() {
    Serial.begin(115200);

    dashDevice.setup(wifi.macAddress(), "Bob Name"); // unique deviceID, and device name
    mqtt_con.setup(MQTT_USER, MQTT_PASSWORD);
    mqtt_con.setCallback(&processIncomingMessage);
    wifi.attachConnection(&mqtt_con);
    wifi.begin(WIFI_SSID, WIFI_PASSWORD);
}

void loop() {
    wifi.run();
}

Run the Dash app once more. Tap the All Devices button, followed by the Find New Devices button. Then select the My Devices On Dash option to show a list of new IoT devices that have announced themselves to the dash server. Your device will be shown (named "Bob Name"). Select your IoT device to add the MQTT connection to your existing IoT device with the TCP connection.

For the next step, you will need to setup your Dash app with Dial and Knob controls, as described in the section above Adding Controls to Dash App.

When you adjust the Knob on the Dash app, a message with the Knob value is sent your IoT device, which returns the Knob value into the Dial control, which you will see on the Dash app.

For MQTT connections operating through the dash MQTT broker, a service is available for sending Alarms (push notifications) to your mobile device. These are handy for sending messages or alarms to the user when they are not currently using the Dash app. To send an alarm, simply call the MQTT connection's sendMessage method with two attrbutes; the first being any message String and the second having alarm_topic specified.

mqtt_con.sendMessage("message", alarm_topic);

Combining TCP and MQTT

The ESP8266 is capable of operating with both TCP and MQTT connections. It's relativelty easy to combine to code for both connections in one ESP8266 IoT device where each connection calls a common processIncomingMessage callback.

When combining connections, it is useful to create a generic sendMessage function to reply to an incoming message for a specific connection type. This prevents sending unnecessary reply messages to all connections:

void sendMessage(ConnectionType connectionType, const String& message) {
    if (connectionType == TCP_CONN) {
        tcp_con.sendMessage(message);
    } else {
        mqtt_con.sendMessage(message);
    }
}

Troubleshooting: When you have debugged your code, make sure to set your debug level back to None to minimise any effects of the debug mechanism on you code performance.

This is just the beginning and there is a lot more we can do. Take a look at the examples in the library to see more details.

Layout Configuration

Layout configuration allows the IoT device to hold a copy of the complete layout of the device as it should appear on the Dash app. It includes all infomration for the device, controls (size, colour, style etc.), device views and connections.

When the Dash app discovers a new IoT device, it will download the Layout Configuration from the IoT device to display the device controls they way they have been designed by the developer. This is particularly useful developing commercial products and distributing your IoT devices to other users.

To include the Layout Configuration in your IoT device, simply follow these steps:

  • Design your layout in the Dash app and include all controls and connections that you need in your layout.
  • Export the layout: Tap on the Device button, then tap the Export Layout button.
  • Select the provisioning setup that you want (see below for provisioning details) and tap the Export button. The Layout Configuration will be emailed to you.
  • Copy and paste the C64 configuration text from the email into your Arduino code, assigning it to a pointer to store the text in program memory. Your C64 configuration text will be different to that shown below.
  • Add the pointer to the C64 configuration text (configC64Str) as a second attribute to the DashDevice object.
  • Add the Layout Config Revision integer (CONFIG_REV) as a third attribute to the DashDevice object.
const char configC64Str[] PROGMEM =
"lVNNk5pAEP0r1hxyolKKcU28CShagiDOYlKpHFiZyEScIcPgx1r+9/Tw4ZqsOVhcmu7X3dPvdZ+RYzho8P2HhmZzzwDrjIqCxmiA"
"Zj1jX2yKpwWLwpVr73zfLsIN0lAuTykBwNwL3KEDjjVnUvB0aqkso91RGMJij6UnjwUkJVEOeCkKoqFddESDTrutoSwShMkyyQrL"
"JEHiMEoLwPYhLqks28wYf4FgQugmkUEkKUeD9kf9qUb4PKfgY4DEnq9aJ/zgUuaqRlXPU4O5Zvc+wdfT0BZqmzzlQj2CkKzlU7aF"
"GjGN0jFPU37Iy/Z1IeVu4N+ICgP2QGOZ3FTW0PFdP13v6/0uTE/hne0LsL10rKDiHY++4sqyprXLHfq1MZo/V5bj2ZUxDK3KWDom"
"rvGmUxtWGK5KDRuNDC+wRsFSiSRFCpyMQaslfYVYFzjeCBrDQMWO5Wig64o8UKXyNEOzYneFdP4Wu9ZNlTa4iIloyFklVJImst2w"
"uAkYSt7K/18sFhHLy+1Yn4At1bJJjtZKnWYzLJCj9aFVL4jKxypi8OOddk3oXXXFQQBCAyPdG+gbUZ3PGqIw9jzaqa7PGbqUYg2d"
"23P50g7Fb8OYYNueWklCR9ns13Fxcy7GMIC/TJA1zct97f5DZsnl4/ehWHjkPsrdpumVf1sQwtC9K+n01Acv4ZTJN3Gvmw+b8ULE"
"TQNzNMej4O4NPnIlBaMS5EDo9mDMSVCfCbYDf1KZBsbzxrLrmzHHcClnFJM9XZMlkUWmRjd9zV1gXL3aKmMhJYd6p39uArIH83L5"
"Aw==";

DashDevice    dashDevice(DEVICE_TYPE, configC64Str, CONFIG_REV);

Here is our Knob and Dial example, with TCP and MQTT connections and Layout Configuration added:

#include "DashioESP.h"

const char configC64Str[] PROGMEM =
"lVNNk5pAEP0r1hxyolKKcU28CShagiDOYlKpHFiZyEScIcPgx1r+9/Tw4ZqsOVhcmu7X3dPvdZ+RYzho8P2HhmZzzwDrjIqCxmiA"
"Zj1jX2yKpwWLwpVr73zfLsIN0lAuTykBwNwL3KEDjjVnUvB0aqkso91RGMJij6UnjwUkJVEOeCkKoqFddESDTrutoSwShMkyyQrL"
"JEHiMEoLwPYhLqks28wYf4FgQugmkUEkKUeD9kf9qUb4PKfgY4DEnq9aJ/zgUuaqRlXPU4O5Zvc+wdfT0BZqmzzlQj2CkKzlU7aF"
"GjGN0jFPU37Iy/Z1IeVu4N+ICgP2QGOZ3FTW0PFdP13v6/0uTE/hne0LsL10rKDiHY++4sqyprXLHfq1MZo/V5bj2ZUxDK3KWDom"
"rvGmUxtWGK5KDRuNDC+wRsFSiSRFCpyMQaslfYVYFzjeCBrDQMWO5Wig64o8UKXyNEOzYneFdP4Wu9ZNlTa4iIloyFklVJImst2w"
"uAkYSt7K/18sFhHLy+1Yn4At1bJJjtZKnWYzLJCj9aFVL4jKxypi8OOddk3oXXXFQQBCAyPdG+gbUZ3PGqIw9jzaqa7PGbqUYg2d"
"23P50g7Fb8OYYNueWklCR9ns13Fxcy7GMIC/TJA1zct97f5DZsnl4/ehWHjkPsrdpumVf1sQwtC9K+n01Acv4ZTJN3Gvmw+b8ULE"
"TQNzNMej4O4NPnIlBaMS5EDo9mDMSVCfCbYDf1KZBsbzxrLrmzHHcClnFJM9XZMlkUWmRjd9zV1gXL3aKmMhJYd6p39uArIH83L5"
"Aw==";

char WIFI_SSID[] = "yourWiFiSSID";
char WIFI_PASSWORD[] = "yourWiFiPassword";
char MQTT_USER[] = "yourMQTTuserName";
char MQTT_PASSWORD[] = "yourMQTTpassword";

DashDevice dashDevice("ESP8266_Type", configC64Str, 1);
DashTCP    tcp_con(&dashDevice, true);
DashMQTT   mqtt_con(&dashDevice, true, true);
DashWiFi   wifi;

int dialValue = 0;

void sendMessage(ConnectionType connectionType, const String& message) {
    if (connectionType == TCP_CONN) {
        tcp_con.sendMessage(message);
    } else {
        mqtt_con.sendMessage(message);
    }
}

void processStatus(ConnectionType connectionType) {
    String message((char *)0);
    message.reserve(1024);

    message = dashDevice.getKnobMessage("KB01", dialValue);
    message += dashDevice.getDialMessage("D01", dialValue);

    sendMessage(connectionType, message);
}

void processIncomingMessage(MessageData *messageData) {
    switch (messageData->control) {
    case status:
        processStatus(messageData->connectionType);
        break;
    case knob:
        if (messageData->idStr == "KB01") {
            dialValue = messageData->payloadStr.toFloat();
            String message = dashDevice.getDialMessage("D01", dialValue);
            sendMessage(messageData->connectionType, message);
        }
        break;
    }
}

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    dashDevice.setup(wifi.macAddress(), "Jim Name"); // unique deviceID, and device name

    tcp_con.setCallback(&processIncomingMessage);
    wifi.attachConnection(&tcp_con);
    
    mqtt_con.setup(MQTT_USER, MQTT_PASSWORD);
    mqtt_con.setCallback(&processIncomingMessage);
    wifi.attachConnection(&mqtt_con);

    wifi.begin(WIFI_SSID, WIFI_PASSWORD);
}

void loop() {
    wifi.run();
}

Provisioning

Provisioning is the method by which the Dash app can send user credentials and other setup information to the IoT device and the IoT device will store this data in non-volatile memory.

Details of the provisioning functionality available in an IoT device are contained in the Layout Configuration and must be setup when exporting the Layout Configuration from the Dash app.

A provisioning library is available within the Dash library for the ESP32 (DashioProvisionESP.h and DashioProvisionESP.cpp) which stores the following provisioning data to non-volatile memory:

  • Device Name
  • WiFi SSID
  • WiFI Password
  • Dash MQTT broker User Name
  • Dash MQTT broker Password

The library exposes the following structure to contain the provisioning data:

struct DeviceData {
    char deviceName[32];
    char wifiSSID[32];
    char wifiPassword[63];
    char dashUserName[32];
    char dashPassword[32];
    char saved;
};

Use the provisioning library as follows:

Include the library

#include "DashioProvisionESP.h"

Create a provisioning object

DashProvision dashProvision(&dashDevice);

Setup provisioning in the setup() function. For the TCP connection, we assign the provisioning information to the WiFi object or, for an MQTT connection, we assign the provisioning information to the WiFI object and MQTT connection object:

// setup
DeviceData defaultDeviceData = {DEVICE_NAME, WIFI_SSID, WIFI_PASSWORD, MQTT_USER, MQTT_PASSWORD};
dashProvision.load(&defaultDeviceData, &onProvisionCallback);

dashDevice.setup(wifi.macAddress());
    
mqtt_con.setup(dashProvision.dashUserName, dashProvision.dashPassword);
mqtt_con.setCallback(&processIncomingMessage);
    
wifi.attachConnection(&mqtt_con);
wifi.begin(dashProvision.wifiSSID, dashProvision.wifiPassword);

Here we update the provisioning object with the default provisioning data (using the DeviceData structure), then load the provisioning data. If the provisioning data has never been saved, then the the default provisionig data will be used. We also include a provision callback (discussed later).

Add provisioning process message in the processIncomingMessage callback from the example above becomes:

void processIncomingMessage(MessageData * messageData) {
    switch (messageData->control) {
    case status:
        processStatus(messageData->connectionType);
        break;
    case knob:
        if (messageData->idStr == "KB01") {
            dialValue = messageData->payloadStr.toFloat();
            String message = dashDevice.getDialMessage("D01", dialValue);
            sendMessage(messageData->connectionType, message);
        }
        break;
    default:
        dashProvision.processMessage(messageData);
        break;
    }
}

Your IoT device will let the Dash app know what provisioning services are available when it down loads the layout configuration.

And finally, here is the onProvisionCallback:

void onProvisionCallback(ConnectionType connectionType, const String& message, bool commsChanged) {
    sendMessage(connectionType, message);

    if (commsChanged) {
        mqtt_con.setup(dashProvision.dashUserName, dashProvision.dashPassword);
        wifi.begin(dashProvision.wifiSSID, dashProvision.wifiPassword);
    } else { // Must be a name change
        mqtt_con.sendWhoAnnounce(); // Update to announce topic with new name
    }
}

Dash app to let it know that provisioning is complete. And secondly, if the WiFi or MQTT connection credentials have changed, we need to restart the WiFi or MQTT connection. In the above example we call mqtt_con.setup to update the MQTT connection with the new username and password and wifi.begin to update wifi with the new SSID and password. Alternatively, you could reboot the processor for a fresh start which would also update the MQTT connection and WiFi credentials.

To provision your device, run the Dash app and tap the All Devices button and select your device. Tap the Provisioning button and follow the instructions.

The final provisioning feature is the ability to "Reset" your IoT device. The Dash app provisioning menu also includes a "Reset Device" button. In your IoT device, you will receive an incoming message of ControlType = resetDevice which you can manage in your processIncomingMessage callback as shown in the following example:

void processIncomingMessage(MessageData * messageData) {
    switch (messageData->control) {
    case status:
        processStatus(messageData->connectionType);
        break;
    case knob:
        if (messageData->idStr == "KB01") {
            dialValue = messageData->payloadStr.toFloat();
            String message = dashDevice.getDialMessage("D01", dialValue);
            sendMessage(messageData->connectionType, message);
        }
        break;
    case resetDevice:
        sendMessage(messageData->connectionType, dashDevice.getResetDeviceMessage());
        delay(2000); // Make sure the above message is sent before resetting
        ESP.restart();
        break;
    default:
        dashProvision.processMessage(messageData);
        break;
    }
}

SoftAP Provisioning

SoftAP enables the ESP8266 to be made into a wireless access point. This is particularly useful for provisioning WiFi, TCP and MQTT services because the ESP8266 does not contain BLE functionality (BLE is very useful for provisioning).

The SoftAP functionality is included in the DashioESP library and can be implemented follows:

#include <DashioESP.h>

char DEVICE_TYPE[] = "ESP8266_DashIO";
char DEVICE_NAME[] = "DashIO8266";

#define SOFT_AP_PWD    "" // Can be empty i.e. "" or must be a VALID password

DashDevice    dashDevice(DEVICE_TYPE);
DashSoftAP    soft_AP;
DashTCP       tcp_con(&dashDevice, true);

void setup() {
    Serial.begin(115200);
    delay(1000);

    dashDevice.setup(WiFi.macAddress(), DEVICE_NAME); // Get unique deviceID
    Serial.print(F("Device ID: "));
    Serial.println(dashDevice.deviceID);

    soft_AP.attachConnection(&tcp_con);

    if (!soft_AP.begin(SOFT_AP_PWD)) {
        Serial.println(F("Failed to start soft AP"));
    }
}

void loop() {
    soft_AP.run();
}

The DashSoftAP class behaves in a similar manner to the DashWiFi class. You simply attach a TCP connection to it and it will manage the connection to allow the Dash app to connect to it.

This device is discoverable by the Dash app. However, you will need to connect you phone or tablet WiFi to the SoftAP WiFi SSID which is called "Dash_Provision". You will then be able to discover your IoT device with the normal TCP discovery functionality within the Dash app (Devices/Find New Devices/TCP Discovery). After you have provisioned your device, connect your phone or tablet back to you normal WiFi.

For referency only, the SoftAP IP address is: 192.168.68.100 and uses port 55892 for discovery only on the Dash app. This address and port is independent of the address and port for the TCP connection attached to the SoftAP.

Take a look in the library example for DashIO_ESP8266_softAP, which incorporates full provisioning of WiFi, TCP and MQTT connections. A push button is used to switch to the SoftAP to enable provisioning. After the phone or tablet disconnects from the SoftAP, the IoT device switches back to the WiFi that you have provisioned.

Jump In and Build Your Own IoT Device

When you are ready to create your own IoT device, the Dash Arduino C++ Library will provide you with more details about what you need to know:

https://dashio.io/arduino-library/