Contents

Dash Arduino Examples

9 February 2024

Here are some simple examples that demonstrate how to use the Dash Arduino library with some of the commonly used controls. They have been created with the ESP32 in mind, but can easily be adapted other Arduino capable development boards as required. Cut and paste the code to get started with the examples.

There are more examples included in the arduino-dashio library.

LED Toggle (Button & TextBox)

Dash project to demonstrate toggling an LED on an ESP32 development board. It has the following features:

  • Tapping a Button/Indicator control on the Dash app toggles an LED on the ESP32 and increments counter.
  • The counter value is displayed in a TextBox control on the Dash app.
  • Communicates via BLE and MQTT connections.
  • Uses LED on pin 13, but can be changed as required.
  • Uses the serial monitor to show what is going on (115200 baud).
#include "DashioESP.h"

const char configC64Str[] PROGMEM =
"lVPbctowEP0VRq/1NFzTJm/4EqAYQ2yVdKb0wRdha2IkRpa5hOHfu5LthITmoaNhOOweLdqze07INV10//uPgabe3KxQ4Np+hbDz"
"CwM6obKkCbpH0Rc7zV5+7Pz9rT2kq5vBrp867BEZKOZMCp5PbGDhdgci21AQJnXAXqpAyags4BdASWVOFFN/GygjNM2kH0rK0X37"
"a2egTk1b8IJCnAHdm3uOuk0OcpjTVIUsx8OOD8E1F5tQvpGOzb2rqltBYlroim0DPUcJPm7J28U9TWT2eqnf/T741jHQ4aqcTvSh"
"85wXZBolcxYQBiJJUZIzaGdPahFnw0UNHO9nhdz5qALDpd1obuGab7k1sJfLJ61+IY9aLnMYTCwlthT5LDw8gOYBfYFMpwudpIIm"
"Fs/LDQOVu/DmIoMRVJHqWQZi5eaV0nk/tWpIqrTJRUIE8LhQf5qH8XOTeU5Z0iSeMipJnfiUjEXICr0K8VHrHdfMUUhZEXHBL9ah"
"vqi3wuSHpugsFBym9T55VVi17/M9NNbrXVAvRLo1EIWOvXCjx80ZQdWghrUFrLGP68Uf+YtxBU2MvUsPOPt59hDfRsHUWt3EZHWz"
"dLuDhatM0MxpPBmNXfhgiPH1uunDppvWSITHD3YxtV2iUkrOHBZGOUmacXH2qgFJaLlpBXkoScvMS/Ivh32wUa8N59psn7jq2jC9"
"O3Uqv1XT+V9vXMjtqqe1zDKPtOgmHtWusB7ACyeUkB2NSUBkuVXtPmJsmK56F6ysrXNLSvb12q5Tn+wAns9/AQ==";

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

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

int ledPin = 13;
const char *BUTTON_ID = "B01";
const char *TEXT_BOX_ID = "T01";

DashDevice dashDevice("ESP32_Type", configC64Str, 1);
DashBLE    ble_con(&dashDevice, true);
DashMQTT   mqtt_con(&dashDevice, true, true);
DashWiFi   wifi;

bool buttonValue = false;
int counter = 0;

void sendMessage(const String& message) {
    // Send all messages through both connections in this simple example.
    ble_con.sendMessage(message);
    mqtt_con.sendMessage(message);
}

void sendData() {
    String message = dashDevice.getButtonMessage(BUTTON_ID, buttonValue);
    message += dashDevice.getTextBoxMessage(TEXT_BOX_ID, String(counter));
    sendMessage(message);
}

void processButtonPress() {
    counter += 1;
    buttonValue = !buttonValue;
    digitalWrite(ledPin, buttonValue);
    sendData();
}

void processIncomingMessage(MessageData *messageData) {
    switch (messageData->control) {
    case status:
        sendData();
        break;
    case button:
        if (messageData->idStr == BUTTON_ID) {
            processButtonPress();
        }
        break;
    }
}

