The Web Serial API
The Web Serial API allows us to interact with serial devices by sending and receiving data from within a web browser.
Using this API we can select and connect to a serial device, and then send and receive messages in just a couple of lines of JavaScript code.
As you can imagine, this is API is only supported by modern Chromium based desktop browsers right now (April 2020) but hopefully support will improve in the near future. At this moment you need to enable your browser's Experimental Web Platform Features, just copy and paste the right URL:
chrome://flags/#enable-experimental-web-platform-features
opera://flags/#enable-experimental-web-platform-features
edge://flags/#enable-experimental-web-platform-features
Why?
Why not? This API brings one more capability to the more widespread platform, the web. Having access to physical devices will make it easier for people with web development knowledge to start diving into the waters of the IoT movement by interfacing with it through the browser, a familiar platform for them.
So dust off that Arduino you may have laying around, connect it to your computer and let's begin connecting the web and the physical world.
How?
Connection
After validating if serial is supported by the browser, we use the requestPort
method to prompt the user with a Browser provided UI displaying a list of available serial devices.
We then open
a port to connect with that device. Here the baudRate
needs to match the baud rate used by the device. The expected baud rate values are:
115200, 57600, 38400, 19200, 9600, 4800, 2400, 1800, 1200, 600, 300, 200, 150, 134, 110, 75, 50
Using the readable
ReadableStream and writable
WriteableStream properties that we get from the port we create a reader and a writer.
if ('serial' in navigator) {
try {
const port = await navigator.serial.requestPort();
await port.open({ baudRate: 9600 });
this.reader = port.readable.getReader();
this.writer = port.writable.getWriter();
}
catch (err) {
console.error('There was an error opening the serial port:', err);
}
}
else {
console.error('The Web serial API doesn\'t seem to be enabled in your browser.');
}
Here we're storing both the reader and the writer objects globally for later use.
Reading and Writing
The data that is transferred between the browser and the device is encoded, so we need to create an encoder to use when sending a message and a decoder to be able to properly read a message.
constructor() {
this.encoder = new TextEncoder();
this.decoder = new TextDecoder();
}
Writing
Writing data, or sending a message, is really easy. First we take the message we wish to send and encode it, then using the write
method of the global writer object we previously created.
async write(data) {
const dataArrayBuffer = this.encoder.encode(data);
return await this.writer.write(dataArrayBuffer);
}
The write
method returns a promise that we can use to validate the completion of the write process.
Reading
The reading process is actually similar to the write one, using the reader's read
method we get the data that's coming from the device and pass it to the decorder's decode
method.
async read() {
try {
const readerData = await this.reader.read();
return this.decoder.decode(readerData.value);
}
catch (err) {
const errorMessage = `error reading data: ${err}`;
console.error(errorMessage);
return errorMessage;
}
}
Here we can catch any reading error and do something with it.
That's it! that's the basic setup of a Web Serial API handler.
Examples
The basic example uses a regular Arduino without any additional hardware. When sending a 1
as a message the Arduino's onboard LED will light up, when sending 0
it'll turn off.
- Demo: https://unjavascripter-web-serial-example.glitch.me/
- Code: https://github.com/UnJavaScripter/web-serial-example
The "advanced" example keeps the same Web Serial API handler code, it just adds some extra bits to the client side code. It also expects the user to have a MAX7219 controlled 8x8 LED matrix, a really affordable and easy to get LED matrix.
- Demo: https://unjavascripter-web-serial-led-matrix.glitch.me/
- Code: https://github.com/UnJavaScripter/web-serial-led-matrix
Origin Trial
Update: Web Serial API is promoted to stable from Chrome 89. This means that an Origin Trial token is not required anymore.
Origin trials enable us to ship experimental features without having our users enable flags in their browsers. Check out more here: https://github.com/GoogleChrome/OriginTrials and here https://developers.chrome.com/origintrials/
Browser support
- Chrome
- Experimental: 80+
- Stable: 89+
- Edge (Chromium based)
- Opera (you need to enable it's flag in opera://flags/#enable-experimental-web-platform-features)
Further reading
- WICG's Serial API Draft: https://wicg.github.io/serial/
- WICG's Serial API Explainer: https://github.com/WICG/serial/blob/gh-pages/EXPLAINER.md
- Google's Weg Serial Codelab: https://codelabs.developers.google.com/codelabs/web-serial/#0
Next steps
Chromium + Web Serial API + Raspberry Pi == Awesomeness 🔥
Top comments (15)
I would like to use this to connect to HC-05, HC-06 devices for controlling arduinos boards.
Your web app doesn't seem to see those when I try to connect,
What do I need to do?
Indeed the example code is quite basic, you would need to update the arduino code to return the sensor readings and handle it on the browser side by catching the messages received by the
read
function. I'll check if I still have one of those sensors and maybe add it to the example code.Great Job, you have saved my life
that's great dude !
really made my day
I'm wondering how to use this with a react application ...
Hey Diego, thanks for the great tutorial! I'm running your demo with an Arduino Duemilanove right now and am getting the error "error reading data: TypeError: Cannot read property 'read' of undefined" when I try to send messages. Any idea what could be causing that? Thanks again!
Hi Lillie, It seems like I didn't add a Serial message to be send back in the Arduino code. I just updated the repo and the example. Give it a try and let me know how it goes.
Works like a charm! Thank you Diego, excited to use this in my own projects :)
Please let me know what you create :D
Hello Diego! Thanks for this, it's awesome and easy to implement. I just have one question...
When I run the project locally it works perfect but when I load it into a Elastic Beanstalk app running with ruby it seems like my browser doesn't have enabled "enable-experimental-web-platform-features" but it is enable. Any reason for this? Thanks in advance.
Hi, did you add an Origin Trial token to your app?
The enable-experimental-web-platform-features makes the API available for your browser when developing locally. Once you deploy it you'll need an Origin Trial token to make the browser load the API. Check developers.chrome.com/origintrials...
It's working fine. Thanks man!
Another thing I had to do in order to use Serial API was adding a SSL certificate to my site.
Great to know! Yep, these new APIs require the web app to use HTTPS to be made available because they can be a risk if not loaded securely.
I think either the experimental API was changed or your code is broken, because it doesn't run (either from here or gitHub) for me, but if I change 'baudrate' to 'baudRate' in your script it begins to work.
(I posted an issue on your github as well)
I definitely should blow away dust from my arduino xD. Good Job 👍
Thanks! Please do and let me know what you create :)