Internet of Stranger Things using MQTT

Grant Russell, Junior Developer

Internet of Stranger Things using MQTT

Stranger Things is a science fiction-horror web TV show. I have never actually seen the show, however, I know that it features a very interesting concept of a wall covered in party lights and letters of the alphabet. This wall used for communicating with the supernatural.

Can we build a wall which allows our Newcastle office to communicate with our London office?

There are various projects online which set out to build a version of this wall, however, most resembled the set-up of a web page running on a raspberry pi, which controlled LED's directly from the pins. I wanted to remove the need for a Raspberry pi and use a much cheaper micro-controller, whilst pushing more control into the cloud.

This article will describe the basic set-up that I used to create this wall for our Newcastle office to communicate with the London office. This was only a prototype for a bit of fun and to prove the reliability of MQTT, but there are lots of ways this could be expanded to add functionality, purpose or security. I have also simplified the code to the bare basics.

Hardware

Below is the hardware, I purchased for this project. You could use any Arduino based microcontroller, however, I really recommend looking at the NodeMCU, as it's incredibly cost effective and has a WiFi component built in. As for the lights; any WS1811 or WS1812 addressable LEDs will work well.

MQTT

The heart of this project is MQTT. This is a very simple protocol used commonly in the Internet of Things, due to its speed and simplicity. Essentially MQTT is broken into a few basic concepts:

  • Broker - This is the server which receives and forwards messages between devices.
  • Topic - A broker can have multiple topics. A topic is essentially a channel for the devices to talk on.
  • Publish - Devices can publish messages to a topic on the broker.
  • Subscribe - Devices can listen to a topic for any incoming messages.

There are a few free MQTT brokers hosted in the cloud (here, here and here), but it is also straightforward to set up your own using mosquitto.

Building the interface

The interface was a simple HTML page with letters of the alphabet. I didn't spend very much time on the presentation of this page, but it allows the user to click any letter. The markup contained two data tags for LED index and the colour index.

<span class="letter" data-led="5" data-color="1">A</span>
<span class="letter" data-led="7" data-color="2">B</span>
<span class="letter" data-led="8" data-color="3">C</span>
<span class="letter" data-led="10" data-color="4">D</span> 

I then used this Javascript library for connecting to the MQTT broker along with the following code.

$(document).ready(function () {
    // Create a client instance
    client = new Paho.MQTT.Client("broker.mqttdashboard.com", Number(8000), "clientId-" + sessionId);

    // set callback handlers
    client.onConnectionLost = onConnectionLost;
    client.onMessageArrived = onMessageArrived;

    // connect the client
    client.connect({ onSuccess: onConnect });
});


function onConnect() {
    // Once connected, subscribe to our topi
    client.subscribe(mqttDestination);
}

// Click event down
$(".letter").mousedown(function () {
    sendLetterPress($(this), true);
});

// Click event up
$(".letter").mouseup(function () {
    sendLetterPress($(this), false);
});

function sendLetterPress($letter, isOn) {
    // Get led and colour indexes from the data tags
    var led = $letter.attr("data-led");
    var color = $letter.attr("data-color");

    if (!isOn) {
        color = 0; // colour 0 is off
    }

    var message = led + "," + color; // format led,color
    sendMessage(message);
}

function sendMessage(message) {
    message = new Paho.MQTT.Message(message);
    message.destinationName = mqttDestination;
    client.send(message);
}

This simple page was uploaded to Microsoft Azure and hosted using a cloud web app instance.

 

Building the hardware

The LED's were connected to the NodeMCU's 5v pin, ground pin and D1 pin (GPIO5). Check here for pin information.

Next, I used the Arduino IDE to program the NodeMCU with a connection to the MQTT server.

The plugins needed for this project are:

  1. ESP8266wifi
  2. PubSubClient
  3. Adafruit_NeoPixel

The following snippet is the full code used on the NodeMCU. Its a bit rough due to it being a quick hacky project, but it works reliably and shows how the different plugins work together.

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

// Define data pin for LEDs (GPIO) and number of LEDs
#define LIGHTPIN    5
#define NUMPIXELS   50

