23 Oct 2015, 13:54

Facebook is the Amazon AWS Killer

AWS is now an $8Bn business for Amazon. Could anyone have predicted that in 1997 or even 2007? This week I was talking to a few people about who might be able to disrupt AWS. It won’t be easy. We immediately discounted the old guys like HP, IBM, Dell etc and even Google seems to be a complete also-ran in IaaS. Microsoft Azure might have potential.

PaaS is separate as it’s higher up the stack and makes use of IaaS. I expect Open Source PaaS like OpenShift 3 to grow very quickly in the coming years as it makes deployment and management of Apps incredibly easy. Where PaaS might hurt AWS is in abstracting what IaaS you are using, so as long as the feature set is there, you don’t care where your Apps are running (apart from Geo lag and data protection laws).

One of the people I talked to thinks that AWS will be slayed by a startup who may not even exist yet. He also felt that smart VPSes like DigitalOcean might do it. I’m a huge fan of DO and gained enormous reliability and speed by switching to it from Google Compute Engine/CloudSQL whilst reducing my bill by 2/3rds. But it still feels like an SME/SMB or individual developer solution.

So I’m predicting it’ll be Facebook. Why? Think about the following:

The big open question is how they do their virtualisation and containerisation. If they have that nailed, then I think they might go for it.

Let’s re-visit this post in 5 years.

29 Sep 2015, 17:35

Running the latest Chromium 45 on Debian Jessie on your Raspberry Pi 2

For some odd reason, Google and others stopped auto-generating armhf binaries of Chromium last year. Version 37 was the last one I could find. Luckily the Ubuntu guys have been building it all along and their version 45 installs really easily on the all-new Debian/Raspbian Jessie on your Raspberry Pi 2.

Pi loves Chromium

This was a surprise as this kind of thing usually degenerates into dependency hell.

Here you go (updatred to mention libgcrypt):

wget http://ftp.us.debian.org/debian/pool/main/libg/libgcrypt11/libgcrypt11_1.5.0-5+deb7u3_armhf.deb
wget http://launchpadlibrarian.net/218525709/chromium-browser_45.0.2454.85-0ubuntu0.
wget http://launchpadlibrarian.net/218525711/chromium-codecs-ffmpeg-extra_45.0.2454.85-0ubuntu0.
sudo dpkg -i libgcrypt11_1.5.0-5+deb7u3_armhf.deb
sudo dpkg -i chromium-codecs-ffmpeg-extra_45.0.2454.85-0ubuntu0.
sudo dpkg -i chromium-browser_45.0.2454.85-0ubuntu0.

It even appears correctly in your desktop menus!

Note: Some users reporting issues in comments that I’m not seeing.

Apart from security fixes and the latest Chromium features, you also get full access to most of the Apps and Extensions in the Chrome Web Store. This includes the Espruino Chrome App, which means you or your kids now have everything you need to develop and deploy Embedded and IoT projects for under €100. Honestly, that’s revolutionary.

Pi loves Espruino

20 Sep 2015, 13:03

TingoDB and SQLite instead of MongoDB and MySQL for tiny projects or Raspberry Pi

As an old Embedded guy who actually cares about resource usage, I’ve been bothered for quite a while by the number of trivial Node.js projects out there that require a full-blown MongoDB or MySQL server to operate. It’s a particular problem on the Raspberry Pi or small DigitalOcean Droplets. In many cases a simple SQLite DB or TingoDB is more than sufficient.

If your Web App has 2 registered users and 100 visitors per day, may just maybe you don’t need MongoDB or MySQL. I have a set of small Node.js projects now where I use the following combinations and they all work well. Anything you are doing in-house on a Raspberry Pi should absolutely use one of these. In fact I’m not even sure recent Mongos run on the RPi 1?

Dulcimer, LevelUP and LevelDB