void setup() {
    pinMode(ledPin, OUTPUT);
    
    Serial.begin(115200);
    delay(1000);
    
    dashDevice.setup(wifi.macAddress(), "Toggle Button"); // 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);

    ble_con.setCallback(&processIncomingMessage);
    ble_con.begin();
}

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

Temperature Sensor (TimeGraph)

Dash project to demonstrate graphing data from a temperature sensor attached to an ESP32:

  • Demonstrates a TimeGraph control on the Dash app, which is updated every 10 minutes.
  • The temperature is also shown on a TextBox control on the Dash app.
  • Compatible with Dallas one-wire temperature sensors.
  • Communicates through a Dash MQTT connection.
  • Requires the Dash MQTT server for continuous temperature storage.
  • Tempreature sensor is attached to pin 13, but can be changed.
  • A BLE connection could be added for provisioning and to locallly connect.
  • Uses the serial monitor to show what is going on (115200 baud).
#include "DashioESP.h" // Dash ESP core library
#include "OneWire.h" 
#include "DallasTemperature.h" // Library for Dallas Temperature ICs. Supports DS18B20, DS18S20, DS1822, DS1820

#define DEVICE_TYPE "TempMonitor"
#define DEVICE_NAME "Temperature"

// WiFi
#define WIFI_SSID      "yourWiFiSSID"
#define WIFI_PASSWORD  "yourWiFiPassword"

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

#define GRAPH_UPDATE_SECONDS (60 * 10) // Every 10 mins

const char configC64Str[] PROGMEM =
"jVPbbuIwEP2Vys/JyiGBQt/iONCqELrBpSut9iEQFyxCjBynhVb9p/2G/bId59JC2UqrvMzlzFF8Zs4rGpMxuvr5y0K30ZTU0WxM"
"4zpi4Q8G0SsqS5GiK9Tz/aDfdYlNe8S3vbDv2IRSbLvukOKB5w06IUEWWspcK5ndUBhhBDtQ2iWK57qqBHWlzIUuIP3zO4BMC51x"
"A+fbHVeJLhWH6pqL1VrHiRYSXeFvTtfxLgcN+E4WAuo5DEXTKDQcfK/9TKxMKQgjFsZQfJRqm2gDup9AemjHWlL4NcWXoqiYINss"
"UnbY8Q/WZ5HqdYN2LLQ/J1hmsuC3i3Saz3gOMmlV8jdQj940Mk78uyYIo/s6Gk9HdeDPaat6wBp8MG4COp8/VPoX+lDJQ6YxDeOZ"
"0VirbJLshyD1TLxAz+taaKVEGsis3OagbKdjoWINuteV+r8slJfbd4hzuqtmM4aaSJVyBUCpoBHLQ5JdkKzkbXuzytMvu23jYS30"
"yQRTSV5Up7A81NI1SJIly83RGTRDzKRE7lvCKcyv+EXM00+AM2KjRCyf4Y2uewT9kMvpW0jA46NkWy1b5hzVS/MbQwTXMWtsMIrv"
"ro994PWGQScYEtsNKbW9Hu7bfRpimxLSw/6lGw6Jb47N34tiIkB6B58qfUPZ6B2Q7GFZ3X955NP9uz13gLtfmaUiGycLnp266two"
"ZyYwzoLvmCPW9RqqSlRuSaJAzO7HowwA/4892keaAQdjbFQmjEW1toSNGnMEQ7DEK0r5k1jyGdflDv5g8p0xVB0trepzwZ+bw31c"
"xfwJwre3vw==";

// Create device
DashDevice dashDevice(DEVICE_TYPE, configC64Str, 1);

// Create Connections
DashMQTT mqtt_con(&dashDevice, true, true);
DashWiFi wifi;

// Create Control IDs
const char *TEMPTB_ID = "TB01";
const char *GRAPH_ID = "IDTG";

