Digistump Oak-Based System Monitor

Embedded UI on the Oak – HTML and JavaScript

One final push and we’ve got this! Let’s take a look at the HTML and JavaScript that makes up the UI.

Most embedded UIs look awful, in my opinion. This is probably a combination of several factors. For one, those of us who do embedded systems are typically not UI designers. We’d rather be bit-banging IO or flipping register bits than trying to line up div elements with CSS. The other is that there’s usually not that much space to store UI elements on the device, so we opt to go small and simple.

I am by no means a “pro” web designer, but I learned to use a few frameworks that make creating something that looks halfway decent an easy thing. I suggest getting the hang of things like jQuery and Bootstrap, as they will save you a lot of time in producing simple yet elegant web interfaces.


Design Decision In order to store the HTML and JS on the Oak, I opted to use C++ Raw Strings. I also had to put this string into a .cpp file, so that the Arduino IDE wouldn’t preprocess it. It does some crazy stuff looking for functions to add prototypes for and so forth, if the file ends in .ino. Don’t let the filename scare you, it’s 99% HTML and JS with a little bit of trickery.

Design Decision Some ESP8266 boards will allow you to write files directly to the flash memory. The Oak doesn’t have this feature, however. It’s also possible to ask the compiler to put string literals into flash, as by default they go into SRAM. The ESP8266 has far less SRAM than it does flash, but it’s plenty for my purposes, so I didn’t bother trying to move the HTML/JS into flash.

Yadda yadda code snippets, yadda yadda GitHub.

<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>

Design Decision Note these lines in the HTML head – I’m using Bootstrap and jQuery, and I could have embedded these files in the Oak’s SRAM as well. However, there are free content delivery networks on the internet that will serve these files for you, so I’m going to leverage those. If you plan on not connecting your client to the internet, you might want to download these files, put them in strings, and then have them served by your Oak instead.

<h3>Disk Status</h3>
<div id="status-dsk"></div>

There are sections in the HTML that I mark with unique “id” values. That way, I can fill them in later, after I’ve gotten the latest report from the Oak. Most of the HTML in this is just for layout purposes, and all the data will be inserted into the layout later in JavaScript.

function reportSuccess(json) {
  if(json == null || typeof json !== 'object') {
    $("#rpt-status").html("Invalid response from Oak!");
  if('t' in json) {
    $("#rpt-status").html("Last update: "+json['t'])
  } else {
    $("#rpt-status").html("No update timestamp!");
  if('c' in json) {
    var output = ''
    for(var i in json['c']) {
      output += buildProgressBar("CPU"+i, json['c'][i], 100, "")
  if('m' in json) {
    var output = ''
    var used = (json['m']['t']-json['m']['a'])/(1024*1024)
    var total = json['m']['t']/(1024*1024)
    output = buildProgressBar("RAM", used, total, "MiB used")
  if('d' in json) {
    var output = ''
    for(var i in json['d']) {
      var used = json['d'][i]['u']/(1024*1024*1024)
      var total = json['d'][i]['t']/(1024*1024*1024)
      output += buildProgressBar(json['d'][i]['m'], used, total, "GiB used")

function reportError(xhr, status, error ) {
  $("#rpt-status").html("Error in the response from the Oak!");

function getReport() {
  $.ajax("/report", {

This chunk of code is responsible for requesting and displaying the PC’s report. We generated this report a loooong time ago in the Python code, shuffled it between two boards in Processing, and finally we’re fetching it to the client browser here.

It all starts with getReport, which uses jQuery’s ajax functionality to make an asynchronous request to the Oak. The Oak (hopefully) responds with a valid report, and then the reportSuccess function is called.

Design Decision Note that I don’t set a timer to call getReport again until after the Oak has responded. The ESP8266 is not a high performance web serving device by any stretch of the imagination. By waiting until the request finishes before ever thinking about starting a new one, the ESP8266’s web server doesn’t get backed up with requests.

In reportSuccess, the JSON response from the Oak is parsed out, and HTML is generated to show the results to the user. I have a helper routine called buildProgressBar that makes a nifty little progress bar out of data like CPU usage. After processing each section of the JSON, I write the output HTML into the div element whose ID corresponds to that section in the HTML.

  $("#kbd-send").click(function (e) {
      method: 'POST',
      success: onKbdSuccess,
      data: {mod:getMod(), str:$("#kbd-str").val()}

The buttons are assigned function callbacks in the $(document).ready() handler. This handler runs once the page is ready, so just one time after everything on the page has initialized. Here again, I use the ajax functionality of jQuery to send the request to the server. The data field in the ajax call sends parameters to the server that I can read in the Oak’s code. Here, I’m sending the mod parameter with the keyboard modifier boxes that have been checked, and the str parameter with the characters typed into the input blank.

Security Warning Just a reminder that if you are at all concerned about security (and for pete’s sake, you SHOULD BE!) this is a terrible idea to make a keyboard accessible over WiFi. Once SSL is supported by the ESP8266 Arduino libraries, and if you can come up with an authentication scheme to block unwanted users, this will become less of a security issue. For now, either don’t use this part of it (ie, comment out the Pro Micro code) or use at your own risk!

Conclusion & Next Steps

There are a few things I still need to do on this build:

I’d like to be able to put the PC to sleep via the Pro Micro. I think this should be possible by extending the USB HID descriptor so that it supports the “Sleep” key found on some multimedia keyboards. I just haven’t gotten around to parsing the USB HID spec and figuring out exactly how to extend the descriptor in the Arduino’s keyboard library. Waking the PC up is no big deal, just reset the Pro Micro via the web interface.

Security is still at the top of my mind, and I wish I could easily address it. SSL and a password on the Oak’s web interface will make this somewhat more secure, but having a keyboard accessible via WiFi is always going to be a bit of a security risk.


I did a crappy job of cutting the case, it’s all sorts of wonky. This thing will be hidden behind other cables, wires, etc, so it’s not a huge deal, but I do need to just take my time or perhaps just break out the Dremel next time.

I’ve done my best to summarize all the hardware and software involved in this build. No doubt there are areas I’ve glossed over in the previous 5,000 or so words, so feel free to ask questions and I’ll do my best to answer.

Prev Page 5 of 5 Next

  • Pradeep Kumar

    This post is awesome. Great work!!!….I am inpsired to chase down my project. I was one of the backer of Digistump Oak. I want to use oak to wake up my AVR from stand-by mode (for starters) and be able to control over wifi once I am familiar. I am just getting started. Your post motivates me to chase this down.

    • agent86ix

      This was quite the project! Unfortunately, I broke my Pro Micro a few weeks afterwards and haven’t fixed it yet. All that work, sigh… :(

      Good luck with your project!