Tag Archives: MQTT

Fist pass TRÅDFRI MQTT Bridge

I’ve been working on integrating the new IKEA TRÅDFRI Lights into my Home Automation system. I’d really like a native NodeJS system so I can plug it directly into Node-RED, but I’ve not found a working CoAP over DTLS setup just yet.

So in the mean time I’ve been working on a very basic MQTT to CoAP client bridge in Java using the Eclipse Californium library.

It still needs some work, but here is the first pass:

package uk.me.hardill.coap2mqtt;

import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.logging.Level;

import org.eclipse.californium.core.CaliforniumLogger;
import org.eclipse.californium.core.CoapClient;
import org.eclipse.californium.core.CoapHandler;
import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.core.Utils;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.ScandiumLogger;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.pskstore.StaticPskStore;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.json.JSONArray;
import org.json.JSONObject;

/**
 * @author hardillb
 *
 */
public class Main {
  
  static {
    CaliforniumLogger.disableLogging();
    ScandiumLogger.disable();
//    ScandiumLogger.initialize();
//    ScandiumLogger.setLevel(Level.FINE);
  }
  
  private DTLSConnector dtlsConnector;
  private MqttClient mqttClient;
  private CoapEndpoint endPoint;
  
  private String ip;
  
  private HashMap<String, Integer> name2id = new HashMap<>();
  