LevelDB and LevelUP are well known and widely used. I think Dulcimer probably less so. It’s a Mongoose-ish ORM for LevelDB. I was able to tie it into PassportJS without much effort to handle users/sessions/etc. I have two setups using it and both have been running for many months on d’internet with zero problems. In one case it’s a small stats dashboard running on DigitalOcean with a few registered users who check it maybe once a day. The other one is a registration system for people requesting access to some internal work resources. All submissions go into LevelDB. This is running on OpenShift Online.

TingoDB and Tungus

Of the NoSQL file-based databases, this seems to have the lowest profile and I think it deserves a lot of attention. TingoDB emulates most of the common MongoDB APIs and Tungus emulates most of the Mongoose ones. I was able to take a random Node.js project off GitHub which is based on MongoDB/Mongoose/Mongoskin and get it running using TingoDB/Tungus on an RPi with only a tiny bit of fiddling with the Agenda module. This is huge for those wanting to do NoSQL on Raspberry Pi. I’ll be playing a lot more with this setup in the coming weeks. You really should too.

SQLite, Knex and Bookshelf

Whilst the whole hipster dev world thinks NoSQL is the answer to everything, good old relational databases just keep on trucking. I’ve used SQLite many times over the years for simple projects and it has always worked well. I hadn’t realised until recently that the main SQL ORMs on Node.js support it out of the box. So again, if you have a project that needs a relational DB and doesn’t need huge scale, you should be looking at SQLite. I’m currently wrapping up a simple in-house project using SQLite with Knex and Bookshelf on a Raspberry Pi. The DB hold simple relationships between devices and their status.

The latter two setups above also have the big advantage that you can use them in development or in the early production stages and then you can drop in MongoDB or MySQL at a later stage if needed, without any re-coding.

20 Sep 2015, 11:38

Running ZeroMQ with Node.js on Raspberry Pi

ZeroMQ is a lightweight messaging library and looks ideal for lower powered devices like the RPi. Building and running is pretty easy. These are really just notes for myself for future reference.

I don’t actually need a messaging system yet but I wanted to play around with one. Running a simple Request / Reply setup with Node.js on both Windows 10 and RPi was interesting. One advantage a message queue gives you over REST is that the receiver can be out of action and still get all transmitted messages when it recovers. You also have things like Pub/Sub etc. I’ve been running the code from here 100% reliably for two days now on both setups. At some point I’ll check max throughput on a Pi.

Installation on Raspberry Pi:

sudo apt-get install libtool pkg-config build-essential autoconf automake
mkdir build
cd build/
wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.3.tar.gz
tar -zxvf libsodium-1.0.3.tar.gz
cd libsodium-1.0.3/
sudo make install
cd ..
wget http://download.zeromq.org/zeromq-4.1.3.tar.gz
tar -zxvf zeromq-4.1.3.tar.gz
cd zeromq-4.1.3/
sudo make install
sudo ldconfig

Test code for Request / Reply from here:


var zmq = require("zmq");  
var socket = zmq.socket("req");  
var counter = 0;

