Contents

Dash IoT Guide (Arduino MKR NB 1500)

1 October 2024

This guide demonstrates how to an Arduino MKR NB 1500 to the Dash app using the Arduino or PlatformIO IDE and the DashIO Adruino library. It also shows how to load user controls (widgets) into your mobile device to monitor and control an IoT device.

Getting Started

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

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

Requirements

The Dash server is used to enable communication between the MKR NB 1500 and you mobile phone or tablet, so you will need Dash subscription.

Grab an Arduino MKR NB 1500 board, Arduino IDE and follow this guide.

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

You will all need a mobile SIM card that is suitable for communicating through LTE Cat M1/NB1.

Install

Arduino IDE

You will need to add the DashioMKR1500 library into your project. It is included in the Arduino IDE Library manager. Search the library manager for the library titled "DashioMKR1500" 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.
  • MKRNB is required for LTE connectivity.
  • MQTT by Joël Gähwiler is required for MQTT messaging.

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:mkrnb1500]
platform = atmelsam
board = mkrnb1500
framework = arduino
lib_deps = 
    arduino-libraries/MKRNB@^1.5.1
    256dpi/MQTT@^2.5.1
lib_ldf_mode = deep
monitor_speed = 115200

Guide

MQTT Over LTE Basics

Lets start with a simple MQTT connection.

#include <DashioMKR1500.h>

#define DEVICE_TYPE "SAMD LTE"
#define DEVICE_NAME "ArdyWardy"

// MQTT
#define MQTT_USER      "yourMQTTuserName"
#define MQTT_PASSWORD  "yourMQTTpassword"

DashDevice dashDevice(DEVICE_TYPE);
DashMQTT mqtt_con(&dashDevice, true, true);
DashLTE lte(true);

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
    
    Serial.begin(115200);
    delay(2000);

    dashDevice.name = DEVICE_NAME;

    mqtt_con.setup(MQTT_USER, MQTT_PASSWORD);

    lte.attachConnection(&mqtt_con);
    lte.begin();
}

void loop() {
    digitalWrite(LED_BUILTIN, lte.cellConnected);

    lte.run();
}

This is about the fewest number of lines of code necessary to get talking to the Dash app. There is a lot happening under the hood to make this work. After the #include " DashioMKR1500.h" we create a device with the device_type as its only attribute. We also create a MQTT connection, with the newly created 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.

An LTE (Cat M1/NB1) connection object is created with DashLTE lte(true);, where the attribute enables printing messages to the serial monitor.

In the setup() function we set the built in LED to be used to show the connection status of the LTE connection, start the serial monitor and assign the device name to the dashDevice object.

At this point in the code you can also assign your own deviceID with dashDevice.setup("yourDeviceID"). However, if you don't set the deivceID, the IMEI from the LTE modem will be assigned to the deviceID.

The dash MQTT connection is then setup with a username and password. Make sure you enter your username and password correctly where they are defined at the top of the code example.

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

Finally, we start the lte connection lte.begin().

This device is discoverable by the Dash app.

Setup the Arduino IDE serial monitor to 115200 baud and run the above code. Then run the Dash app on your mobile device and you will see connection "WHO" messages on the Arduino serial monitor as the Dash app detects and communicates with your IoT device.

Lets add Dial control messages that are sent to the Dash 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.

// This example uses an Arduino MKR NB 1500 board

#include <DashioMKR1500.h>

#define DEVICE_TYPE "SAMD LTE"
#define DEVICE_NAME "ArdyWardy"

// MQTT
#define MQTT_USER      "yourMQTTuserName"
#define MQTT_PASSWORD  "yourMQTTpassword"

DashDevice dashDevice(DEVICE_TYPE);
DashMQTT mqtt_con(&dashDevice, true, true);
DashLTE lte(true);

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

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

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
    
    Serial.begin(115200);
    delay(2000);

    dashDevice.name = DEVICE_NAME;

    mqtt_con.setup(MQTT_USER, MQTT_PASSWORD);

    lte.attachConnection(&mqtt_con);
    lte.begin();

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

void loop() {
    digitalWrite(LED_BUILTIN, lte.cellConnected);

    timer.tick();
    lte.run();

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

The line 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 parameter 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 My Devices on Dash option to show a list of new IoT devices on the Dash server. Your IoT device shpuld 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 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 MQTT connection with the setCallback function.

// This example uses an Arduino MKR NB 1500 board

#include <DashioMKR1500.h>

#define DEVICE_TYPE "SAMD LTE"
#define DEVICE_NAME "ArdyWardy"

// MQTT
#define MQTT_USER      "yourMQTTuserName"
#define MQTT_PASSWORD  "yourMQTTpassword"

DashDevice dashDevice(DEVICE_TYPE);
DashMQTT mqtt_con(&dashDevice, true, true);
DashLTE lte(true);

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);
            mqtt_con.sendMessage(message);
        }
        break;
    }
}

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
    
    Serial.begin(115200);
    delay(2000);

    dashDevice.name = DEVICE_NAME;

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

    lte.attachConnection(&mqtt_con);
    lte.begin();
}

