After the short excurse with Blynk in the previous post I wanted to do the same without a company between. After all, controlling an LED is rather simple.
So back to basics: MQTT it is. Got a Mosquitto instance on the Internet with the following docker-compose.yml definition:
version: '2'
services:
mqtt:
image: toke/mosquitto
ports:
- "1883:1883"
volumes:
- "./mqtt/config:/mqtt/config"
- "./mqtt/log:/mqtt/log"
- "./mqtt/data:/mqtt/data"
restart: always
Very basic configuration. No TLS, but I defined a user. Not secure, but good enough for now.
The Espruino program running on the ESP8266 is simple (slightly updated as I found out that properly reconnecting/re-subscribing is critical):
np=require("neopixel");
var debug=false;
var mqtt = require("MQTT").connect({
host: "mqtt.my.domain.org",
username: "MY_USERNAME",
password: "MY_PASSWORD",
});
var myPixel=[0, 0, 0];
const ledPin=D2;
function updateLED(v) {
np.write(ledPin, v);
}
// s: RxxxGxxxBxxx with xxx=0..255
// All valid: R0G0B0, G255R0B0, G10, G80B80R
function getRGB(s) {
var rgb=[-1, -1, -1];
var currentIndex=-1;
for (let i=0; i<s.length; ++i) {
switch(s[i]) {
case 'r':
case 'R': currentIndex=0; rgb[currentIndex]=0; break;
case 'g':
case 'G': currentIndex=1; rgb[currentIndex]=0; break;
case 'b':
case 'B': currentIndex=2; rgb[currentIndex]=0; break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (currentIndex!=-1) {
rgb[currentIndex]=rgb[currentIndex]*10+s.charCodeAt(i)-48;
break;
}
}
}
var res={};
if (rgb[0]!=-1) { res.r=Math.min(rgb[0], 255); }
if (rgb[1]!=-1) { res.g=Math.min(rgb[1], 255); }
if (rgb[2]!=-1) { res.b=Math.min(rgb[2], 255); }
return res;
}
function setLED(s) {
let values=getRGB(s);
if (values.hasOwnProperty('r')) myPixel[1]=values.r;
if (values.hasOwnProperty('g')) myPixel[0]=values.g;
if (values.hasOwnProperty('b')) myPixel[2]=values.b;
updateLED(myPixel);
}
function log(s) {
if (debug) {
console.log(s);
}
}
// Program starts here
updateLED([20,0,0]);
mqtt.on("connected", () => {
log("Subscribing to LED");
mqtt.subscribe("LED");
});
mqtt.on('disconnected', () => {
log("Disconnected...");
updateLED([0,20,0]);
//setInterval(()=>{
// updateLED([0,20,0]);
// setTimeout(()=>{updateLED([0,0,0]);},50);
//}, 500);
setTimeout(()=>{
log("reconnecting...");
mqtt.connect();
}, 1000);
});
mqtt.on('error', (err) => {
log("Got error: "+error);
updateLED([0,20,0]);
});
mqtt.on('publish', function (pub) {
log("topic: "+pub.topic);
log("message: "+pub.message);
setLED(pub.message);
});
mqtt.on('subscribed', () => {
log("Subscription successful");
});
mqtt.on('subscribed_fail', () => {
log("Subscription failed");
});
mqtt.on('unsubscribed', () => {
log("unsubscribed received");
});
mqtt.on('ping_reply', () => {
log("ping_reply received");
});
mqtt.on('puback', () => {
log("puback received");
});
mqtt.on('pubcomp', () => {
log("pubcomp received");
});
Test via plumber:
❯ plumber write mqtt --topic="LED" --client-id="Plumber-1" --address="tcp://MY_USERNAME:MY_PASSWORD@mqtt.my.domain.org:1883" --input-data "R200G0B200"
Testing also works with MQTTlens.
And there we go: An Internet controllable LED! Total costs: Wemos D1 mini: $3, WS2812B LED module: $1.