  Main(String psk, String ip) {
    this.ip = ip;
    DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(new InetSocketAddress(0));
    builder.setPskStore(new StaticPskStore("", psk.getBytes()));
    dtlsConnector = new DTLSConnector(builder.build());
    endPoint = new CoapEndpoint(dtlsConnector, NetworkConfig.getStandard());
    
    MemoryPersistence persistence = new MemoryPersistence();
    try {
      mqttClient = new MqttClient("tcp://localhost", MqttClient.generateClientId(), persistence);
      mqttClient.connect();
      mqttClient.setCallback(new MqttCallback() {
        
        @Override
        public void messageArrived(String topic, MqttMessage message) throws Exception {
          // TODO Auto-generated method stub
          System.out.println(topic + " " + message.toString());
          String parts[] = topic.split("/");
          int id = name2id.get(parts[1]);
          System.out.println(id);
          String command = parts[3];
          System.out.println(command);
          try{
            JSONObject json = new JSONObject("{\"9001\":\"Living Room Light\",\"9020\":1491515804,\"9002\":1491158817,\"9003\":65537,\"9054\":0,\"5750\":2,\"3\":{\"0\":\"IKEA of Sweden\",\"1\":\"TRADFRI bulb E27 opal 1000lm\",\"2\":\"\",\"3\":\"1.1.1.0-5.7.2.0\",\"6\":1},\"9019\":1,\"3311\":[{\"5850\":1,\"5851\":10,\"9003\":0}]}");
            JSONObject settings = json.getJSONArray("3311").getJSONObject(0);
            if (command.equals("dim")) {
              settings.put("5851", Integer.parseInt(message.toString()));
            } else if (command.equals("on")) {
              if (message.toString().equals("0")) {
                settings.put("5850", 0);
                settings.put("5851", 0);
              } else {
                settings.put("5850", 1);
                settings.put("5851", 128);
              }
            }
            String payload = json.toString();
            Main.this.set("coaps://" + ip + "//15001/" + id, payload);
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
        
        @Override
        public void deliveryComplete(IMqttDeliveryToken token) {
          // TODO Auto-generated method stub
        }
        
        @Override
        public void connectionLost(Throwable cause) {
          // TODO Auto-generated method stub
        }
      });
      mqttClient.subscribe("TRÅDFRI/+/control/+");
    } catch (MqttException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  private void discover() {
    try {
      URI uri = new URI("coaps://" + ip + "//15001");
      CoapClient client = new CoapClient(uri);
      client.setEndpoint(endPoint);
      CoapResponse response = client.get();
      JSONArray array = new JSONArray(response.getResponseText());
      for (int i=0; i<array.length(); i++) {
        String devUri = "coaps://"+ ip + "//15001/" + array.getInt(i);
        this.watch(devUri);
      }
      client.shutdown();
    } catch (URISyntaxException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  private void set(String uriString, String payload) {
    System.out.println("payload\n" + payload);
    try {
      URI uri = new URI(uriString);
      CoapClient client = new CoapClient(uri);
      client.setEndpoint(endPoint);
      CoapResponse response = client.put(payload, MediaTypeRegistry.TEXT_PLAIN);
      if (response.isSuccess()) {
        System.out.println("Yay");
      } else {
        System.out.println("Boo");
      }
      
      client.shutdown();
      
    } catch (URISyntaxException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  private void watch(String uriString) {
    
    try {
      URI uri = new URI(uriString);
      
      CoapClient client = new CoapClient(uri);
      client.setEndpoint(endPoint);
      CoapHandler handler = new CoapHandler() {
        
        @Override
        public void onLoad(CoapResponse response) {
          System.out.println(response.getResponseText());
          JSONObject json = new JSONObject(response.getResponseText());
          if (json.has("3311")){
            MqttMessage message = new MqttMessage();
            int state = json.getJSONArray("3311").getJSONObject(0).getInt("5850");
            message.setPayload(Integer.toString(state).getBytes());
            message.setRetained(true);
            String topic = "TRÅDFRI/" + json.getString("9001") + "/state/on";
            String topic2 = "TRÅDFRI/" + json.getString("9001") + "/state/dim";
            name2id.put(json.getString("9001"), json.getInt("9003"));
            MqttMessage message2 = new MqttMessage();
            int dim = json.getJSONArray("3311").getJSONObject(0).getInt("5851");
            message2.setPayload(Integer.toString(dim).getBytes());
            message2.setRetained(true);
            try {
              mqttClient.publish(topic, message);
              mqttClient.publish(topic2, message2);
            } catch (MqttException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
            }
          } else {
            System.out.println("not bulb");
          }
        }
        
        @Override
        public void onError() {
          // TODO Auto-generated method stub
          
        }
      };
      client.observe(handler);
    } catch (URISyntaxException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    
  }

  /**
   * @param args
   */
  public static void main(String[] args) throws InterruptedException {
    String psk = args[0];
    String ip = args[1];
    Main m = new Main(psk, ip);
    
    m.discover();
  }

}

I’ve tagged the code onto the gist as well for now, but I’ll check the whole thing in as a separate project soon.

EDIT: now with it’s own Github repo here

GPIO buttons – Demo stand

I recently got asked to help out building a demo for one of the projects the team has been working on.

The project uses situational awareness to vary the level of authentication needed to carry out different tasks. Rather than make the person using the demo run all over the place to change location or log in and out of various systems we have stubbed out the inputs and needed a way to toggle them locally. The demo runs on a tablet so we wanted something that could sit near by and allow the inputs to be varied.

To do this I thought I’d have a play with a Raspberry Pi and the GPIO pins. The plan was to attach 5 buttons to the toggle the state of the inputs, the buttons have lights that will show the state.

This would have been a perfect use case for the new Raspberry Pi Zero as it only needs 1 USB port and the GPIO pins, but I couldn’t convince any of the guys on the team lucky enough to grab one to give it up.

The chosen buttons where 5 of these:

http://uk.rs-online.com/web/p/push-button-switches/7027140/

I’d uses something very similar in the past for the MQTT Powered Video Walls in IBM Southbank Client Centre.

The first single button prototype worked nicely after I’d worked out what was needed with pull-up resistors for the buttons and current limiting resistors for the LEDs.

Button prototype

Node-RED was set up to read the state of the buttons from the GPIO pints and to update toggle the LED output the same way.

YouTube Preview Image
Node-RED flow to read buttons
Node-RED flow to read buttons

To make the demo totally standalone the rPi acts as a WiFi access point that the tablet connects to. It uses this connection to load the demo and to read the updates from the buttons via MQTT over Websockets.

The USB WiFi adapter available was a Edimax EW-7811UN, The default Linux driver for this doesn’t support HostAPD, luckily there is a page with instructions and a github project with a functional version.

Mosquitto 1.4.2 was installed to act as the the MQTT broker and to support MQTT over websockets so the web app could connect directly to get the button updates.

Having finished off the wiring and configuring the rPi the whole thing was mounted in a display board put together by Andy Feltham.

And ended up looking like this, all ready for a overlay with a description and instructions.

Completed Board

Building Bluetooth LE devices

While waiting for my flic.io buttons to turn up I’ve been playing with building my own Bluetooth Low Energy devices.

Since I already had a couple of sensors hooked up to publish their values via MQTT I thought I would try and build a bridge between MQTT and BLE.

I’m using a Raspberry Pi, a Bluetooth 4.0 USB dongle and a NodeJS npm module called bleno.

It turned out to be petty easy, first a short file to set up the BLE service and connect to MQTT:

var util = require('util');
var bleno = require('bleno');
var mqtt = require('mqtt');

var BlenoPrimarySerivce = bleno.PrimaryService;

var TopicCharacteristic = require('./topicCharacteristic');

var config = require("./config");

var client = mqtt.connect(config.broker);

var topicCharacteristic = new TopicCharacteristic(config);

client.on('connect', function(){
  client.subscribe(config.topic);
});

client.on('message',function(topic, message){
  topicCharacteristic.update(message);
});

bleno.on('stateChange', function(state){
  if (state === 'poweredOn') {
    bleno.startAdvertising('mqtt', ['ba42561bb1d2440a8d040cefb43faece']);
  } else {
    bleno.stopAdvertising();
  }
});

bleno.on('advertisingStart', function(error){
  if(!error) {
  	bleno.setServices([
  	  new BlenoPrimarySerivce({
  	  	uuid: 'ba42561bb1d2440a8d040cefb43faece',
  	  	characteristics: [
  	  	  topicCharacteristic
  	  	]
  	  })
  	]);
  }
});

And then something to add the characteristic for the topic:

var util = require('util');
var bleno = require('bleno');

function TopicCharacteristic(config, client) {
	bleno.Characteristic.call(this, {
		uuid: '6bcb06e2747542a9a62a54a1f3ce11e6',
		properties: ['read', 'write', 'notify'],
		descriptors: [
			new bleno.Descriptor({
				uuid: '2901',
				value: 'Topic: ' + config.topic
			})
		]
	});

	this._value = new Buffer(0);
	this._updateValueCallback = null;
	this._client = client;
	this._topic = config.topic;
}

util.inherits(TopicCharacteristic, bleno.Characteristic);

TopicCharacteristic.prototype.onWriteRequest = function(data, offset, withoutResponse, callback) {
	this._value = data;
	client.publish(this._topic, data);
	callback(this.RESULT_SUCCESS);
}


TopicCharacteristic.prototype.onReadRequest = function(offset, callback) {
	callback(this.RESULT_SUCCESS, this._value);
}

TopicCharacteristic.prototype.onSubscribe = function(maxValueSize, updateValueCallback) {
	this._updateValueCallback = updateValueCallback;
}

TopicCharacteristic.prototype.onUnsubscribe = function () {
	this._updateValueCallback = null;
}

TopicCharacteristic.prototype.update = function(value) {
	this._value = value;
	if (this._updateValueCallback) {
		this._updateValueCallback(this._value);
	}
}

module.exports = TopicCharacteristic;

I’ve used 2 randomly generated UUIDs, one for the service and one for the characteristic.

The code should allow you to read the last value published, publish a new value and subscribe to notifications when new values arrive.

I pulled together a quick Android app to subscribe to the notifications and update when a new value is published and it seams to be working well.

The code is all up on Github and on npmjs so feel free to have a play.

You can test it with the Bluez gatttoool.

[hardillb@bagend ~]$ gatttool -b 00:1A:7D:DA:71:15 -I
[00:1A:7D:DA:71:15][LE]> connect
Attempting to connect to 00:1A:7D:DA:71:15
Connection successful
[00:1A:7D:DA:71:15][LE]> primary
attr handle: 0x0001, end grp handle: 0x0005 uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x0006, end grp handle: 0x0009 uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x000a, end grp handle: 0x000e uuid: ba42561b-b1d2-440a-8d04-0cefb43faece
[00:1A:7D:DA:71:15][LE]> characteristics 
handle: 0x0002, char properties: 0x02, char value handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb
handle: 0x0004, char properties: 0x02, char value handle: 0x0005, uuid: 00002a01-0000-1000-8000-00805f9b34fb
handle: 0x0007, char properties: 0x20, char value handle: 0x0008, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x000b, char properties: 0x1a, char value handle: 0x000c, uuid: 6bcb06e2-7475-42a9-a62a-54a1f3ce11e6
[00:1A:7D:DA:71:15][LE]> char-write-req 0x000d 0300
Characteristic value was written successfully
Notification handle = 0x000c value: 42 61 72 
Notification handle = 0x000c value: 48 65 6c 6c 6f 57 6f 72 6c 64 
Notification handle = 0x000c value: 54 65 73 74 69 6e 67 20 31 32 33 
[00:1A:7D:DA:71:15][LE]> 
[00:1A:7D:DA:71:15][LE]> quit

The lines that start Notification handle contain the bytes published, in this case

  • Bar
  • HelloWorld
  • Testing 123

Moo Sticker engines fixed

A few years ago I create a web app to allow people to order their own sets of Moo stickers with the MQTT, Node-RED and Owntracks(at the time called MQTTitude) logos.

These worked well with people ordering about 1 pack a month until Moo changed the way they did authentication. I didn’t have time to fix it until today.

I’ve moved the app from being a J2EE app over to one written in NodeJS using express, this along with the change in Moo’s authentication method has made the code a lot shorter and easier to read. I’ve published the new code on github here.

To order a set of stickers click on the appropriate image:

Playing with Asterisk PBX

I’ve been meaning to get back and have a proper play with Asterisk again for a while. Last week Amazon sent me one of those emails about things you’ve looked at but not bought and I spotted this:

It was down from £60 to £35 so I did exactly what they wanted and bought one.

Now normally I don’t use my land line at all, it’s just there to let the internets in, it doesn’t even have a handset plugged in. But there are a few little projects kicking around the back of my mind I’ve been thinking about for a while and the OBi110 should let me play with them.

The first is to see if the (unused, never given to anybody but my ISP to set up the conection) number for the land line has ended up on any lists for scamers/spammers and people generally trying to sell me stuff. My mobile gets at least 1 call a week about payment protection and the like and even my work office number has started getting recorded calls about getting my boiler replaced.

I could have probably just used the call log on the OBi110 but I wanted to be able to potentially record these calls and a few other things so I needed something a little smarter which is were Asterisk comes in. Asterisk is a opensource VoIP PBX this basically means it acts like a telephone exchange for calls made over the internet. I’ve seen people run Asterisk on the old Linksys Slugs so I was sure it should run fine on a Raspberry Pi as long as it wasn’t dealing with too many calls and not doing much codex transcoding. As I already had a Pi running my SMS/MMS rig it seamed like a good place to put all my telephone stuff.

Installing Asterisk on the Pi was just a case of running apt-get install asterisk. It comes with a bunch of default config files (in /etc/asterisk), but there are 2 main ones that I needed to change to make some simple things work.

sip.conf
This file is where you can configure what clients can connect to your asterisk instance via the SIP protocol. To start with I’m going to set up 2 different clients, one for a softphone running on my laptop and one for the OBi110. It sets up few things, but the import bit for later is the context which controls which bit of the extentions.conf file we jump to when receiving a call from each client.

[general]
context=local
srvlookup=yes

[softphone]
defaultuser=softphone
type=friend
secret=password123
qualify=no
nat=no
host=dynamic
canreinvite=no
context=local
disallow=all ; only the sensible codecs
allow=ulaw
allow=alaw
allow=gsm

[obihai]
defaultuser=obihai
type=friend
secret=password123
qualify=yes
dtmfmode=rfc2833
canreinvite=no
context=external
disallow=all
allow=ulaw

extensions.conf
This file defines how Asterisk should handle calls, it has two contexts called local and external. The local context defines 2 paths, the first for extension 100, when this number is called from the softphone Asterisk calls out to a small python program called agi-mqtt which publishes a JSON object to the calls/local MQTT topic which contains all the information Asterisk has about the call. It then answers the call then plays audio file containing HelloWorld and finally hangs the call up. I’m mainly using this local context to testing things out before copying them over to the external context.

The second path through the local context uses a special case extension number “_0Z.”, this matches any number that starts with 0[1-9] (so won’t match against 100). This path forwards the dialed number on to the OBi110 to place the call via the PSTN line.

The external context only contains 1 path which matches the phone number of the PSTN line and currently matches the 100 extension (play HelloWorld). At some point later I’ll setup this path to forward calls to a local softphone or forward to a voicemail account.

[local]
exten => _0Z.,1,AGI(/opt/asterisk/agi-mqtt/mqtt,/opt/asterisk/agi-mqtt/mqtt.cfg,calls/local)
exten => _0Z.,2,Dial(SIP/${EXTEN}@obihai);
exten => _0Z.,3,Congestion()
exten => _0Z.,103,Congestion()
exten => t,1,Hangup()

exten => 100,1,AGI(/opt/asterisk/agi-mqtt/mqtt,/opt/asterisk/agi-mqtt/mqtt.cfg,calls/local)
exten => 100,2,Answer()
exten => 100,3,Playback(en_US/hello-world)
exten => 100,4,Hangup()

[inbound]

exten => 0123456789,1,AGI(/opt/asterisk/agi-mqtt/mqtt,/opt/asterisk/agi-mqtt/mqtt.cfg,calls/pstn-in)
exten => 0123456789,2,Answer()
exten => 0123456789,3,Playback(en_US/hello-world)
exten => 0123456789,4,Hangup()

Now Asterisk is all working properly I setup the OBi110 using the instructions found here.

After a bit of playing I have inbound and outbound calls working and some MQTT enabled logging. Next up is looking at using the SIP Client built into Android to allow calls to be made and received from my mobile phone.

Unpacking binary data from MQTT in Javascript

While doing trawl of Stackoverflow for questions I might be able to help out with I came across this interesting looking question:

Receive binary with paho mqttws31.js

The question was how to unpack binary MQTT payloads into double precision floating point numbers in javascript when using the Paho MQTT over WebSockets client.

Normally I would just send floating point numbers as strings and parse them on the receiving end, but sending them as raw binary means much smaller messages, so I thought I’d see if I could help to find a solution.

A little bit of Googling turned up this link to the Javascript typed arrays which looked like it probably be in the right direction. At that point I got called away to look at something else so I stuck a quick answer in with a link and the following code snippet.

function onMessageArrived(message) {
  var payload = message.payloadByte()
  var doubleView = new Float64Array(payload);
  var number = doubleView[0];
  console.log(number);
}

Towards the end of the day I managed to have a look back and there was a comment from the original poster saying that the sample didn’t work. At that point I decided to write a simple little testcase.

First up quick little Java app to generate the messages.

import java.nio.ByteBuffer;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;

public class MessageSource {

  public static void main(String[] args) {
    try {
      MqttClient client = new MqttClient("tcp://localhost:1883", "doubleSource");
      client.connect();

      MqttMessage message = new MqttMessage();
      ByteBuffer buffer = ByteBuffer.allocate(8);
      buffer.putDouble(Math.PI);
      System.err.println(buffer.position() + "/" + buffer.limit());
      message.setPayload(buffer.array());
      client.publish("doubles", message);
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      client.disconnect();
    } catch (MqttException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}

It turns out that using typed arrays is a little more complicated and requires a bit of work to populate the data structures properly. First you need to create an ArrayBuffer of the right size, then wrap it in a Uint8Array in order to populate it, before changing to the Float64Array. After a little bit of playing around I got to this:

function onMessageArrived(message) {
  var payload = message.payloadBytes
  var length = payload.length;
  var buffer = new ArrayBuffer(length);
  uint = new Uint8Array(buffer);
  for (var i=0; i<length; i++) {
	  uint[i] = payload[i];
  }
  var doubleView = new Float64Array(uint.buffer);
  var number = doubleView[0];
  console.log("onMessageArrived:"+number);
};

But this was returning 3.207375630676366e-192 instead of Pi. A little more head scratching and the idea of checking the byte order kicked in:

function onMessageArrived(message) {
  var payload = message.payloadBytes
  var length = payload.length;
  var buffer = new ArrayBuffer(length);
  uint = new Uint8Array(buffer);
  for (var i=0; i<length; i++) {
	  uint[(length-1)-i] = payload[i];
  }
  var doubleView = new Float64Array(uint.buffer);
  var number = doubleView[0];
  console.log("onMessageArrived:"+number);
};

This now gave an answer of 3.141592653589793 which looked a lot better. I still think there may be a cleaner way to do with using a DataView object, but that’s enough for a Friday night.

EDIT:

Got up this morning having slept on it and came up with this:

function onMessageArrived(message) {
  var payload = message.payloadBytes
  var length = payload.length;
  var buffer = new ArrayBuffer(length);
  uint = new Uint8Array(buffer);
  for (var i=0; i<length; i++) {
	  uint[i] = payload[i];
  }
  var dataView = new DataView(uint.buffer);
  for (var i=0; i<length/8; i++) {
      console.log(dataView.getFloat64((i*8), false));
  }
};

This better fits the original question in that it will decode an arbitrary length array of doubles and since we know that Java is big endian, we can set the little endian flag to false to get the right conversion without having to re-order the array as we copy it into the buffer (which I’m pretty sure wouldn’t have worked for more than one value).

Network Attached Light Sensor

It was one of the projects that started out with an innocent enough question….

What do you know about light sensors?

The answer was not much, but I can find out, if you can give me some context (which is a pretty standard answer from ETS if it’s something new to us). The person asking was interested in measuring the natural light levels at a reasonable number of out door locations in order to help in making a call on if a given activity was safe to continue.

The client had already been looking at an existing solution which was using USB enabled light meters being routed over ethernet back to a central location. This felt like a really clunky solution so I was asked to have a look to see if I could come up with something a little different.

My first thought was to look at using something with a LDR but that would only really give relative light levels or require a lot of work to calibrate the system. It was time to have a bit of a poke around.

After a bit of searching I found reference to a TSL2561 ic which is a light level sensor that outputs via I2C. I found 2 boards with these sensors mounted, the first from Adafruit but I couldn’t find a UK supplier to get one to test with. A bit more digging and I found a very similar Sparkfun board that was available from Cool Components in the UK. The sensor has a range of 0.1 to 40k lux

The client has a PoE enabled ethernet network covering the site they wish to measure which means I should be able to thuse the a PoE enabled Ethernet Arduino to read from the sensor and report the values back to a central location. I used the sample library from Sparkfun as a starting point and then extended it with the MQTT client library from Nick O’leary.

I threw a prototype together with a breadboard and installed it on the window sill in my office to see how it performed. On the whole it seams pretty good, but I need to find a off the shelf meter that measures in lux to see how the values compare.

I now had a sensor that is publishing it’s light levels about once a second, this solved the initial problem but I needed to visualise the results. I fired up Node-Red to take the values and do a few things with them.

  • Stash the timestamp and light level in a mongo db store
  • Publish alerts when the value dropped below a given threshold
  • Use the light level to change the brightness of my Digispark RGB LED

I also ran up a visualisation using the Rickshaw Javascript library which allows you to draw time series charts using the D3 visualisation library.

The chart on the left show the sun coming over the building opposite my east facing office windows then cutting off sharply as it tracks round to the west.

The next challenge is to come up with an enclosure for the whole thing so it can survive outside in the British summer.

d3 MQTT Tree visualiser updated

I’ve been having a bit of a play updating my d3 based MQTT topic tree visualiser this weekend.

  1. I’ve been trying to tidy things up a bit and break the code out into a little standalone library and only leave the MQTT connection code in the index.html.
  2. I’ve been improving the handling of long payload. There is now a nicer popup tooltip with a longer version of the payload, it’s still cropped at 100 chars but I’m still working on wrapping it and showing a few lines to get more in.
  3. I’ve been moving the MQTT connection code over to use binary WebSocket connections* rather than the JSON based proxy I was using before. The version hosted here is still using the proxy for now, but the binary versions work and I’ll move it over once I’ve finished playing with my internal broker setup.

I still need to try and make the whole thing resize or at least not have hard coded dimensions, but that might have to wait for next time.

I’ve stuck the updated version up here, I’ll stick the code up on github once I get sign off from the boss.

https://github.com/hardillb/d3-MQTT-Topic-Tree

* There is a new IBM WebSphere MQ feature pack supporting this, details can be found here