Controlling my IoT Christmas Jumper with CheerLights

‘Twas the night before Christmas Jumper Day, when all through the house… not a single festive sweater could be found!

Each year in the Citrix office in Cambridge, UK, we take part in the annual Save the Children Christmas Jumper Day. But at 8pm the evening before, I found myself without a suitable Yuletide sweater to wear, so I decided to make my own. Happily, I had some useful bits and pieces sitting on my workbench, so I set about making myself an IoT-controlled, multi-color LED Christmas jumper. I later decided to connect it to CheerLights. Here’s how it works.

20171217_094449The lights themselves are a string of WS2811 red-green-blue, individually controllable LEDs, meaning each LED can be set to a different color under the control of suitable software. I’m a huge fan of these LEDs; they can be easily connected to Raspberry Pis, Arduinos, and many other devices. They can be chained together to form delightfully elaborate displays with very simple wiring; and can produce some really funky colors and effects. In the past, I’ve used them as Christmas tree lights (including using them to scroll dot-matrix messages on the tree!), for jazzing up PowerPoint presentations, for showing load on a cluster of servers, to illuminate a telephone box panel, and more.

In previous projects, I’ve connected these lights either directly to a Raspberry Pi, or to an Arduino, which itself is connect to a Pi via serial-over-USB. The former method is a little hit-and-miss because the 3.3v output from the Pi isn’t always enough to drive the 5v control input to the LEDs, in this case some additional electronics are needed to make it all work. Annoyingly, the particular LEDs I found to use for the Christmas jumper couldn’t handle the 3.3v signal so, to save time soldering an interfacing circuit, I adopted the Arduino method (most Arduinos drive their outputs at 5v). I recycled an Arduino sketch I created some time ago for a big push button that had a circle of 8 WS281x LEDs within its translucent shell, stripped out all the code for the push button, leaving just the part that could take a command over the serial-over-USB channel to change the LED colors (such as “COLOR #FF0000” to show red) — code here.

20171215_203429When I first created this IoT Christmas jumper I controlled it via an Alexa skill – see my Citrix blog post for more details on this. However I later became aware of CheerLights – a project that allows lights across the world to be synchronized to one color and be controlled by anyone via Twitter. My jumper seemed like a great fit for this so I set about modifying the code to work with it. I created a basic node.js program (code here) to run on a Raspberry Pi Zero W that polls the CheerLights API, from which it receives color commands which it then sends to the Arduino via serial-over-USB. It adjusts the color value to reduce the brightness of the LEDs and extend the battery life. I added a call to this script from /etc/rc.local to have it run on boot.

The final step (for phase 1 – there’s more!) was to attach the LEDs and Arduino to a suitable jumper, put it on, connect the Pi to a USB power pack, and secure the whole thing in my pockets, under my belt, and so on. Now my Christmas jumper will change color at the same time as many other lights across the world, all controllable by anyone who wants to.

IMG_8673f

As a bonus I modified the Alexa skill I was using for the original version of the hack to have it send a #CheerLights tweet in response to an Alexa command. This was done by creating an Azure Logic App to send the tweet and calling that from the Azure Function that I am using as the Alexa skill handler.

 

Advertisements

A universal IR remote control using MQTT

In some previous hacking I created an add-on for the Kodi media player which allowed me to control Kodi and the TV it is connected to (by using an IR blaster) using messages published through my home MQTT broker. The original purpose for this hack was to enable voice control via an Amazon Echo Smart Home Skill.

alloffI’ve since added another use-case where a single push-button connected to a Raspberry Pi Zero W publishes a single MQTT message which my rules engine picks up and then publishes a number of messages to act as an “all off” button: it sends “off” messages to all lights in the room (these are a mixture of LightwaveRF and Philips Hue – both interfaced via services that subscribe to the local MQTT broker); a “pause” message to Kodi; and a TV “off” message to the TV.

However, despite having this capability I still use separate traditional IR remote controls for the TV and Kodi, and a 433MHz control for the LightwaveRF lights. It seemed like a good idea to take advantage of the MQTT control to reduce the need for so many remote controls so I set about turning the Hama remote control I use for Kodi into a universal control for all the devices.

The strategy I used was to have the Hama remote control publish MQTT messages and add some rules to the broker’s rules engine to map these to the required MQTT messages(s) to control Kodi, the TV, or the lights. I chose to connect the Hama USB IR receiver to a new Raspberry Pi Zero W – I could have left this connected to the Pi 3 running Kodi and created a new add-on to talk to it but I have future plans that call for another Pi in this location and this seemed like it would be easier… – and set about building a small service to run on the Pi to relay received IR commands to the MQTT broker.

iroverview
Overview of the overall architecture

After a few false starts with LIRC I settled on consuming events from the remote control via the Linux event subsystem (same as handles keyboard and mouse). There are some node.js libraries to enable this but I found a much more complete library available for Python which, critically, implements the “grab” functionality to prevent “keystrokes” from the IR control also going to the Pi’s login console.

I’ve already implemented a few Python MQTT clients using the Paho library (including the Kodi add-on itself) so I recycled existing code and simply added an input listener to attach to the two event devices associated with the IR control (hard-coded for now) and, after a little processing of the event, publish an MQTT message for each button press. The Hama remote acts like a keyboard and some of the buttons include key modifiers: this means that a single button push could involve up to 6 events: e.g. key-down for left-shift, key-down for left-ctrl, key-down for ‘T’, followed by three key-up events in the reverse order. My code maintains a simple cache of the current state of the modifier keys so that when I get a key-down event for a primary key (e.g. ‘T’ in the above example) I can publish a MQTT message including the key and its active modifiers.

 for event in self.device.read_loop():
     if event.type == evdev.ecodes.EV_KEY:
         k = evdev.categorize(event)
         set_modifier(k.keycode, k.keystate)
         if not is_modifier(k.keycode) and not is_ignore(k.keycode):
             if k.keystate == 1:
                 msg = k.keycode + get_modifiers()
                 self.mqttclient.publish(self.topic, msg)

(The full code for the service can be found here.)

This results in MQTT messages of the form

IR/room2av KEY_VOLUMEUP
IR/room2av KEY_VOLUMEDOWN
IR/room2av KEY_LEFT
IR/room2av KEY_RIGHT
IR/room2av KEY_DOWN
IR/room2av KEY_UP
IR/room2av KEY_PAGEUP
IR/room2av KEY_PAGEDOWN
IR/room2av KEY_T_KEY_LEFTCTRL_KEY_LEFTSHIFT

The next step was to add a rule to the rules engine to handle these. The rules engine is a simple MQTT client that runs on the same Raspberry Pi as the MQTT broker; it listens to topics of interest and based on incoming messages and any relevant state (stored in Redis) publishes message(s) and updates state. In this case there is no state to worry about, it is simply a case of mapping incoming “IR/*” messages to outbound messages.

A (partial) example is:

function handle(topic, message, resources) {
  switch (topic) {
  case "IR/room2av":
    switch (message.toString()) {
    case "KEY_UP":
      resources.mqtt.publish("KODI/room2/KODI", "Action(Up)");
      break;
    case "KEY_DOWN":
      resources.mqtt.publish("KODI/room2/KODI", "Action(Down)");
      break;
    case "KEY_PAGEUP":
      resources.mqtt.publish("Light/room2/Lamp", "on");
      resources.mqtt.publish("Light/room2Ceiling", "on");
      break;
    case "KEY_PAGEDOWN":
      resources.mqtt.publish("Light/room2/Lamp", "off");
      resources.mqtt.publish("Light/room2Ceiling", "off");
      break;
    case "KEY_VOLUMEUP":
      resources.mqtt.publish("KODI/room2/TV", "VOL_p");
      break;
    case "KEY_VOLUMEDOWN":
      resources.mqtt.publish("KODI/room2/TV", "VOL+m");
      break;
...

Here we can see how buttons pushes from this one IR remote are routed to multiple devices:

  • the “up” and “down” navigation buttons result in messages being sent to Kodi (the message content is simply passed to Kodi as a “builtin” command via the xbmc.executebuiltin(…) API available to add-ons);
  • the “+” and “-” channel buttons (which map to PAGEUP and PAGEDOWN keycodes) have been abused to turn the lights on and off – note the two separate messages being sent, these actually end up going to LightwaveRF and Philips Hue devices respectively; and
  • the “+” and “-” volume buttons send IR commands to the TV (this happens to be via the Kodi add-on but is distinct from the Kodi control) – the “VOL_p” and “VOL+m” being the names of the IR codes in the TV’s LIRC config file.

A major gotcha here is that when controlling a device such as the TV with an IR blaster, there will be an overlap between the blast of IR from the Hama device and from the IR blaster connected to the Kodi Pi, and the TV will find it difficult to isolate the IR intended for it. To avoid this I’ve had to put tape over the TV’s IR receiver and IR blaster which is glued to it such that IR from the Hama control can’t get through.

The end result is that I can now use a single IR remote control to navigate and control Kodi, turn the TV on and off and adjust its volume, and control the lights in the room. Because everything is MQTT under the hood, and I’ve got plumbing to route messages pretty much anywhere I want, there is no reason why that IR remote control can’t do other things too. For example it could turn off all the lights in the entire house, or turn off a TV in another room (e.g. if I’ve forgotten to do so when I left that room), or even to cause an action externally via my Azure IoT gateway (more on this in a future blog post). And because the rules engine can use state and other inputs to decide what to do, the action of the IR remote control could even be “contextual”, doing different things depending on circumstances.

 

An IoT-connected PowerPoint multi-device show!

Today I was honoured to have the opportunity to discuss the challenges and opportunities IoT brings to the world of technical documentation in a keynote for the inaugural Cambridge meet-up of the Write The Docs community.

One of the topics I covered was how IoT enables human-computer interaction across a much broader range of devices than the traditional screen, keyboard and pointing device – the 4th generation user interface as Steve Wilson describes it – leading to richer, more natural, and more immersive experiences for users of applications (in the broadest sense).

To help illustrate this in the context of a presentation I decided to extend the slideshow beyond the projector and slide clicker and include lights and buttons to create a more entertaining experience. For example I talked about some of the opportunities devices such as Amazon Echo could create. As I reached the relevant slide a set of LEDs came on to illuminate an actual Echo device on a stand on the stage. I also had a large push-button illuminated with controllable multi-coloured LEDs; this became a software-defined push-button which did different things during the course of the presentation including advancing slides, turning off the LEDs, and so on. The LED colour and action assigned to the button were controlled based on the slide being projected.

As usual I used Octoblu as the IoT automation platform. A key input to the flow was a trigger for each time the PowerPoint presentation advanced a slide. To do this I created a PowerPoint macro named “OnSlideShowPageChange” (called in the obvious manner by the application) which sent a HTTP POST to an Octoblu trigger with the current slide number and the total number of slides from the presentation.

Sub OnSlideShowPageChange(ByVal SSW As SlideShowWindow)
    Set objHTTP = CreateObject("MSXML2.ServerXMLHTTP")
    URL = "https://triggers.octoblu.com/v2/flows/<UUID>/triggers/<UUID>"
    objHTTP.Open "POST", URL, False
    objHTTP.setRequestHeader "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"
    objHTTP.setRequestHeader "Content-Type", "application/json"
    objHTTP.send "{""currentSlide"":" & SSW.View.CurrentShowPosition & ",""numSlides"":" & SSW.Presentation.Slides.Count & "}"
End Sub
To drive PowerPoint I created a new Octoblu connector that runs on my laptop that can remote control a PowerPoint slideshow. This is a very simple connector that really just adds an Octoblu shim on top of the excellent slideshow NPM library. The connector can do a few things but the most useful is the “GotoSlide” endpoint which can take a slide number, or one of “next”, “prev”, “first” or “last”. A simple example of using it in a flow is:
pptnode
(BTW if you want to use my connectors see https://github.com/jamesbulpin/my-meshblu-connectors for how to add my custom connector repository to your own Octoblu account. All the custom connectors in this blog post can be found in that repo.)
The flow looks rather more complex that it really is:
pptflowannotated
There are essentially five main parts to the flow:
  1. The trigger from the PowerPoint macro (telling us which slide is being shown) setting the state of a string of WS2811 LEDs (using my WS2811 Raspberry Pi connector which is also in the repo mentioned above) based on which slide is being shown. These LEDs illuminate the Echo and a few other props. The JSON templates contain configuration to tell the WS2811 connector which LEDs to turn on to illuminate the relevant prop. When the final slide is shown the entire string is put into a moving pattern (just for fun 🙂 ).
  2. The slide number is also used to show a slide progress bar on a second string of WS2811 LEDs. The connector takes the current slide number and the total number of slides. With the LEDs in the string mapping to slides in the presentation in a one-to-one manner, it turns LEDS green for slides already shown, red for those not yet shown, and yellow for the currently showing slide.
  3. The slide number is used to define the colour of the illuminated push-button (or to turn it off entirely) and to send it a string which the button will return (see below) if pushed – this makes the function of the button dynamically configurable depending on which slide is being shown.
  4. If the push-button is pushed it sends the string configured in #3 above. This part of the flow dispatches messages depending on that string. If it’s “ON” all the LEDs on both strings are put into a moving pattern and “OFF” turns all LEDs off. Otherwise it’s interpreted as a slideshow command (e.g. “next”) and routed to the PowerPoint connector described earlier.
  5. An additional effect of the button push in the “ON” and “OFF” cases is setting the button to do the opposite, i.e. if the push was “ON” then the button is reconfigured for “OFF”.

An aside on the WS2811 connector: This works by specifying a mode (via a parameter on the message) and, depending on the mode, some additional parameters such as colour or slide number. Currently the available modes are:

  • “off” – what you’d expect
  • “solid” (or “color”) – display a solid colour on all LEDs – requires the “color” parameter (can be a word such as “red” or hash hex value such as “#00553e”)
  • “slide” – the slide progress bar described above – requires “slide” and “slidemax” parameters
  • “colorwheel” – a moving and colour-changing dynamic pattern
  • “twinkle” – colorwheel modulated with a random twinkling (each LED turning on and off randomly)
  • “percent” – show a percentage bargraph (“VU meter” style) on the LEDs – requires the “percent” parameter, turns on the first percent% of the LEDs
  • “direct” – takes a JSON object containing a description of which LEDs should be which colour: pass the object (e.g. using the JSON Template node and passing {{msg}} in the parameter) containing a key named “groups” which is a list of objects with “color” (string color name/hascode) and “leds” (a list of integer LED index with the first LED on the wire being zero) parameters.

e.g.:

{
  "groups":[
    {
      "color":"blue",
      "leds":[1,3,5,9]
    },
    {
      "color":"red",
      "leds":[0,10,132]
    }
  ]
}

(This WS2811 connector, developed in collaboration with John Moody, is a better version of the one I described in a previous post – see that post for info on the wiring.)

Now of course there are ways to do similar things to all of this without IoT but like many things with IoT it’s the removal of barriers of cost, accessibility and vendor compatibility that make an IoT approach interesting.

If you’re at Citrix Synergy 2017 in Orlando later this month be sure to join me and a growing list of IoT and automation experts for SYN401 to see what else is possible with the Octoblu platform. I can guarantee that the PowerPoint (there won’t be much – it’s a far more interactive session than that!) will be even more IoT than in today’s event!

Displaying messages on my Christmas tree lights

IoT and Christmas go well together – all those festive lights and sounds are just asking to be connected up to the Internet and automated! My own Christmas tree is of course no exception and, as I wrote about over on the Citrix blogs, now sports a string of individually-addressable LEDs controlled by a Raspberry Pi which is in turn managed by Octoblu.

But I decided to go a step further than simply displaying some fun flashing patterns and colours on the tree: seeing as every LED can be individually managed, if I could determine the exact position on the tree of each LED I could come up with geometric patterns or even use it as a sort of dot matrix display to show messages.

iotreecalibratewebcamMy solution was to connect a webcam to the Raspberry Pi and write a script that turned each LED on one by one, captured an image from the webcam, and found the coordinates of the bright spot on the image. After the script ran through all 100 LEDs it then normalised the coordinates to a 100 x 100 grid and output a list of coordinates that I could then use in my LED controller program. The code is a bit gross, being a mash-up of various bits of other scripts quickly bolted together, including a web server that I used to view the image while positioning the camera.

iotreecalibrateTo visually check the output I wrote a quick bit of HTML/JavaScript that used the pre-normalised coordinates to overlay blobs on top of a captured image from the webcam (with all LEDs lit) – I could then see if the blobs lined up with the LEDs. As you can see in the image here there is at least one misalignment caused by the reflection of the light on a nearby white surface.

So armed with a set of coordinates for the LEDs I then extended my existing WS2811 LED control program (see my post on the Citrix blog for more on this, and on the hardware setup) to use coordinates, instead of just LED sequence number, for its patterns.

Firstly I created a simple set of test patterns that moved a vertical line horizontally across the tree, a horizontal line vertically and then the two different diagonals. I also updated the VU meter effect to use Y coordinates instead of LED sequence number.

However the most fun piece was rendering text on the tree. To do this I found a suitable encoding of a dot matrix font online (there are loads out there) and morphed it into a suitable form to include in Node.js. I then wrote a very simple rendering function that runs over a string of text one character at a time, using the font configuration to determine which pixels in each column of the 5×7 matrix should be illuminated. The end result was an array with an entry per column (for the entire message) with the entry being an encoding of which row LEDs should be illuminated. I also created a similar array to record which colour to use for each column – in this case populated to cycle through red, green and blue on a character-by-character basis (this helps the eye to see the characters better).

var dmmessage = " XMAS";

var dmarray;
var dmcolor;
var colorcycle = 255;
function renderMessage() {
  dmarray = new Uint32Array(dmmessage.length * 7 + 5);
  dmcolor = new Uint32Array(dmmessage.length * 7 + 5);
  for (var i = 0; i < dmmessage.length; i++) {
    var fonttablebase = (dmmessage.charCodeAt(i) - 0x20) * 5;
    for (var j = 0; j < 5; j++) {
      dmarray[i*7+j] = font5x7[fonttablebase + j];
      dmcolor[i*7+j] = colorcycle;
    }
    if (colorcycle == 0xff0000) {
      colorcycle = 255;
    } else {
      colorcycle = colorcycle << 8;
    }
  }
}

In the main part of the WS2811 driver program, where a loop iterates over the 100 LEDs choosing which colour to display for each, the code uses the incrementing offset variable (increments once per 100-LED update cycle, every 80ms) to index into the column array, offset by the X coordinate of the LED. A quantised Y coordinate of the LED is used to look up the row pixel on/off data from the array entry – the quantisation effectively creating 7 rows across the surface of the tree.

function dotmatrix_y(y) {
  if (y < 25.0) return 8;
  if (y < 40.0) return 0;
  if (y < 50.0) return 1;
  if (y < 60.0) return 2;
  if (y < 70.0) return 3;
  if (y < 80.0) return 4;
  if (y < 90.0) return 5;
  return 6;
}

function dotmatrix(pos) {
  var x = xy.xy[pos][0];
  var ydot = dotmatrix_y(xy.xy[pos][1]);

  var idx = Math.floor(((offset + x)/10)%dmarray.length);
  var column = dmarray[idx];

  if ((1<<ydot)&column) {
    return dmcolor[idx];
  }
  return 0;
}

I added a new parameter to the Octoblu message to allow the display string to be set. I updated the Octoblu flow and Alexa skill to allow a command such as “Alexa, tell the Christmas tree to say Hello”.

Alexa intent schema (new part in bold, full code here):

{
  "intents": [
    {
      "intent": "ModeIntent",
      "slots": [
        {
          "name": "mode",
          "type": "LIST_OF_MODES"
        }
      ]
    },
    {
      "intent": "ColorIntent",
      "slots": [
        {
          "name": "color",
          "type": "LIST_OF_COLORS"
        }
      ]
    },
    {
      "intent": "TextIntent",
      "slots": [
        {
          "name": "text",
          "type": "LIST_OF_WORDS"
        }
      ]
    },
    {
      "intent": "AskOctobluIntent"
    },
    {
      "intent": "SorryIntent"
    }
  ]
}

AWS Lambda intent handler (new part in bold, full code here):

def on_intent(intent_request, session):
    """ Called when the user specifies an intent for this skill """

    print("on_intent requestId=" + intent_request['requestId'] +
          ", sessionId=" + session['sessionId'])

    intent = intent_request['intent']
    intent_name = intent_request['intent']['name']
    
    mode = None
    color = None
    text = None

    session_attributes = {}
    speech_output = None

    # Dispatch to your skill's intent handlers
    if intent_name == "ModeIntent":
        if ('mode' in intent['slots']) and ('value' in intent['slots']['mode']):
            mode = intent['slots']['mode']['value']
    elif intent_name == "ColorIntent":
        if ('color' in intent['slots']) and ('value' in intent['slots']['color']):
            color = intent['slots']['color']['value']
            mode = "solid"
    elif intent_name == "TextIntent":
        if ('text' in intent['slots']) and ('value' in intent['slots']['text']):
            text = intent['slots']['text']['value']
            mode = "dotmatrix"
    elif intent_name == "AskOctobluIntent":
        mode = "octoblu"
    elif intent_name == "SorryIntent":
        speech_output = "I'm sorry James, I can't do that"
    else:
        raise ValueError("Invalid intent")

    obmsg = {"debug":intent}
    if mode:
        obmsg["mode"] = mode
    if color:
        obmsg["color"] = color
    if text:
        obmsg["text"] = text
    url = octoblu_trigger
    data = urllib.urlencode(obmsg)
    req = urllib2.Request(url, data)
    response = urllib2.urlopen(req)
    the_page = response.read()
    
    if not speech_output:
        speech_output = "OK"
    return build_response(session_attributes, build_speechlet_response(speech_output))

WS2811 Octoblu message handler (new part in bold):

conn.on('ready', function(rdata){
  console.log('UUID AUTHENTICATED!');
  console.log(rdata);

  clearTimeout(connectionTimer);
  connectionTimer = undefined;

  conn.update({
    "uuid": meshbluJSON.uuid,
    "token": meshbluJSON.token,
    "messageSchema": MESSAGE_SCHEMA,
  });

  conn.on('message', function(data){
    console.log('Octoblu message received');
    console.log(data);
    mode = data.mode;
    color = ("color" in data)?data.color:null;
    if (("text" in data) && data.text) {
      dmmessage = " " + data.text.toUpperCase();
      renderMessage();
      offset = 0;
    }
    handle_message(mode, color)
  });

});

Octoblu connects together the HTTPS POST request from the Alexa skill handler with the WS2811 driver program.

iotreeoctoblu

And there we have it – Alexa-controlled scrolling message display on a Christmas tree thanks to Raspberry Pi and Octoblu!

 

Hybrid local/cloud style home automation

When I first started automating items in my home I used a local (“on-prem” if you like) point solution for security lighting. This had a connection to an internet service (Google Calendar) but only in a “pull” manner, and only for that specific use-case. As time went on I wanted to enable a broader set of use-cases, some of which required other internet-connected services in both pull and push directions. I experimented with using Octoblu, with the local LightwaveRF controller being connected to it – all the automation therefore was being performed in the cloud. As I looked at adding additional devices (PIR detectors, reed switches, IR blasters, and so on) I encountered a number of failures and delays caused by this reliance on my domestic internet connection and the (at the time) patchy reliability of Octoblu (it’s far more solid now).

My solution was to use a “hybrid” model with a local message broker and set of services running inside the home, and a connection to Octoblu for cases when external services were involved. Overall this means that truly local operations, such as PIRs turning on lights depending on the position of various reed switches, can be kept within my home network with no reliance on my internet connection or any external service. In cases where external stimuli are used (e.g. for connecting Amazon Echo’s smart home stuff) or external actions are required (e.g. sending a text message using Twilio) the bridge between the two worlds allows a message to be forwarded in either direction.

The on-prem piece

homeautopiThe local system is based on a MQTT broker running on a Raspberry Pi. This provides a publish-subscribe messaging system that various services connect to. The broker and many of the services run on the same Raspberry Pi however the broker also listens on a TCP port to allow other Raspberry Pis (and potentially other devices) in the home to join the party. I also have a Redis key-value-pair (KVP) store which is used to store state such as presence information or whether it’s day or night.

As an illustration of how the system is used: the LightwaveRF radio-controlled lighting system is connected via a small node.js service that subscribes to lighting messages (e.g. “Light/Kitchen/Main=on“) and sends the appropriate command to the local LightwaveRF hub which then emits the 433MHz radio command. When I later added a Philips Hue system alongside the LightwaveRF one I created a similar node.js service that also subscribes to lighting messages and makes calls to the Hue hub’s API as necessary. In both cases the services ignore messages about lights they don’t control. This all means other services, such as the PIR detector, or the rules engine, can simply publish a lighting control message without worrying about whether it’s a LightwaveRF, Hue, or (perhaps in the future) other system managed locally or via a cloud service.

Other services that publish and/or subscribe to MQTT messages include:

  • A Bluetooth Low Energy (BLE) advertisement monitor – this is used to determine the presence of absence of various things
  • A Google calendar interface that can update local KVP state, or generate messages, based on scheduled events
  • A GPIO service that enables things like PIRs and push buttons to be attached to Raspberry Pis
  • A timer service that other services and rules can use to provide timeouts, e.g. to turn off PIR-controlled lights after a set delay
  • Plug-ins for the Kodi media player to control Kodi itself as well as IR emitters for nearby TVs and other devices (stay tuned for a blog post on this)

homeautoruleA core part of the whole system is the rules engine. This is a service that subscribes to MQTT messages, implements rules, and publishes MQTT messages as necessary. It doesn’t directly interface to any device. An example of a rule is managing lighting for the basement stairs: inputs to the rule are two PIRs (via the GPIO service), the value of the day/night KVP, and the timeout message from a named timer (from the timer service). If a PIR triggers and it is night (from the KVP) then a lighting message (“Light/Stairs/Basement=on“) is published; when the PIR stops detecting, a timer control message is published (“Timer/basement_stairs_lighting/reset=<time in 60s>“) to start the timer; receipt of the timer’s timeout message causes the rule to emit an “off” lighting message.

The Octoblu bridge

homeautodiagram

So, what if I want to control stuff in the home from somewhere else? Or if I need to send a message to a cloud-connected device or service? That’s where the Octoblu connector comes in. I built a basic connector that runs as a node.js service on the main MQTT Raspberry Pi. This subscribes to a subset of MQTT topics and has the ability to publish arbitrary messages into the on-prem system. It also acts as an Octoblu connector, maintaining a bidirectional connection into Octoblu. This allows my entire home automation world to appear as a thing in Octoblu so I can send messages to it, which leads to MQTT published messages, and receive messages from it, which came from MQTT subscribed messages.

As an example, I can remote-control a light using a button on a web page by connecting an Octoblu “trigger” (a thing that can respond to a HTTPS POST and then emit a message into the Octoblu platform) to the home automation thing, and ensure the message payload includes the right MQTT topic (e.g. “Light/Kitchen/Main“) and message (e.g. “on“). The connector routes this message into my home as a published MQTT message that the LightwaveRF or Hue service will act on. This is the core mechanism I used when connecting my home lighting up to Amazon Echo using an Alexa smart home skill adapter.

In the other direction an on-prem service, such as the rules engine, can send a message to Octoblu just by publishing to a suitable topic (currently this means it has a “Octoblu/” prefix) via the local MQTT broker. An example of this is a security feature that sends me a text message if a particular combination of stimuli are seen. The rules engine publishes a message “Octoblu/alert/<detail>=<status>“, the Octoblu connector receives this via its MQTT subscription and sends it into Octoblu. The Octoblu flow then decides how to process this message which may end up with a call to the Twilio thing to send a text message.

Other examples of using the Octoblu connection include:

  • Sending events for sunrise and sunset, based on pulling data from a weather API (saves having to drill walls to install a light sensor)
  • Routing messages from Alexa skills handlers (these run in the cloud as AWS Lambda functions so I need a way to route the message back across my firewall)

In closing

I’ve ended up with a hybrid-cloud style of IoT management for my home automation, not by design, but by evolution. I find that being able to combine local automation – with its low latency and lack of reliance on external connectivity and services, with a powerful cloud-based automation platform able to send and receive from a variety of cloud/internet services, is a best-of-both-worlds solution.

homeautooctoblu

Hacking Big Mouth Billy Bass – part 2/3

A couple of weeks ago there was a lot of interest on social media in a video showing a Big Mouth Billy Bass animatronic novelty device seemingly take on the role of Amazon Echo, including animating its mouth and body in sync with the speech. With my recent exploits in connecting strange crap to Octoblu I decided to have a go at automating and Internet-connecting Billy Bass.

In this three-part blog series I’ll cover:

  1. Reverse-engineering Billy Bass and automating its movements based on audio input
  2. (This part) – connecting Billy Bass to the Octoblu IoT automation platform and synthesising speech for it
  3. Controlling Billy Bass with Amazon Echo and Slack

Recap and introduction

In the first part of this blog series I described how I added an Arduino to Big Mouth Billy Bass to enable it to be controlled by commands over USB-serial as well as automatically moving its mouth in sync with audio input. In this instalment I’ll describe how I connected this to the Internet using the Octoblu IoT automation platform.

The Octoblu connector

“Things” can be connected to Octoblu via “connectors”. Connectors run somewhere that they can talk to the “thing” as well as talk to Meshblu, Octoblu’s messaging platform. In this case my connector runs on a Raspberry Pi. The overall operation of the connector is that it listens for incoming Meshblu messages which contain a base64 encoded mp3 file. Each received message is added to a local FIFO queue (to avoid trying to play multiple messages simultaneously). When a message is played from the queue the connector first sends a USB-serial message to the Billy Bass Arduino to tell it to move the fish’s head. It then creates a temporary mp3 file and base64-decodes the mp3 content from the message into it. Next the connector spawns an external mp3 player program on the Pi which plays the mp3 file and on completion the callback in the connector gives the fish’s tail a quick flip before returning all body parts to their at-rest position.

I created the connector using a simple node.js program based on the v1 (now deprecated) Meshblu NPM library (“npm install meshblue@1.34.1”). This is very much prototype code with hard-coded items and without much error handling. To use the meshblu library I needed to create a “generic device” from Octoblu’s “All Things” list: having clicked on “GENERIC DEVICE” in the “OTHER” section, I clicked the “CONNECT GENERIC DEVICE” button; leaving the selection as “Register a new thing”, entering a name for the device (“My Billy Bass” here) and leaving the “type” field empty; I clicked “CONNECT GENERIC DEVICE” again; and found the new “My Billy Bass” device in the list of my things. I clicked on the device to bring up its configuration page. From here I copied the UUID, and clicked “GENERATE TOKEN” to generate and show an authentication token – I copied this also. I then added both the UUID and token to a file named meshblu.json (see the comment in the code for an example) alongside the connector.

connectorsetup

As well as meshblu, I also needed to npm-install child_process, fs, serialport and tempfile. Having connected the Arduino to the Pi and ensured its serial interface was available as /dev/ttyACM0, and having connected the fish’s audio input (this goes to the amplifier and to the Arduino’s analogue input) to the Pi’s headphone socket, I ran “node piaudio.js”. This confirmed I was connected to Meshblu but this won’t do anything useful without an Octoblu flow.

piaudiojs

The Octoblu flow

So now I’ve got a fish that can talk and move its mouth, head and tail by playing an mp3 file sent to it via Meshblu. To make this do something worthwhile meant building an Octoblu flow to create a suitable mp3 file containing synthesised speech. To do this I used the text-to-speech web service Voice RSS. This service provides an easy-to-use API service to turn text into speech in a variety of formats, languages and accents. It has a free tier that allows for up to 350 requests per day. I had looked at doing text-to-speech locally on the Pi using a tool such as Festival, however I found the Voice RSS output to be much higher quality, and using a web service allows for more future flexibility than having everything locked down on the Pi.

The flow begins by generating the text that Voice RSS will later convert to speech. In this flow I have two ways to do this: firstly, for testing, I am hard-coding two test phrases using “compose” tools (these are just assigning the text strings to a key named “text” that will later be extracted in the HTTP API call to Voice RSS). The second mechanism is to receive the text via a HTTPS POST call into an Octoblu trigger tool (it’s named “Call from Alexa skill” in this flow – you’ll see this in use in the video at the end of this post as well as in the third part of this series). This then gets put into the same “text” key as above.

billbassflow

The Voice RSS API is very easy to drive from Octoblu. I simply used a HTTP GET tool configured with the various API parameters, including my voicerss.org API key. The text to convert is taken from the “text” field of the incoming message using Octoblu’s “moustache” syntax (“{{msg.text}}”).

voicerss

collatemp3The output from the HTTP GET is base64 encoded mp3 which is carried in the message as the only payload. The “compose” tool that follows the HTTP GET simply puts this payload into a key named “mp3” to match what the connector is expecting (the reason for doing this is to allow extension with other data/metadata, such as customised motor commands, in the future). This is then sent to the “My Billy Bass” device (the one I configured earlier) with the “Use Incoming Message” option enabled (this causes the message we just created with the mp3 data to be sent directly to the connector).

Summary

Having connected my Arduino-connected Big Mouth Billy Bass to a Raspberry Pi and to Octoblu I now have a HTTPS POST webhook I can call with some text that will be sent to Voice RSS to be synthesised into speech. This will then be sent to Billy Bass via my Meshblu connector and the fish will speak and move its mouth, head and tail to sync with the speech.

In the third and final part of this blog series I’ll use these mechanisms to connect Billy Bass to Amazon Echo (as in the video below) and Slack.

Code

 

 

 

Connecting the BS button to Octoblu

This didn’t start out as a sweary project. The original idea was to connect a big red button to Octoblu to allow it to start an online meeting, replicating what Chris Matthieu did with a BT.TN in this video. I thought it would be fun to get an actual Staples Easy Button and wire it up via a Raspberry Pi or similar. However, I’ve not been able to find one of these big red buttons available in the UK so I hunted around for alternatives. I came across a range of similar devices on Amazon with various words printed on the button and a set of recorded sounds appropriate to the printed word that would be played on a push of the button. I decided on this particular version, which has some, er, colourful things to say.

My original plan was to discard all the electronics except for the push button but on hearing the amusing, if slightly rude, things the device had to say I thought it would be a shame to strip it of its ability to answer back. So instead my unidirectional button->Octoblu plan became a bidirectional one with the ability to send a message to Octoblu when the button is pressed and to have Octoblu be able to send a message to the button to cause it to utter one of its delightful musings.

img_9031First step was to take a look inside to see how it worked and how much space I had to play with. I was pleasantly surprised at how easy it was to disassemble the device in a non-destructive manner. Removing three screws allowed me to easily separate the main body from the upper casing and push button. Care was necessary because the two halves are joined by two wires leading to the LED within the button – these broke from the PCB several times during the project.

The inspection gave me some key information to plan the build:

  1. The device runs on 3V from two batteries in a compartment in the base.
  2. There is quite a bit of empty space in the unit.
  3. The switch is part of the PCB with a clear trace from it to the main integrated circuit, ideally placed to be cut and soldered to.

img_9047u421Next I decided on an interfacing mechanism. I wanted to make the unit USB-connected to give me the flexibility of using it either with a Raspberry Pi or with a Windows NUC in the office. This meant using an Arduino or similar device with a USB interface. An Arduino Micro would have been a good choice but I don’t have any in stock right now and this was a “right now” project. Instead I dug out an old USBmicro.com U421 device – this is a simple 16 pin USB-connected GPIO device that is sufficiently small to fit within the battery compartment of the button, having removed the battery contacts and the central dividing wall.

Next job was powering the device so it can play its sounds and light up its LED. The unit normally works off two AAA batteries, i.e. 3V. The U421 is a 5V device. It’s probably the case that the button’s electronics would be fine with 5V but I didn’t want to blow it up and spoil my evening IoT playtime so I put three diodes in series to drop sufficient voltage to be safe (yes, I know this isn’t exactly elegant, but it works).

img_9043For the input side of things, the physical button push leading to a message to Octoblu, I simply cut the trace on the PCB between the push switch contacts and the device’s main IC and scratched off a patch of PCB laquer to be able to solder a wire directly to the PCB trace and hence connect to the switch contact (the other end of which was ground). This then connects to a GPIO pin on the U421 using a pull-up.

The output function, simulating a key press to trigger the device to play a sound, was done by connecting a GPIO pin configured as an output to the other side of the severed PCB trace connected to the device’s IC. I put a 4k7 resistor between the two chips to deal with voltage mismatches.

img_9065While coding up the software to talk to the U421 (more of which later) I realised that the dumb nature of this unit was forcing me to use a painfully tight polling loop to avoid missing button pushes. Later U421 revisions have a latching feature to help with this problem but this particular device can only be polled. I worked around this in hardware by inserting a one-shot timer (using a standard 555 monostable circuit with a 22k resistor and 47uF capacitor giving about a second of pulse) between the push button and the U421 to extend the time the the effect of the button push could be detected by software and therefore allowing a less aggressive polling frequency. The 555 circuit was assembed in free-space (gross, I know) and tucked into a gap beneath the circuit board inside the button device.

The software to drive this is currently in very raw prototype form. I’m currently using node.js with the old v1.x meshblu library to talk to Octoblu and the very comprehensive “usb” NPM package to interface with the U421 over USB. On the button side the script runs a 100ms polling loop to read the relevant GPIO pin. On a state change corresponding to a push it sends a message to Octoblu. On the sound/LED activation side it listens for messages from Octoblu and on receipt, toggles the output GPIO pin down and up to simulate a button push.

bsflowIn Octoblu I created a new generic device and copied its UUID and token to the local meshblu.json config file alongside my node.js program. To test both the send and receive function I created a flow that on receipt of a message (i.e. someone has pushed the button) sends it to a delay node that then forwards it back to the generic device 3 seconds later leading to a message back to the node.js program which triggers the button device to play a sounds and flash its LED. And it works!

img_9068