Ask me anything
Netduino, WebSockets, SignalR
I recently completed a project for the Peggy Notebaert Nature Museum for their Nature’s Struggle Exhibit. They had purchased 8 mechanical switch type buttons, and 8 MK808 Android media players and needed help connecting them together to make 1 of the MK808s play a video when a corresponding button was pressed.
Because the buttons were simple switches, I knew the first step was going to be attaching them to one or more microcontrollers. Between the Arduino and Netduino I had available at the time, I went with the Netduino because its a bit easier for me to work with.
From the Netduino, there was either the option of using USB to connect directly to the MK808 or to use the network to connect. Bluetooth was in theory also an option, but suffered from the limitation that it could only pair with one device at a time, so running the 8 MKs would take 8 microcontrollers. The problem with USB was similar. For Bluetooth or USB, the only real option would be to create a native app and watch for their input, or use the web browser and watch for keystrokes. Considering all that, I decided to use the network connection to connect to the browser on each.
So this narrowed things down, now I needed 8 web pages, each with different media, and each updating when they were triggered via a websocket. SignalR turned out to be the simplest websocket arrangement for me to setup and the requirements ended up being really simple because in the original implementation, the only message ever passed over the socket was “play”
The basic program running on the Netduino was setup to watch each digital I/O pin for the button to trigger and use a socket connection to hit a URL corresponding the screen with the same number, which sends the websocket “play #” message to all the browsers. The Netduino socket connections are much lighter than the HTTP Client, which created all sorts of out of memory issues even in basic use.
Every browser knows its own index, so it only plays if it receives the matching index from the websocket. There is ways to only signal certain clients, but in the context of this project it doesn’t really matter.
So the final architecture is roughly:
Along the way I discovered it was also extremely useful to have a ping and reload request.
Ping allows me and administrators to see which browsers are still connected to SignlaR. The need for this came out of the network connection at the museum originally being very unreliable and the Android browser having additional problems that only aggravate that issue. With a basic ping implementation, at least we could see what was still online.
Reload allows me to push updates to the server and then remotely reload all the browsers to make sure they show the updated content. Without this, someone would need to actually be on site with a mouse and reload the eight browsers one at a time. I don’t think I’d ever create a websocket server/client combo where the server couldn’t reload the client remotely, there are too many situations where that is just vital.
I’ll post the code for this project soon.
Over the weekend I fixed a bug in my oldest in-production code, an Excel VBA Macro that I created about 15 years ago. It was one of those bugs that wasn’t noticeable at first, but over the years has caused more severe reporting issues, but never crashes or input issues. In other words, you’d only think it was there, if you looked into the reporting a bit.
The bug assumed that a semicolon delimited field would have fixed width data elements. Now you might ask, why would you need delimiters with fixed width data? And you’d have a good point. The bug was that instead of splitting the data with the delimiters, which seems obvious, a FOR loop incremented the character index by 6 in every loop. But, the data items had 6 characters, so with the delimiter the width of each item was actually 7 characters. So basically, the only possibly correct value returned, was the first value. Every subsequent value was omitted or returned incorrectly because part of the data happened to line up. Its a little worse than that because the data width was sometimes 8 instead of 6, but with so much else wrong, that’s an afterthought.
This is just embarrassingly bad code. I would never write something like that now. But the interesting thing is I’m not sure when I learned that lesson. Clearly I didn’t learn it on that project, or the bug would have been fixed already. I learned that lesson on some other project, but when? How many times did I make a mistake like that, and how many times did I make other mistakes, that remained bugs, until they were possibly fixed years later?
When we create software, we never know for sure how long it will be in use. When we accrue technical debt, we never know when there will be a chance to pay it off. Developers need to make a commitment to solve problems correctly, instead of easily, whenever possible. When time doesn’t allow for an ideal solution, we need to be honest with ourselves, our teams, and our documentation. Explain what’s missing and include the plan in documentation or comments.
You are probably writing bad code today, don’t let it go unnoticed for 15 years.
A doctype is required. The typical HTML5 <!DOCTYPE html> will do the trick. I could not figure out today why the inlined styles generated by Juice wouldn’t render a <table> as display: block; on any mobile clients but looked fine in FireFox and broken in Chrome. When I diffed a working file against a non-working file, my eye just skipped over the very first little line: Doctype, its your friend, or foe - depends on the day.