OneWire oneWire(13); // Temperature sensor connected to pin 13
DallasTemperature tempSensor(&oneWire);

int graphSecondsCounter = 0;
float sampleTempC;
float temperatureC;
float tempSum = 0;

bool oneSecond = false;

void processStatus() {
    String message = dashDevice.getTextBoxMessage(TEMPTB_ID, String(temperatureC));
    message += dashDevice.getTimeGraphLine(GRAPH_ID, "L1", "Avge Temp", line, "red", yLeft);
    mqtt_con.sendMessage(message);
}

void processIncomingMessage(MessageData *messageData) {
    switch (messageData->control) {
    case status:
        processStatus();
        break;
    default:
        break;
    }
}

void setTemperatureEverySecond(float temperature) {
    String messageToSend = "";
    if (temperature > -100) { // i.e. no an error
        temperatureC = temperature;
        messageToSend = dashDevice.getTextBoxMessage(TEMPTB_ID, String(temperatureC));

        // Now calculate the average temperature and send every 10 minutes. Dash server will store this.
        tempSum += temperatureC;
        graphSecondsCounter++;
        if (graphSecondsCounter >= GRAPH_UPDATE_SECONDS) {
            float tempFiltered = tempSum / GRAPH_UPDATE_SECONDS;
            messageToSend += dashDevice.getTimeGraphPoint(GRAPH_ID, "L1", tempFiltered);

            String text = "The temperature is " + String(tempFiltered) + "°C";

            graphSecondsCounter = 0;
            tempSum = 0;
        }
    } else {
        messageToSend = dashDevice.getTextBoxMessage(TEMPTB_ID, "Err");
    }
    mqtt_con.sendMessage(messageToSend);
}

void oneSecondTimerTask(void *parameters) {
    for(;;) {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        oneSecond = true;
    }
}

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

    dashDevice.setup(wifi.macAddress(), DEVICE_NAME); // unique deviceID
    
    mqtt_con.setup(MQTT_USER, MQTT_PASSWORD);
    mqtt_con.setCallback(&processIncomingMessage);
    mqtt_con.addDashStore(timeGraph, GRAPH_ID);
        
    wifi.attachConnection(&mqtt_con);
    wifi.begin(WIFI_SSID, WIFI_PASSWORD);
    
    // Setup 1 second timer task for measuring the temperature.
    // This is way more often than necessary, but isn't a problem.
    xTaskCreate(
        oneSecondTimerTask,
        "One Second",
        1024, // stack size
        NULL,
        1, // priority
        NULL // task handle
    );

    // Start temperature sensor conversion
    tempSensor.setWaitForConversion(false);
    tempSensor.begin();
    tempSensor.requestTemperaturesByIndex(0);
}

void loop() {
    wifi.run();

    if (oneSecond) {
        oneSecond = false;
    
        if (tempSensor.isConversionComplete()) {
            sampleTempC = tempSensor.getTempCByIndex(0);
            tempSensor.requestTemperaturesByIndex(0);
        }
        setTemperatureEverySecond(sampleTempC);
    }
}

Simple DMX (Color Picker)

Dash project to demonstrate controlling a SparkFun ESP32 Thing Plus DMX to LED Shield with a DMX controlled RGB LED light:

  • Demonstrates the use of the CololPicker control on the Dash app.
  • Communicates via TCP connection only.
  • A BLE connection could be added for provisioning but should use a switch to change to TCP only once provisioning is done. This is because BLE and DMX together use most of the ESP capability. However, this may change in the future when faster ESP32s become available.
  • Uses the serial monitor to show what is going on (115200 baud).
#include "DashioESP.h"
#include <SparkFunDMX.h>

#define DEVICE_TYPE "ESP32_DMX"
#define DEVICE_NAME "Basic DMX"

// WiFi
#define WIFI_SSID      "yourWiFiSSID"
#define WIFI_PASSWORD  "yourWiFiPassword"

