Yesterday I came across this tweet from Charlie Gerard showing off an Arduino connected to a webpage:

How to pique my curiosity in one tweet

Her mention of a "Web Serial API" immediately got my attention as something that sounds kind of cool, but also dangerous (in a good way).

It turns out that there's a draft API from W3C, seemingly pushed somewhat by Google, to expose serial interfaces through a web browser. You can find the draft here, though it doesn't quite seem to match Chrome's implementation. This seemingly replaces Chrome's own serial API for Chrome Apps.

Note that only Chromium seems to have implemented it so far, and you have to manually enable it as it's guarded behind a feature flag for now.

Google also have this great getting started guide, interacting with a micro:bit board.

I wanted to play around with it, had a quick look around the house, and discovered that the only device I have left that still has a raw serial interface is my trusty Uniden UBCD396XT TrunkTracker IV radio scanner. It's a wonderful little device that lets me tune in to AM radio, FM radio, aviation/ATC, marine, and even the NSW Government Radio Network (GRN), home to the internal radio systems of NSW Ambulance, Fire & Rescue, the RFS, RMS, and so on.

It has a nifty little serial API that lets you remotely control the device, as well as read the system state and write to it. The only program I know of that actually implements this is FreeSCAN, an old hard-to-find tool that was written a long time ago in Visual Basic .NET. Fortunately, I managed to find bits of the spec online.

The browser API is actually surprisingly simple, as is the device's text-based API.

As far as the device is concerned, every command - request or response - is a comma-delimited list of values, and terminated with a new line (\r). The first value is the command type, any following values are command-specific parameters, or general error codes.

As far as the browser's concerned, it was a bit tricky when I started deviating from Google's sample code, but it's quite nice nonetheless.

To get a serial port, assuming your browser has serial support enabled, you have to call await navigator.serial.requestPort() from a trusted user event (that bit is very important!) such as a form onsubmit event or onclick event from a button the user clicked. The browser will prompt the user to select a port, and they can reject the request entirely:

Chrome's serial port selection interface. COM3 is my adapter, I don't actually know what COM1 is, and at this point I'm a little bit scared to find out.

Next, you have to open your port, by calling await{baudrate: myBaudRate});. You either need to let the user specify a baud rate, or try auto-detecting it.

There's probably a smarter way, but I implemented auto-detection by trying every supported baud rate (from fastest to slowest) and stopping when I found one that worked.

Once connected, since I want text input, I needed to set up a bunch of read and write text streams:

const decoder = new TextDecoderStream();
const inputDone = port.readable.pipeTo(decoder.writable);
const inputStream = decoder.readable;

const reader = inputStream.getReader();

const encoder = new TextEncoderStream();
const outputDone = encoder.readable.pipeTo(port.writable);
const outputStream = encoder.writable;

const writer = outputStream.getWriter();
Setting up read and write text streams from a serial port.

Once I've done that, I can read and write a bunch of data with await and await writer.write()!

I've put together a little experimental/demo site at which will auto-detect the baud rate and dump the list of systems stored in the programmable memory for my radio (or any other Uniden radio with the same serial protocol).

Dumping my current list of programmed systems from my Uniden radio scanner.

The code for this is available on GitHub.

I can imagine this being used to port all sorts of "legacy" systems to the web, like perhaps point-of-sale machines.

As a parting thought, I wonder if you could connect a serial 56K modem, and give your web page it's own internet connection... πŸ€”πŸ€”πŸ€”