void loop() {
    digitalWrite(LED_BUILTIN, lte.cellConnected);

    lte.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 ble_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:

// This example uses an Arduino MKR NB 1500 board

#include <DashioMKR1500.h>

#define DEVICE_TYPE "SAMD LTE"
#define DEVICE_NAME "ArdyWardy"

// MQTT
#define MQTT_USER      "yourMQTTuserName"
#define MQTT_PASSWORD  "yourMQTTpassword"

DashDevice dashDevice(DEVICE_TYPE);
DashMQTT mqtt_con(&dashDevice, true, true);
DashLTE lte(true);

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() {
    pinMode(LED_BUILTIN, OUTPUT);
    
    Serial.begin(115200);
    delay(2000);

    dashDevice.name = DEVICE_NAME;

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

    lte.attachConnection(&mqtt_con);
    lte.begin();
}

void loop() {
    digitalWrite(LED_BUILTIN, lte.cellConnected);

    lte.run();
}

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.
  • 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 Layout Config Revision integer (CONFIG_REV) as a third attribute to the DashDevice object.
const char configC64Str[] PROGMEM =
"vVNNc9owEP0rGZ09HWOapMPNxoFQbAxGIe10elCwgC1CYmQ5QDL896z8kbhJLr10fGDZfXor7dv3TKIgIr1fvx0yniQBRs+kKCAj"
"PaK+//wKMzmcTeHb+q5j9vEf1pkRh+TmJDgCJkka+xEmlkoarcQoxOQ4cDsWw2WWSHFKZMoFZznijS64Q3bsSHod13XInmkuTXko"
"XNgzmmcLJgqEXmPZgCm70PLXIRsO641JmQFFeu6XrotfjZqqHDAtER0klCaxvcBGHWKQsW23YiLH1qcG90ZyZT+HbKV66CuhNDLE"
"TCukckgGTAyUEOqQj7HePMCmG+xQc26hB8jM5pX10iHHD60879q7xgvvAK/pnnHe8yhMq8nTmx+0isJRnYr9aR3cTO6qKEqGVeAv"
"wiqYR31a4/tRHYSLxX2pYqNS4M9HfSuS0QKnMUCt5vCElY6HU15ryPA1xU7mpOd5dm4oS5VpXiyL3Suk87falXCWOlA64/ptMOzU"
"FLZrmTX5+w0YXheaXMj09uL9AaqZzMsNWZ5wXrZrzcxA5g9KK/JxRezpMg7UsWEPBFtu39U+kNsppKgz6XW7LWhrVrgkgA+fsF25"
"+Upyci718qO2Z9bjweVlMB2ledGd319lT3v/sGp7JvBT/LfXfAl5ubHeu4GW7vlfJilXHMSrFDFk0hJcBLbDZ45xa8q9AmnaeldG"
"wFV54LrVKroZ0M/d+A+eKSQY1IaQtn36t2ltGjpMp7dVGFA6aaJh7aD+AH3zTDL+CEs+56bYI5VEIZ0DrMDJWL5x4hml1fXDErYA"
"fqi3fbVO+SOG5/ML";

DashDevice    dashDevice(DEVICE_TYPE, configC64Str, CONFIG_REV);

Here is our Knob and Dial example, with MQTT connection through LTE and Layout Configuration added:

// This example uses an Arduino MKR NB 1500 board

#include <DashioMKR1500.h>

#define DEVICE_TYPE "SAMD LTE"
#define DEVICE_NAME "ArdyWardy"

// MQTT
#define MQTT_USER      "yourMQTTuserName"
#define MQTT_PASSWORD  "yourMQTTpassword"

const char configC64Str[] PROGMEM =
"vVNNc9owEP0rGZ09HWOapMPNxoFQbAxGIe10elCwgC1CYmQ5QDL896z8kbhJLr10fGDZfXor7dv3TKIgIr1fvx0yniQBRs+kKCAj"
"PaK+//wKMzmcTeHb+q5j9vEf1pkRh+TmJDgCJkka+xEmlkoarcQoxOQ4cDsWw2WWSHFKZMoFZznijS64Q3bsSHod13XInmkuTXko"
"XNgzmmcLJgqEXmPZgCm70PLXIRsO641JmQFFeu6XrotfjZqqHDAtER0klCaxvcBGHWKQsW23YiLH1qcG90ZyZT+HbKV66CuhNDLE"
"TCukckgGTAyUEOqQj7HePMCmG+xQc26hB8jM5pX10iHHD60879q7xgvvAK/pnnHe8yhMq8nTmx+0isJRnYr9aR3cTO6qKEqGVeAv"
"wiqYR31a4/tRHYSLxX2pYqNS4M9HfSuS0QKnMUCt5vCElY6HU15ryPA1xU7mpOd5dm4oS5VpXiyL3Suk87falXCWOlA64/ptMOzU"
"FLZrmTX5+w0YXheaXMj09uL9AaqZzMsNWZ5wXrZrzcxA5g9KK/JxRezpMg7UsWEPBFtu39U+kNsppKgz6XW7LWhrVrgkgA+fsF25"
"+Upyci718qO2Z9bjweVlMB2ledGd319lT3v/sGp7JvBT/LfXfAl5ubHeu4GW7vlfJilXHMSrFDFk0hJcBLbDZ45xa8q9AmnaeldG"
"wFV54LrVKroZ0M/d+A+eKSQY1IaQtn36t2ltGjpMp7dVGFA6aaJh7aD+AH3zTDL+CEs+56bYI5VEIZ0DrMDJWL5x4hml1fXDErYA"
"fqi3fbVO+SOG5/ML";

DashDevice dashDevice(DEVICE_TYPE, configC64Str, 1);
DashMQTT mqtt_con(&dashDevice, true, true);
DashLTE lte(true);

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() {
    pinMode(LED_BUILTIN, OUTPUT);
    
    Serial.begin(115200);
    delay(2000);

    dashDevice.name = DEVICE_NAME;

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

    lte.attachConnection(&mqtt_con);
    lte.begin();
}

void loop() {
    digitalWrite(LED_BUILTIN, lte.cellConnected);

    lte.run();
}

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/