// Wifi connection details
const char* ssid = "";
const char* password = "";
const char* mqtt_server = "";

// LEDs and colours
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, LIGHTPIN, NEO_GRB + NEO_KHZ800);
int totalColours = 5;
uint32_t colors[] = { pixels.Color(0, 0, 0), pixels.Color(0, 255, 0), pixels.Color(255, 0, 0), pixels.Color(0, 0, 255), pixels.Color(255, 255, 0) };

// States
long lastReconnectAttempt = 0;
long lastMessage = 0;
int currentStandbyStatus = -100;

// Wifi and MQTT clients
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

void setup() {
  pixels.begin(); // Enable LEDs
  pixels.show(); // Turn all LEDs off
  setStandby(3); // Set all LEDs blue
  
  Serial.begin(9600); // Configure Serial port
  setup_wifi();
  lastReconnectAttempt = 0;

  // Configure MQTT
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void setup_wifi() {
  delay(10);
  // Connect to wifi
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

// This function will be called when a MQTT message comes through
void callback(char* topic, byte* payload, unsigned int length) {
  lastMessage = millis();
  setStandby(0);
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");

  // Get the message as a string
  String message = "";
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
    message += (char)payload[i];
  }
  Serial.println();
  Serial.println("message: " + message);

  // Get the message mode
  int msgMode = getValue(message, ',', 0).toInt();

  // If mode is individual LED
  if(msgMode == 0) {
    // Set the led colour
    int led = getValue(message, ',', 1).toInt();
    int color = getValue(message, ',', 2).toInt();
    pixels.setPixelColor(led, colors[color]);
    pixels.show(); 
  } else if(msgMode == 1) {
    // If mode is 1 - flash the lights
    getAttention();
  }

}

// Reconnect to MQTT
boolean reconnect() {
  if (client.connect("arduinoClient")) {
    // Once connected, publish an announcement...
    
    // ... and resubscribe
    client.subscribe("TheTinIoST");
  }
  return client.connected();
}

void loop()
{
  if (!client.connected()) {
    setStandby(1); // Turn on multi-colour lights
    Serial.println("not connected");
    long now = millis();
    // Delay 5 seconds between reconnect attempts
    if (now - lastReconnectAttempt > 5000) {
      lastReconnectAttempt = now;
      // Attempt to reconnect
      if (reconnect()) {
        // Success
        Serial.println("Successfully connected to MQTT");
        setStandby(-1);
        lastReconnectAttempt = 0;
      }
    }
  } else {
    // Client connected
    client.loop();
    long now = millis();
    if(now - lastMessage > 10000 && currentStandbyStatus != -1) {
      setStandby(-1);
    }
  }

}

// Helper to get a value from the MQTT message as we are sending a comma delimated message
String getValue(String data, char separator, int index)
{
  int found = 0;
  int strIndex[] = {0, -1};
  int maxIndex = data.length()-1;

  for(int i=0; i<=maxIndex && found<=index; i++){
    if(data.charAt(i)==separator || i==maxIndex){
        found++;
        strIndex[0] = strIndex[1]+1;
        strIndex[1] = (i == maxIndex) ? i+1 : i;
    }
  }

  return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}

// Sents all LEDs to multi colour or all to a preset colour
void setStandby(int state) {
  currentStandbyStatus = state;
  for(int i=0, j=1; i < NUMPIXELS; i++, j++){
    if(j >= totalColours) {
      j = 1;
    }
    if(state == -1) {
      pixels.setPixelColor(i, colors[j]);
    } 
    else {
      pixels.setPixelColor(i, colors[state]);
    }
  }
  pixels.show();
}

// Flash LEDs
void getAttention() {
  for(int j=1; j < totalColours; j++){
    for(int i=0; i < NUMPIXELS; i++){
      pixels.setPixelColor(i, colors[j]);
    }
    pixels.show();
    delay(200);
  }
  setStandby(0);
}

The Result

Here is a short demo of the wall spelling out the word "Happy"

Comments are closed