This is a text-only version of the following page on https://raymii.org:
---
Title       :   Get serial port data on the web with live updating
Author      :   Remy van Elst
Date        :   28-09-2019
URL         :   https://raymii.org/s/blog/Get_serial_port_data_on_the_web_with_live_updating.html
Format      :   Markdown/HTML
---



Recently I was asked to build a test setup to measure the accuracy of a few
loadcells (weight sensors) in a frame. Someone other than me would execute the
test and required access to the measured data, preferably live and without
needing knowledge of ssh or linux. The weight sensors can be read with an
openscale board from sparkfun via a serial connection which the board emulates
over USB (via an FTDI chip), via that same serial connection you can also
calibrate them or change settings like amount of decimals. As a quick solution I
created a webpage that uses a small amount of javascript to live update the
readings from the board. screen is used to capture the output of the serial
connection to a file which the webpage displays and updates. In this article
I'll talk about how I put various bits of software together to get the output of
the serial connection onto a nice webpage with live updating.

![the hardware setup][1]

The picture above shows the hardware setup. The blurred part is the actual
hardware part with the load cells in it. You can see one cell on the left. The
red boards are the Sparkfun Openscale boards, connected via a USB hub to a
[Variscite VAR-SOM-MX6][3] board running Yocto Linux (openembedded). I built a
webserver package for the board (nginx) since normally it runs nothing except
the main application.

The choice for this linux board was just because that was available. It could
just as well be a regular PC or Raspberry Pi, anything that can run screen and a
webserver.

The tests themselves will be done by someone else, who can navigate to a webpage
to check the current readings. Below is a screenshot of the webpage:

![the webpage][2]


<p class="ad"> <b>Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:</b><br><br> <a href="https://leafnode.nl">I'm developing an open source monitoring app called  Leaf Node Monitoring, for windows, linux & android. Go check it out!</a><br><br> <a href="https://github.com/sponsors/RaymiiOrg/">Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.</a><br><br> <a href="https://www.digitalocean.com/?refcode=7435ae6b8212">You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60 days. </a><br><br> </p>


If the test results are good enough, those load cells might be used in the
future, but not with the sparkfun openscale boards. The cells will then just be
connected to the regular machine and we'll write our own software to use the
sensors.


### Hardware setup

The hardware I used consisted of common off the shelf parts:

- [5 Sparkfun OpenScale][4] boards
- [5 Double-ended beam load cells][5]
- 6 port USB hub
- Linux board to connect it all up (could be a Raspberry Pi)

Connecting it all is not a hard job either:

- Screw in the load cells on the desired places.
- Wire them into the screw terminal on the sparkfun board
- Connect the sparkfun boards to the USB hub
- Connect the USB hub to the linux computer

I didn't require a powered USB hub since the linux board itself gave enough
power but your results can vary. Try less sensors or a powered USB hub if the
serial connection is flaky.

### Software setup

This is where stuff gets interesting. The goal of this setup was to be easy for
a non linux user to use. Someone who will test the accuracy of the sensors, what
happens when you do X, how does the setup respons when Y happens, etc. That
person is not interested in the software side of things, they just require the
measurements.

The time I got allocated to build this was not much, that's why this setup is so
simple. With more time, I might have added graphs, log archiving or a piece of
custom software to read the sensors without the sparkfun board. But, it had to
be done quickly, which required me to think about what tools were at hand for
this job.

The essential thing the setup needed to do was to provide log data in an easily
accessible format. During testing you don't want to click five times or type
commands, you just want to test. The setup should provide that.

The sparkfun openscale boards barf their output right on the (emulated) serial
port. What if we could present that data on a webpage so that the tester just
needed a laptop or tablet during their testing. What if we could make that data
live update using modern fancy web techniques? (Remember I'm doing embedded C,
not javascript).

#### screen

Screen is a linux terminal multiplexer. You can have one terminal and use screen
to get more. Handy when you're doing work via SSH, you can reconnect to the
screen session and continue on if for whatever reason the connection dropped. Or
you can run your IRC client, it won't disconnect when you log off.

As a byproduct of earlier times you can also connect to a serial port. Handy for
sending `AT` commands to your modem or connecting to your actual physical
terminal. We're going to use that to connect to the USB serial device in the
sparkfun board.

Screen supports logging of the session to a text file and allows you to insert
timestamps in that log. Perfect for what we want. That log can then be read out
via the webpage we're creating later on.

Start screen with the following commandline options:

   screen -dmS sensor1 -L -Logfile sensor1.txt
   /dev/serial/by-id/usb-FTDI_FT231X_USB_UART_XXXX

Replace the actual serial device by the path to your device. The sparkfun board
all have a unique serial number, so I can match them to the actual sensors.

- `-dmS`: start screen in detached mode and name this session `sensor1`
- `-L`: log the output to a file
- `-Logfile`: change the filename of the log from the default to `sensor1.txt`