const PROGMEM char *configStr = 
"xVVdc6IwFP0rTl6X6RbtuF3fBCzSIihQ3NmPB5SoWTHphGBlO/73vYHQ9WunfdqFEU/uvbnJPfcEXpBruKj37YeGHjzfqFHoWkGN"
"osGXqEaWo0yj/liBgfdYI9e3a9CPrSaDGal405XgBZVjlhNBGA0SeKLetYaeSSpWaqhrSBCR4SYK9ZDnewOkoaeEYyocyIysWAdD"
"UZAUBrf+aviwzD9H3z/6LL3Xo6/lfTecQMCcUcFZVk0x5YwqMwyi6h9Skvka81CUlXU6HAxcsOaYpj7NSp8GOMNJDr5FkuVYQ7vz"
"va8wWa5EM77qXsOt7yVRcTytCs5VeqMfOqbcleDZKNndweZC8gs8ehvyLDlJTZYVG5qjXhtYyFdQb21BPcELWJ4Wm9cQ/bi8mhGZ"
"2mA8xRziGJeLZsl83XjWS5o2jumKCKwcjc0im5bNk/IwPuIJzSvq52VV8FwF2wmh+YxxdoFXObvCBts1yUcJZ9DOY+dZdklDwJ6h"
"wE7nIPSArK6GCFTuJRu5oscoRhXdTl/p1xwGSquRHYyHNTSiyKu6oUQDIu8XvPg5YRvf8gpjFJsTvpTNV90aOvbQhV8ENrZYNFXY"
"HGN6Ii2jDYZZIQSjA5rMMpw2DWP0tA9nIj7RT/tG3ueM/uVMnB0mJUCYgHdCLmL46PiAXV91quuCml89BwSbnOU52muvzN1vb21/"
"Qv3JZ3ITFx8eZyE2rXcw58o6W+G6bBlZgU8p1N+m0AetLHErAN+/5/H9JF6Q5x/yxjcP+I46TrBrT8ei67Sn2Esmb5N3UXadtzkL"
"SbbF/D/w1c+ylu+9n7Wrrrw+HZEXwdu5OttGZKtvjXkHX5gXlOItmeMQi+JJxpljVL0crcocE/ysXpCLZYC3APf73w==";

DashioDevice dashDevice(DEVICE_TYPE, configStr, 1);

// DashIO comms connections
DashWiFi wifi;
DashTCP  tcp_con(&dashDevice, true);

// Create Control IDs
const char *COLOR_PICKER = "C1";
const char *AUTO_BUTTON = "B1";
const char *DBO_BUTTON = "B2";
const char *ALL_ON_BUTTON = "B3";

HardwareSerial dmxSerial(2);
uint8_t enPin = 21; // Enable pin for DMX shield (Free pin on Thing Plus or Feather pinout)
uint16_t numChannels = 7; // Number of DMX channels, can be up tp 512

SparkFunDMX dmx;
bool tick = false;
int angle = 0;
bool autoButton = true;
int count = 0;

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

    message = dashDevice.getButtonMessage(AUTO_BUTTON, autoButton, "pause", "Manual");
    message += dashDevice.getButtonMessage(DBO_BUTTON, true);
    message += dashDevice.getButtonMessage(ALL_ON_BUTTON, true);
    tcp_con.sendMessage(message);
}

void sendAutoButton() {
    String text;
    String icon;
    if (autoButton) {
        text = "Manual";
        icon = "pause";
    } else {
        text = "Auto";
        icon = "play";
    }
    tcp_con.sendMessage(dashDevice.getButtonMessage(AUTO_BUTTON, autoButton, icon, text));
}

void processButton(MessageData *messageData) {
    if (messageData->idStr == AUTO_BUTTON) {
        autoButton = !autoButton;
        sendAutoButton();
    } else if (messageData->idStr == DBO_BUTTON) {
        autoButton = false;
        writeDMXcolor(0, 0, 0); // Blackout
        sendColorHex(0, 0, 0);
        sendAutoButton();
    } else if (messageData->idStr == ALL_ON_BUTTON) {
        autoButton = false;
        writeDMXcolor(255, 255, 255); // All On
        sendColorHex(255, 255, 255);
        sendAutoButton();
    }
}

