Contents

DashIO IoT Guide (Adafruit Bluefruit SPI Friend)

9 Feb 2022

So, what is DashIO? It is a quick effortless way to connect your IoT device to your phone. It allows easy setup of controls such as Dials, Text Boxes, Maps, Graphs, Notifications..., from your IoT device. You can define the look and layout of the controls on your phone from your IoT device. There are three methods to connect to your phone; Bluetooth Low Energy (BLE), TCP or MQTT. What's Dash then? Dash is a mqtt server with extra bits added in to allow you to send notifications, share your devices, and save your settings from your phone via the DashIO app.

This guide demonstractes how to make a BLE connection with an Adafruit Bluefruit SPI friend using the Arduino IDE.

Getting Started

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

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

Requirements

Grab an Adafruit Bluefruit SPI friend, Arduino IDE and follow this guide.

You will need to install the DashIO app on your pobile phone or tablet. And if you'd like to connect to one IoT device for free, setup a free DashIO account from within the DashIO app.

Install

DashIO

You will need to add the DashIO library into your project. Download the DashIO library from GitHub with the following link: https://github.com/dashio-connect/arduino-dashio

You will need to copy the following files from the arduino-dashio library into your project directory:

  • DashioBluefruitSPI.h
  • DashioBluefruitSPI.cpp
  • bluefruitConfig.g

BLE

You will need to install the Adafruit library for the Bluefruit SPI friend, which are available in the Arduino IDE Library Manager. Search for the library "Adafruit BluefruitLE nRF51" and install.

Guide

BLE Basics

Lets start with a simple BLE connection.

#include "DashioBluefruitSPI.h"

#define DEVICE_TYPE "BF_SPI_BLE_Type"
#define DEVICE_NAME "Jill Name"

DashioDevice dashioDevice(DEVICE_TYPE);
DashioBluefruit_BLE  ble_con(&dashioDevice, true);

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

    dashioDevice.name = DEVICE_NAME;
    ble_con.begin(true);
}

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

This is about the fewest number of lines of code to get talking to the DashIO app. There is a lot happening under the hood to make this work. After the include we create a device with the device_type as its only attribute. We also create a BLE connection, with the newly created device being an attribute to the connection. The second attribute of the BLE connection enables the received and transmitted messages to be printed to the serial monitor.

In the setup function we assign the device name to the device and start the BLE connection ble_con.begin(true);. The device_ID is automagically obtained from the macAddress of the Bluefruit SPI Friend.

The "true" attribute of the ble_conn.begin(true); causes the Bluefruit SPI friend to perform a factory reset to make sure everything is in a known state. This can be set to "false" when you have finished your code.

This device is discoverable by the DashIO app. You can also discover your IoT device using a thiry party BLE scanner (e.g. BlueCap). The name of your IoT device will be a concatentation of "DashIO_" and the device_type.

Setup the Arduino IDE serial monitor with 115200 baud and run the above code. Then run the DashIO app on your mobile device and you will see connection "WHO" messages on the Arduino serial monitor.

Troubleshooting: Occasionally, the DashIO app is unable to discover a BLE connection to the IoT device. If this occurs, try deleting the the IoT device from the Bluetooth Settings of your phone or tablet.

If you like, you can assign your own device_ID by changing the BLE connection begin function call with the following:

dashioDevice.deviceID = "myUniqueDevieID";
ble_con.begin(true, false);

Lets add Dial control messages that are sent to the DashIO app every second. To do this we create a new task to provide a 1 second time tick and then send a Dial value message from the loop every second.

#include "DashioBluefruitSPI.h"

#define DEVICE_TYPE "BF_SPI_BLE_Type"
#define DEVICE_NAME "Jill Name"

DashioDevice dashioDevice(DEVICE_TYPE);
DashioBluefruit_BLE  ble_con(&dashioDevice, true);

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

    dashioDevice.name = DEVICE_NAME;
    ble_con.begin(true);
}

void loop() {
    ble_con.run();
    ble_con.sendMessage(dashioDevice.getDialMessage("D01", int(random(0, 100))));
    delay(1000);
}

The dashioDevice.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 DashIO app and the second parameter is simply the Dial value.

Once again, run the above code and the DashIO 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 DashIO app.

It would be nice to have the DashIO app automagically setup a new Device View and place your control on the new Device View. To do that we need to add a few more lines of code. Most importantly, we need to respond to messages coming in from the DashIO app. To do this we add a callback into the BLE connection with the setCallback function:

#include "DashioBluefruitSPI.h"

#define DEVICE_TYPE "BF_SPI_BLE_Type"
#define DEVICE_NAME "Jill Name"

DashioDevice dashioDevice(DEVICE_TYPE);
DashioBluefruit_BLE  ble_con(&dashioDevice, true);

void processConfig(ConnectionType connectionType) {
    String message((char *)0);
    message.reserve(2048);

    message += dashioDevice.getConfigMessage(DeviceCfg(1)); // One Device Views

    DialCfg first_dial_control("D01", "DV01", "Dial", {0.24, 0.14, 0.54, 0.26});
    message += dashioDevice.getConfigMessage(first_dial_control);

    DeviceViewCfg deviceView("DV01", "A Dial", "up", "0");
    message += dashioDevice.getConfigMessage(deviceView);

    ble_con.sendMessage(message);
}