// Just a helper function for logging to the console with a timestamp.
function logToConsole (message) {  
    console.log("[" + new Date().toLocaleTimeString() + "] " + message);

function sendMessage (message) {  
    logToConsole("Sending: " + message);

// Add a callback for the event that is invoked when we receive a message.
socket.on("message", function (message) {  
    // Convert the message into a string and log to the console.
    logToConsole("Response: " + message.toString("utf8"));

// Begin listening for connections on all IP addresses on port 9998.
socket.bind("tcp://*:9998", function (error) {  
    if (error) {
        logToConsole("Failed to bind socket: " + error.message);
    else {
        logToConsole("Server listening on port 9998");

        // Increment the counter and send the value to the clients every second.
        setInterval(function () { sendMessage(counter++); }, 1000);


var zmq = require("zmq");  
var socket = zmq.socket("rep");

// Just a helper function for logging to the console with a timestamp.
function logToConsole (message) {  
    console.log("[" + new Date().toLocaleTimeString() + "] " + message);

// Add a callback for the event that is invoked when we receive a message.
socket.on("message", function (message) {  
    // Convert the message into a string and log to the console.
    logToConsole("Received message: " + message.toString("utf8"));

    // Send the message back aa a reply to the server.

// Connect to the server instance.

23 Aug 2015, 14:42

TIL you can import 3D models from Tinkercad to Minecraft. How cool is that?

We had friends over from the US last weekend and their kids loved my Printrbot 3D printer. They had learned Tinkercad in school so that’s what they used to create things so they could print them out. They are now hounding their parents for a printer. Oops :-) But I noticed a “Download to Minecraft” option in Tinkercad and finally just had a chance to try it out. Wow!

In Tinkercad

It’s incredibly easy. Take any model from Tinkercad and pick the Minecraft option. You may have to tweak the sizing when you are doing it seriously. Then install the MCEdit Minecraft editor and import the file into one of your worlds. Then load up Minecraft and thar she blows.

In Minecraft

I totally love the intersection of 3D design, 3D printing and Minecraft - the real and the virtual coming together. The younger kids are gonna be psyched when I show em!

22 Jul 2015, 08:14

Creating an OTP fob compatible with Google Authenticator using an Espruino Pico

Now that the Espruino Pico has (beta) HID support, it can pretend to be a keyboard or mouse (or other HID compatible device). This makes it possible to send characters to the active window on your Windows/Linux/Mac PC. I’ve cobbled together some code which turns the Pico into a device like a YubiKey. Press the button and get the latest auth code pasted automatically for you.

Espruino Pico OTP

This code is basically just from three sources, merged together with a few tiny tweaks by me.

(Note I’ve just discovered that Espruino has its own hmac implementation so I can possibly drop jsSHA)

The main thing you need for this is your Google Authenticator Secret. Unfortunately you can only get this when you have access to the original QR code that you scanned (it’s in the URL). But if you are setting up 2FA access to a new service e.g. Digital Ocean, then you can grab the secret and save it in the code. If your system also uses a PIN (e.g. SSO on some corporate sites), save that in the code too.

You’ll need a very recent pre-release Firmware for this to work at all e.g. from here

Once that’s flashed, go into Settings on the Web IDE and make sure “Set Current Time” is ticked in the Communications section. Auth Fobs rely on having an accurate time and that setting keeps unsetting itself on my PC.

Now copy the code below onto the Espruino using the Web IDE and then type save() to make sure it can survive a power-cycle. Remove and re-insert the Pico, press the button and you should get the auth code you need.

Now for the bad news: This currently doesn’t work on my Windows 8.1 PC but works semi-reliably on my work MBP. Once the HID code makes it to production, I’m sure it will work fine.

// Code assembled by Conor O'Neill from three sources. None of it copyright me.
// This code copyright Brian Turek https://github.com/Caligatio/jsSHA https://github.com/Caligatio/jsSHA/blob/master/LICENSE
'use strict';(function(E){function t(c,a,e){var g=0,b=[],d=0,f,k,l,h,m,w,n,q=!1,r=!1,p=[],t=[],v,u=!1;e=e||{};f=e.encoding||"UTF8";v=e.numRounds||1;l=y(a,f);if(v!==parseInt(v,10)||1>v)throw Error("numRounds must a integer >= 1");if("SHA-1"===c)m=512,w=z,n=F,h=160;else throw Error("Chosen SHA variant is not supported");k=x(c);this.setHMACKey=function(a,b,d){var e;if(!0===r)throw Error("HMAC key already set");if(!0===q)throw Error("Cannot set HMAC key after finalizing hash");if(!0===u)throw Error("Cannot set HMAC key after calling update");
f=(d||{}).encoding||"UTF8";b=y(b,f)(a);a=b.binLen;b=b.value;e=m>>>3;d=e/4-1;if(e<a/8){for(b=n(b,a,0,x(c));b.length<=d;)b.push(0);b[d]&=4294967040}else if(e>a/8){for(;b.length<=d;)b.push(0);b[d]&=4294967040}for(a=0;a<=d;a+=1)p[a]=b[a]^909522486,t[a]=b[a]^1549556828;k=w(p,k);g=m;r=!0};this.update=function(a){var c,e,f,h=0,n=m>>>5;c=l(a,b,d);a=c.binLen;e=c.value;c=a>>>5;for(f=0;f<c;f+=n)h+m<=a&&(k=w(e.slice(f,f+n),k),h+=m);g+=h;b=e.slice(h>>>5);d=a%m;u=!0};this.getHash=function(a,e){var f,l,m;if(!0===
r)throw Error("Cannot call getHash after setting HMAC key");m=A(e);switch(a){case "HEX":f=function(a){return B(a,m)};break;case "B64":f=function(a){return C(a,m)};break;case "BYTES":f=D;break;default:throw Error("format must be HEX, B64, or BYTES");}if(!1===q)for(k=n(b,d,g,k),l=1;l<v;l+=1)k=n(k,h,0,x(c));q=!0;return f(k)};this.getHMAC=function(a,e){var f,l,p;if(!1===r)throw Error("Cannot call getHMAC without first setting HMAC key");p=A(e);switch(a){case "HEX":f=function(a){return B(a,p)};break;case "B64":f=
function(a){return C(a,p)};break;case "BYTES":f=D;break;default:throw Error("outputFormat must be HEX, B64, or BYTES");}!1===q&&(l=n(b,d,g,k),k=w(t,x(c)),k=n(l,h,m,k));q=!0;return f(k)}}function G(c,a,e){var g=c.length,b,d,f,k,l;a=a||[0];e=e||0;l=e>>>3;if(0!==g%2)throw Error("String of HEX type must be in byte increments");for(b=0;b<g;b+=2){d=parseInt(c.substr(b,2),16);if(isNaN(d))throw Error("String of HEX type contains invalid characters");k=(b>>>1)+l;for(f=k>>>2;a.length<=f;)a.push(0);a[f]|=d<<
8*(3-k%4)}return{value:a,binLen:4*g+e}}function H(c,a,e){var g=[],b,d,f,k,g=a||[0];e=e||0;d=e>>>3;for(b=0;b<c.length;b+=1)a=c.charCodeAt(b),k=b+d,f=k>>>2,g.length<=f&&g.push(0),g[f]|=a<<8*(3-k%4);return{value:g,binLen:8*c.length+e}}function I(c,a,e){var g=[],b=0,d,f,k,l,h,m,g=a||[0];e=e||0;a=e>>>3;if(-1===c.search(/^[a-zA-Z0-9=+\/]+$/))throw Error("Invalid character in base-64 string");f=c.indexOf("=");c=c.replace(/\=/g,"");if(-1!==f&&f<c.length)throw Error("Invalid '=' found in base-64 string");
for(f=0;f<c.length;f+=4){h=c.substr(f,4);for(k=l=0;k<h.length;k+=1)d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(h[k]),l|=d<<18-6*k;for(k=0;k<h.length-1;k+=1){m=b+a;for(d=m>>>2;g.length<=d;)g.push(0);g[d]|=(l>>>16-8*k&255)<<8*(3-m%4);b+=1}}return{value:g,binLen:8*b+e}}function B(c,a){var e="",g=4*c.length,b,d;for(b=0;b<g;b+=1)d=c[b>>>2]>>>8*(3-b%4),e+="0123456789abcdef".charAt(d>>>4&15)+"0123456789abcdef".charAt(d&15);return a.outputUpper?e.toUpperCase():e}function C(c,
a){var e="",g=4*c.length,b,d,f;for(b=0;b<g;b+=3)for(f=b+1>>>2,d=c.length<=f?0:c[f],f=b+2>>>2,f=c.length<=f?0:c[f],f=(c[b>>>2]>>>8*(3-b%4)&255)<<16|(d>>>8*(3-(b+1)%4)&255)<<8|f>>>8*(3-(b+2)%4)&255,d=0;4>d;d+=1)8*b+6*d<=32*c.length?e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(f>>>6*(3-d)&63):e+=a.b64Pad;return e}function D(c){var a="",e=4*c.length,g,b;for(g=0;g<e;g+=1)b=c[g>>>2]>>>8*(3-g%4)&255,a+=String.fromCharCode(b);return a}function A(c){var a={outputUpper:!1,b64Pad:"="};
c=c||{};a.outputUpper=c.outputUpper||!1;a.b64Pad=c.b64Pad||"=";if("boolean"!==typeof a.outputUpper)throw Error("Invalid outputUpper formatting option");if("string"!==typeof a.b64Pad)throw Error("Invalid b64Pad formatting option");return a}function y(c,a){var e;switch(a){case "UTF8":case "UTF16BE":case "UTF16LE":break;default:throw Error("encoding must be UTF8, UTF16BE, or UTF16LE");}switch(c){case "HEX":e=G;break;case "TEXT":e=function(e,b,d){var f=[],c=[],l=0,h,m,p,n,q,f=b||[0];b=d||0;p=b>>>3;if("UTF8"===
a)for(h=0;h<e.length;h+=1)for(d=e.charCodeAt(h),c=[],128>d?c.push(d):2048>d?(c.push(192|d>>>6),c.push(128|d&63)):55296>d||57344<=d?c.push(224|d>>>12,128|d>>>6&63,128|d&63):(h+=1,d=65536+((d&1023)<<10|e.charCodeAt(h)&1023),c.push(240|d>>>18,128|d>>>12&63,128|d>>>6&63,128|d&63)),m=0;m<c.length;m+=1){q=l+p;for(n=q>>>2;f.length<=n;)f.push(0);f[n]|=c[m]<<8*(3-q%4);l+=1}else if("UTF16BE"===a||"UTF16LE"===a)for(h=0;h<e.length;h+=1){d=e.charCodeAt(h);"UTF16LE"===a&&(m=d&255,d=m<<8|d>>>8);q=l+p;for(n=q>>>
2;f.length<=n;)f.push(0);f[n]|=d<<8*(2-q%4);l+=2}return{value:f,binLen:8*l+b}};break;case "B64":e=I;break;case "BYTES":e=H;break;default:throw Error("format must be HEX, TEXT, B64, or BYTES");}return e}function r(c,a){return c<<a|c>>>32-a}function p(c,a){var e=(c&65535)+(a&65535);return((c>>>16)+(a>>>16)+(e>>>16)&65535)<<16|e&65535}function u(c,a,e,g,b){var d=(c&65535)+(a&65535)+(e&65535)+(g&65535)+(b&65535);return((c>>>16)+(a>>>16)+(e>>>16)+(g>>>16)+(b>>>16)+(d>>>16)&65535)<<16|d&65535}function x(c){if("SHA-1"===
c)c=[1732584193,4023233417,2562383102,271733878,3285377520];else throw Error("No SHA variants supported");return c}function z(c,a){var e=[],g,b,d,f,k,l,h;g=a[0];b=a[1];d=a[2];f=a[3];k=a[4];for(h=0;80>h;h+=1)e[h]=16>h?c[h]:r(e[h-3]^e[h-8]^e[h-14]^e[h-16],1),l=20>h?u(r(g,5),b&d^~b&f,k,1518500249,e[h]):40>h?u(r(g,5),b^d^f,k,1859775393,e[h]):60>h?u(r(g,5),b&d^b&f^d&f,k,2400959708,e[h]):u(r(g,5),b^d^f,k,3395469782,e[h]),k=f,f=d,d=r(b,30),b=g,g=l;a[0]=p(g,a[0]);a[1]=p(b,a[1]);a[2]=p(d,a[2]);a[3]=p(f,a[3]);
a[4]=p(k,a[4]);return a}function F(c,a,e,g){var b;for(b=(a+65>>>9<<4)+15;c.length<=b;)c.push(0);c[a>>>5]|=128<<24-a%32;c[b]=a+e;e=c.length;for(a=0;a<e;a+=16)g=z(c.slice(a,a+16),g);return g}"function"===typeof define&&define.amd?define(function(){return t}):"undefined"!==typeof exports?"undefined"!==typeof module&&module.exports?module.exports=exports=t:exports=t:E.jsSHA=t})(this);

// This code copyright Nic Raboy https://blog.nraboy.com/2014/10/generate-time-based-one-time-passwords-javascript/
TOTP = function() {

    var dec2hex = function(s) {
        return (s < 15.5 ? "0" : "") + Math.round(s).toString(16);

    var hex2dec = function(s) {
        return parseInt(s, 16);

    var leftpad = function(s, l, p) {
        if(l + 1 >= s.length) {
            s = Array(l + 1 - s.length).join(p) + s;
        return s;

    var base32tohex = function(base32) {
        var base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
        var bits = "";
        var hex = "";
        for(var i = 0; i < base32.length; i++) {
            var val = base32chars.indexOf(base32.charAt(i).toUpperCase());
            bits += leftpad(val.toString(2), 5, '0');
        for(var i = 0; i + 4 <= bits.length; i+=4) {
            var chunk = bits.substr(i, 4);
            hex = hex + parseInt(chunk, 2).toString(16) ;
        return hex;

    this.getOTP = function(secret) {
        try {
            var epoch = Math.round(new Date().getTime() / 1000.0);
            var time = leftpad(dec2hex(Math.floor(epoch / 30)), 16, "0");
            var hmacObj = new jsSHA("SHA-1", "HEX");
			hmacObj.setHMACKey(base32tohex(secret), "HEX")
            var hmac = hmacObj.getHMAC("HEX");
            var offset = hex2dec(hmac.substring(hmac.length - 1));
            var otp = (hex2dec(hmac.substr(offset * 2, 8)) & hex2dec("7fffffff")) + "";
            otp = (otp).substr(otp.length - 6, 6);
        } catch (error) {
            throw error;
        return otp;


var totpObj = new TOTP();

// This code copyright Gordon Williams, Pur3 Ltd.,  https://github.com/espruino/Espruino/blob/master/LICENSE
var kb = require("USBKeyboard");

setWatch(function() {
  // These two lines by Conor O'Neill :-)
	var pin = "put_your_PIN_Prefix_here";
  kb.type(pin + totpObj.getOTP("put_your_google_authenticator_secret_here").toString(), function() {
}, BTN, {debounce:100,repeat:true, edge:"rising"});

11 Jul 2015, 19:56

Node v0.10.40 and v0.12.7 for ARM v7 and v6 (Raspberry Pi, Raspberry Pi 2, Banana Pi, ODroid C1) available

I compiled both on a Banana Pi for a change. Nothing special for the 0.12 build, and ./configure –without-snapshot for the 0.10 build.

“make test” threw up the usual 7 minor errors or so.

I ran both Banana Pi and Raspberry Pi 2 without any immediate obvious issues.


mkdir nodetemp
cd nodetemp
wget http://conoroneill.net.s3.amazonaws.com/wp-content/uploads/2015/07/node-v0.10.40-linux-arm-v7.tar.gz
tar -zxvf node-v0.10.40-linux-arm-v7.tar.gz
cd usr/local
sudo cp -R * /usr/local/

UPDATE: I also built 0.10.40 for Raspberry Pi 1 (i.e. Original Model) ARM V6 on a Series 1 Model B: * node-v0.10.40-linux-arm-v6.tar.gz

UPDATE 2: I also built 0.12.7 for Raspberry Pi 1 (i.e. Original Model) ARM V6 on a Series 1 Model B: * node-v0.12.7-linux-arm-v6.tar.gz

06 Jun 2015, 15:06

Save your Yahoo Pipes as Node.js Apps for posterity

I swore I’d never use another Yahoo product after the cultural terrorism of their Geocities shutdown. Now one of their few bright lights of the past decade, Pipes, is biting the dust. However a very very smart person wrote a program called pipes2js three years ago which you may find useful. It converts your Pipes to Node.js apps!

RIP Pipes

Installation is a tiny bit different from the instructions as it doesn’t seem to be on NPM any more. Here’s what worked for me on a Fedora 22 Virtual Box. I assume OSX would be similar. I might try Windows 8.1 later for the laugh.

The steps are as follows:

  • sudo npm install -g git://github.com/neyric/pipes2js.git
  • Go to Yahoo Pipes
  • Get the ID of each Pipe from its URL
  • pipes2js the_id_of_your_pipe
  • cd pipes/the_id_of_your_pipe/
  • Edit package.json and change “pipes2js”: “0.0.1” to “pipes2js”: “git://github.com/neyric/pipes2js.git”
  • npm install .
  • node run.js

The sad reality for me is that most of my Pipes are no longer functional as they use the old brilliantly simple but deprecated Twitter APIs (shakes fist at Twitter too). But it’s nice to be able to keep some sort of copy and give 2 fingers to the purple charlatans.

Update: It runs fine on Windows 8.1 if you have the various Visual Studio Express bits installed to compile Native Node modules. You may also have to run pipes2js with the full path to pipes2js.cmd so Windows can find it.

01 May 2015, 17:49

A Node.js web-server running on Windows 10 IoT on a @Raspberry_Pi 2. Wow!

I’ve been Snarky McSnarkenstein about Windows 10 IoT on Raspberry Pi for quite a while. When it was originally announced, everyone seemed to think they were getting full-blown Windows for free on Pi. Many still think this. What you actually get is a GUI-less version of Windows on to which you can deploy Universal Apps (and others). So unlike Raspbian on Pi, you can’t actually develop on it, you have to have full Windows 10 running somewhere else to develop, compile, deploy and debug. Or as I said on Twitter:

OK, snark over. Let’s give this thing a go.

Several hours later……

Installation is still a nightmare in this Preview version. Between clicking on hidden EULAs and installers failing with nonsense errors, not to mention having to install Windows 10 Preview in VirtualBox, this is not for the faint of heart.

Win 10 VBox

The RaspberryPi bit was ok and creating the image on an SD Card worked fine. Win 10 IoT booted first time with no issues.

Win 10 IoT on Pi

After several more rounds of messing with Visual Studio, I followed the Hello World Tutorial and pointed it at my Pi. I was rather shocked to see it worked!


That was the point the scales fell from my eyes. This is actually a very powerful offering to anyone who uses Microsoft tools already. You can build Drag n Drop Universal Apps which deploy just as easily to the Pi as they do to Windows Desktop/Tablets/Phones. I think Kiosks and Control Dashboards could be a big seller here, particularly for the Pi Compute Module. Obviously Visual Studio itself is damned powerful and is considered by most people, even MSFT haters, to be the most powerful IDE available today.

Then I decided to go a step further. Could I get a Node.js program to deploy?

A bunch more stupid installers later……..

Node.js on Vis Studio

Deploy the code, annnnnnd

Node.js on Pi

Oh wow, that is a bit brain melting. So now I have a web-server on Node.js on Windows on Pi on ARM, all debuggable with some kick-ass tools on my PC.

The only major criticism I can make is that I’d like every single piece including VisStudio and all the add-ons to be installable with a command-line package manager. Much easier for everyone.

I doff my cap to you Mr Nadella, this really is the new Microsoft.


03 Apr 2015, 11:32

Node v0.12.2 for ARM V6/V7 including Raspberry Pi, Raspberry Pi 2 and ODROID-C1

As with Node 0.12.0 and 0.12.1, there are no-precompiled ARM versions of 0.12.2 on the main site.


I’ve built for Pi 1 (ARM v6) and ODROID-C1/Pi2 (ARM v7). The latter was built on an ODROID-C1 and it seems to run fine on the Pi 2 since both are ARM v7. It took 1 hr 15 mins to compile compared to 4+ hrs on the Pi 1.

“make test” on the ODROID gave the same results as on the Pi 1: [10:33|% 100|+ 784|- 4]: Done