The output of the sensor will now be in text file `sensor1.txt`. If you use
`tail -f sensor1.txt` you'll notice that it only updates every 10 seconds,
whereas the openscale board outputs every 2 seconds. Luck has it that screen
allows you to set the flush rate of the log. You can either use a `.screenrc`
file, or send it to the running session as a screen command. My compiled version
of screen has some quirks due to it being an older version, so I cannot use the
`.screenrc` route. Using the `-X` option I can send a command to a running
screen session.

Note that this is not a shell command that will be executed, it is a screen
command. One you could also give inside the running screen session by pressing
`CTRL+A :` (colon).

The [command to set the flush rate][6] is: `logfile flush seconds`, replacing
seconds by the number of seconds.

To send it to a running screen session, in my case, `sensor1`, use the following
command:

   screen -S sensor1 -X logfile flush 2

No quotes around the options. If you `tail` again, you'll see the file being
updated every 2 seconds.

I put a construction like this in a shell script for all 5 sensors, the script
is autostarting at boot of the linux machine via `systemd`. That means that all
sensors will be logged to a file named `sensorX.txt` where X is the sensor
number.

With the logging of the serial port data set up we can continue on to create the
webpage.

#### Fancy pancy javascript webpage

As you might have guessed from the section heading, I don't code javascript
often. My piece of cake is C++ and C for small embedded platforms. If the code
looks simple to you then that's the reason. No frameworks, no `nodejs`, heck, if
I could get away with a `<meta http-equiv="refresh" content="2">` I'd use that.
But, modern day users expect more than that, so why not.

The code you'll see below uses bootstrap css for styling. Hosted bootstrap that
is, just because I'm lazy.

The javascript part is split up into two parts. One is to actually do the XHR
(ajax) request on a file and update the DOM (replace the current contents of an
html element with the new content) and one is to reload the five files using
that first function. The automagic reloading is set with a timer via a
`setInterval`.

The first function to reload the data:

   function reloadData(url, html_id)
   {
       var req = new XMLHttpRequest();
       var now = new Date();
       req.open("GET", url + "?" + now.getTime(), false);
       req.onreadystatechange = function ()
       {
           if(req.readyState === 4)
           {
               if(req.status === 200 || req.status == 0)
               {
                    dataDiv = document.getElementById(html_id);
                    dataDiv.innerHTML = req.responseText.split("\n").reverse().slice(0, 1000).join("\n");

               }
           }
       }
       req.send(null);
   }

The function accepts an URL and a HTML element ID to replace. A timestamp is
appended to every request so that it is not cached by the browser. If the
request fails, nothing happens. The `.onreadystatechange` contains the function
we want to execute every time the readyState attribute changes of the request.

There is no filtering or escaping of contents, the html attribute is blatantly
replaced.

In the status files the data is set with the newest lines on the bottom. For the
webpage, I want those on top, since you're not scrolling when doing the testing.
You just want to look. Therefore, the file line order is reversed with this bit:

   .split("\n").reverse().slice(0, 1000).join("\n");

It also limits the log to at max 1000 entries. Not a great seperation of
concerns, but for this stage, workable.

The second part is a wrapper function to call the `reloadData` on the five files
we want:

   function reloadFiles()
   {
       reloadData("sensor1.txt", "sensor1");
       reloadData("sensor2.txt", "sensor2");
       reloadData("sensor3.txt", "sensor3");
       reloadData("sensor4.txt", "sensor4");
       reloadData("sensor5.txt", "sensor5");
   }

The last part is this:

   setInterval(reloadFiles, 1000);

Which will execute the wrapper function every second.

The HTML part of the page contains some styling and columns for a header, the
actual data is put in four of these blocks:

   <div class="col-md-auto">
     <h2>Sensor 1</h2>
     <pre>
       <div id="sensor1" align="center">
         <p>Loading Sensor 1 Data...</p>
       </div>
     </pre>
   </div>

The text `Loading Sensor 1 Data...` will disappear the first time the function succeeds.

Bare-bones javascript that gets the job done. Maybe not the best, but this whole
setup was put together in two hours and that includes wiring up all the
hardware, which took most of the time.


[1]: /s/inc/img/logging_setup.jpg
[2]: /s/inc/img/logging_webpage.png
[3]: /s/inc/img/var-som-mx6.png
[4]: https://www.sparkfun.com/products/13261
[5]: https://en.wikipedia.org/wiki/Load_cell
[6]: https://www.gnu.org/software/screen/manual/html_node/Log.html

---

License:
All the text on this website is free as in freedom unless stated otherwise.
This means you can use it in any way you want, you can copy it, change it
the way you like and republish it, as long as you release the (modified)
content under the same license to give others the same freedoms you've got
and place my name and a link to this site with the article as source.

This site uses Google Analytics for statistics and Google Adwords for
advertisements. You are tracked and Google knows everything about you.
Use an adblocker like ublock-origin if you don't want it.

All the code on this website is licensed under the GNU GPL v3 license
unless already licensed under a license which does not allows this form
of licensing or if another license is stated on that page / in that software:

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

Just to be clear, the information on this website is for meant for educational
purposes and you use it at your own risk. I do not take responsibility if you
screw something up. Use common sense, do not 'rm -rf /' as root for example.
If you have any questions then do not hesitate to contact me.

See https://raymii.org/s/static/About.html for details.