void processIncomingMessage(MessageData *messageData) {
    switch (messageData->control) {
    case config:
        processConfig(messageData->connectionType);
        break;
    }
}

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

    dashioDevice.name = DEVICE_NAME;
    ble_con.setCallback(&processIncomingMessage);
    ble_con.begin(true);
}

void loop() {
    ble_con.run();
    ble_con.sendMessage(dashioDevice.getDialMessage("D01", int(random(0, 100))));
    delay(1000);
}

The processIncomingMessage callback checks the message type and calls the appropriate function. At this stage we are only interested in CONFIG messages and the processConfig function is called appropriately. This part of the code creates quite long messages, so we allocate enough space for the message, 2048 bytes in this case. We create and send three config response messages:

  1. Device: lets the DashIO app know there is a device with one Device View.
  2. Dial: includes the control_ID of the Dial, the control_ID of the Device View it will be displayed on, the title and the position of the dial on the Device View.
  3. DeviceView: includes the control_ID, title, icon name and colour of the Device View.

Run the DashIO app once more. This time you will need to load the configuration data from your device to display the Dial Control. Tap the All Devices button, followed by the Find New Devices button. Then select the BLE Scan option to show a list of new IoT devices that are BLE enabled. Your device will be shown (named "Jill Name"). Select your IoT device and follow the instructions to load the Device View with a Dial Control.

The next piece of the puzzle to consider is how we receive data from the DashIO app. Lets add a Knob and connect it to the Dial. We do this by adding the Knob to the processIncomingMessage function and send the Knob value to the Dial in the processConfig function. And remember to remove the timer and the horrible delay from the loop:

#include "DashioBluefruitSPI.h"

#define DEVICE_TYPE "BF_SPI_BLE_Type"
#define DEVICE_NAME "Jill Name"

DashioDevice dashioDevice(DEVICE_TYPE);
DashioBluefruit_BLE  ble_con(&dashioDevice, true);

void processConfig(ConnectionType connectionType) {
    String message((char *)0);
    message.reserve(2048);

    message += dashioDevice.getConfigMessage(DeviceCfg(1)); // One Device Views

    DialCfg first_dial_control("D01", "DV01", "Dial", {0.24, 0.14, 0.54, 0.26});
    message += dashioDevice.getConfigMessage(first_dial_control);

    KnobCfg aKnob("KB01", "DV01", "Knob", {0.24, 0.42, 0.54, 0.26});
    message += dashioDevice.getConfigMessage(aKnob);

    DeviceViewCfg deviceView("DV01", "Dial & Knob", "up", "black");
    message += dashioDevice.getConfigMessage(deviceView);
    
    ble_con.sendMessage(message);
}

void processIncomingMessage(MessageData *messageData) {
    switch (messageData->control) {
    case config:
        processConfig(messageData->connectionType);
        break;
    case knob:
        if (messageData->idStr == "KB01") {
            String message = dashioDevice.getDialMessage("D01", (int)messageData->payloadStr.toFloat());
            ble_con.sendMessage(message);
        }
        break;
    }
}

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

    dashioDevice.name = DEVICE_NAME;
    ble_con.setCallback(&processIncomingMessage);
    ble_con.begin(true);
}

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

Run the DashIO app and then you will need to reload the configuration from your IoT device; Tap the All Devices button and select your device, then tap the Get Layout button. Follow the instructions to update the Device View to contain both a Dial and Knob Control. When you adjust the Knob on the DashIO 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 DashIO app.

Finally, we should respond to the STATUS message from the DashIO app. STATUS messages allows the IoT device to send initial conditions for each control to the DashIO 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 "DashioBluefruitSPI.h"

#define DEVICE_TYPE "BF_SPI_BLE_Type"
#define DEVICE_NAME "Jill Name"

DashioDevice dashioDevice(DEVICE_TYPE);
DashioBluefruit_BLE  ble_con(&dashioDevice, true);

int dialValue = 0;

void processConfig(ConnectionType connectionType) {
    String message((char *)0);
    message.reserve(2048);

    message += dashioDevice.getConfigMessage(DeviceCfg(1)); // One Device Views

    DialCfg first_dial_control("D01", "DV01", "Dial", {0.24, 0.14, 0.54, 0.26});
    message += dashioDevice.getConfigMessage(first_dial_control);

    KnobCfg aKnob("KB01", "DV01", "Knob", {0.24, 0.42, 0.54, 0.26});
    message += dashioDevice.getConfigMessage(aKnob);

    DeviceViewCfg deviceView("DV01", "Dial & Knob", "up", "black");
    message += dashioDevice.getConfigMessage(deviceView);
    
    ble_con.sendMessage(message);
}

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

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

    ble_con.sendMessage(message);
}

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

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

    dashioDevice.name = DEVICE_NAME;
    ble_con.setCallback(&processIncomingMessage);
    ble_con.begin(true);
}

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

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.

Jump In and Build Your Own IoT Device

When you are ready to create your own IoT device, the following links will provide you with more details about what you need to know.

Devices & Connections

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

Messaging for All Controls

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

Configuration for All Controls

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