void processColorPicker(MessageData *messageData) {
    autoButton = false;
    sendAutoButton();

    char charbuf[8];
    messageData->payloadStr.toCharArray(charbuf,8);
    long int rgb=strtol(charbuf + 1, 0, 16);
    byte r=(byte)(rgb>>16);
    byte g=(byte)(rgb>>8);
    byte b=(byte)(rgb);
    writeDMXcolor(r, g, b);
}


void processIncomingMessage(MessageData *messageData) {
    switch (messageData->control) {
    case status:
        processStatus(messageData->connectionType);
        break;
    case colorPicker:
        processColorPicker(messageData);
        break;
    case button:
        processButton(messageData);
        break;
    default:
        break;
    }
}

void sendColorHex(int red, int green, int blue) {
    // Convert RGB to hex value to send to Dash app.

    long rgb = ((long)red << 16) | ((long)green << 8 ) | (long)blue;
    String colorHex = String(rgb + 16777216, HEX); // Adding 16777216 puts a 1 at the start of the string and correct padding of zeros for red
    colorHex.setCharAt(0, '#'); // Replace the 1 at the start with the # char
    
    tcp_con.sendMessage(dashDevice.getColorMessage(COLOR_PICKER, colorHex));
}

void writeDMXcolor(int red, int green, int blue) {   
Serial.println("DMX: " + String(red));
    // Update DMX channels to new brightness
    
    // This is just an example of a DMX device that uses 7 channels. We are just using R, G, & B in this example.
    dmx.writeByte(255, 1);   // Master dimmer
    dmx.writeByte(red, 2);   // red
    dmx.writeByte(green, 3); // green
    dmx.writeByte(blue, 4);  // blue
    dmx.writeByte(0, 5);     // White
    dmx.writeByte(0, 6);     // Strobe
    dmx.writeByte(0, 7);     // Change colous

    dmx.update();
}

void updateAutoValues() {
    // Calculate new RGB values for smooth color change, based on the "angle" variable
    
    int red, green, blue;
    float rads;

    // calc RGB levels from angle
    rads = float(angle) * 3.1415926 / 180;

    red = int((sin(rads) + 1) * 255) / 2;
    green = int((sin(rads - (120*PI/180)) + 1) * 255) / 2;
    blue = int((sin(rads - (240*PI/180)) + 1) * 255) / 2;
    writeDMXcolor(red, green, blue);

    count++;
    if (count >= 10) {
        count = 0;
        sendColorHex(red, green, blue);
    }

    // Update angle for next time
    angle++;
    if (angle >= 360) {
        angle = 0;
    }
}

void timerTask(void *parameters) {
    // 100ms timer for DMX
    
    for(;;) {
        vTaskDelay(100 / portTICK_PERIOD_MS);
        tick = true;
    }
}

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

    dmxSerial.begin(DMX_BAUD, DMX_FORMAT); // Begin DMX serial port
    dmx.begin(dmxSerial, enPin, numChannels);
    dmx.setComDir(DMX_WRITE_DIR); // Set communicaiton direction

    dashDevice.setup(wifi.macAddress(), DEVICE_NAME); // Get unique deviceID
    tcp_con.setCallback(&processIncomingMessage);
    wifi.attachConnection(&tcp_con);
    wifi.begin(WIFI_SSID, WIFI_PASSWORD);

    // Setup 100 ms timer task for updating the DMX
    xTaskCreate(
        timerTask,
        "100ms",
        1024, // stack size
        NULL,
        1, // priority
        NULL // task handle
    );
}

void loop() {
    wifi.run();

    if (tick) {
        tick = false;

        if (autoButton == true) {
            updateAutoValues();
        } else {
            dmx.update(); // Send DMX values every 100ms
        }
    }
}