Mozilla WebSerial Testing

ESP32/8266 devices have a cool feature where firmware can be programmed into it through a web interface.
This post shows steps performed to learn how this is done.

Currently this has some limitations on browser compatibility, with anything running on Android not supported yet. This can be seen in some of the documentation, such as:

SerialPort: open() method – Web APIs | MDN (mozilla.org)

 

Install Web Server

Create a new jail in TrueNAS, give it a name like WebSerial.

pkg install nano
pkg install nginx
sysrc nginx_enable=YES
service nginx start

Then point a browser to the jail IP address and you should be greeted with the default nginx page being served up.

nginx important files and locations are:

/usr/local/etc/nginx/

This is the main install path.

nginx.conf is the main file that we can edit direct what pages/ports can be served up.

sites-available/ and sites-enabled/ folders should exist in this location. These should contain nginx config files for sites that we want to server up.

Create a new site:

sudo ln -s /etc/nginx/sites-available/ksfh.ddns.net /etc/nginx/sites-enabled/

paste some html into the file:

<html>
<head>
<title>Welcome to testsite!</title>
</head>
<body>
<h1>Success! The testsite server block is working!</h1>
</body>
</html>

 

Create a folder/file sites-available/testsite

nano sites-available/testsite

paste in some config code:

server {
  listen 80;
  listen [::]:80;

  root /var/www/testsite/html;
  index index.html index.htm;

  server_name testsite;

  location / {
    try_files $uri $uri/ =404;
  }
}

enable this site:

mkdir sites-enabled
sudo ln -s /usr/local/etc/nginx/sites-available/testsite /usr/local/etc/nginx/sites-enabled/

include sites-enabled in the base nginx conf:

nano nginx.conf

add this line inside the http block, commenting out any existing duplicated config. Note that the part must be absolute (for adding certificates later):

include "/usr/local/etc/nginx/sites-enabled/*";

Point your browser at your server IP address and see it serve up the index.html file created earlier.

 

Serve up https site

The WebSerial API requires https. So let’s create this.

Think of a site name, such as uflasher.mydomain.com (replace mydomain.com with a valid domain).

Log into your web control panel, and add a new DNS entry to point to the IP address of the new nginx server above. DNS records take a little while to propogate through the web. Maybe a few minutes, maybe hours.

I have a separate nginx server acting as a reverse proxy, so I can log into this and add details for uflasher.mydomain.com:

Create a file /sites-available/uflasher.mydomain.com and fill it with the following. We start by listeninig on port 80 since lets encrypt certbot will need this soon:

server {
  listen 80;
  server_name uflasher.mydomain.com;

  location / {
    proxy_pass http://(local IP address of flasher server);
    include proxy_params;
  }
}

Create the symlink into sites-available folder:

ln -s /usr/local/etc/nginx/sites-available/uflasher.mydomain.com /usr/local/etc/nginx/sites-enabled/

 

Reload the nginx config:

service nginx reload

 

Open port 80 on your router and forward it to the nginx reverse proxy server. This is needed for certbot to generate a ssl key. Port 80 can be closed after the certificate has been created.

Create a certificate using certbot:

certbot --nginx -d uflasher.mydomain.com -v

This responds with a message saying the certificate was created but couldn’t be installed. Edit the sites-available/uflasher.mydomain.com with the certificate info:

# HTTPS server
#

server {
  listen 443 ssl;
  server_name uflasher.mydomain.com;

  ssl_certificate /usr/local/etc/letsencrypt/live/uflasher.mydomain.com/cert.pem;
  ssl_certificate_key /usr/local/etc/letsencrypt/live/uflasher.mydomain.com/privkey.pem;

  ssl_session_cache shared:SSL:1m;
  ssl_session_timeout 5m;
  ssl_protocols TLSv1.3;

  ssl_ciphers HIGH:!aNULL:!MD5;
  ssl_prefer_server_ciphers on;

  location / {
    proxy_pass (ip address of uflasher server);
    include proxy_params;
  }
}

 

Reload the nginx configuration:

service nginx reload

 

Point the browser at https://uflasher.mydomain.com and see it serve up a secure page 🙂

 

Create a Page With WebSerial Calls

Add a couple of button and some script to the web page:

  <button id=“btn-select-port”>Request Ports</button><br>
  <br>
  <button id=“btn-open-port” style=visibility:hidden”>Open Port</button><br>

In the <script></script> section, create a class to hold our serial port:

    class SerialClass
    {
      constructor() {
        console.log(“SerialClass created”);
        this.serialPort = null;
      }
    }
    const serial = new SerialClass();

Add a listener for the btn-select-port press:

    document.getElementById(“btn-select-port”).addEventListener(‘click’, async () => {
      console.log(“btn-select-port pressed”);
      try
      {
        document.getElementById(“btn-open-port”).style.visibility = ‘hidden’;
        this.serialPort = null;
        this.serialPort = await navigator.serial.requestPort();  // var type has scope outside try-catch
      }
      catch (e)
      {
        console.log(“requestPort gave an error – “ + e.message);
      }
      if(this.serialPort != null)
      {
        process_port_request();
      }
    });

 

Running this page should open a port selection window when the request Port button is pressed:

If a port is selected, then it will call the process_port_request() function:

    function process_port_request()
    {
      console.log(“Processing port request”);
      try
      {
        str = “PID=0x” + this.serialPort.getInfo().usbProductId.toString(16) + “, VID=0x” + this.serialPort.getInfo().usbVendorId.toString(16);
        document.getElementById(“btn-open-port”).style.visibility = ‘visible’;
        console.log(str);
      }
      catch (e)
      {
        console.log(“Unable to process port request dues to “ + e.message);
      }
    }

This just contains some sanity checks and to enable the Open Port button.

Add some handling for the btn-open-port button:

    document.getElementById(“btn-open-port”).addEventListener(‘click’, async () => {
      console.log(“btn-open-port pressed”);
      try
      {
        await this.serialPort.open({ baudRate: 9600 /* pick your baud rate */ });
        console.log(“serialPort with PID 0x” + this.serialPort.getInfo().usbProductId.toString(16) + ” opened OK”);
      }
      catch (e)
      {
        console.log(“openPort gave an error – “ + e.message);
      }
    });

 

Pressing the Open Port button should show some console messages like “serialPort with PID 0xxxx opened OK”.

Next comes the fun part of sending and receiving data out the serial port…….

← All posts
← Gitlab CE Self Hosted Backups KS043 StanLink →