SparkFun will be closed on Tuesday, November 5th to support our team in being able to go out and vote! Orders placed after 2 pm MT Monday, November 4th will ship on Wednesday, November 6th. Thanks for your patience and understanding.
My first few blog posts of the new year will focus on the high-tech tools that might assist in keeping some classic New Year's resolutions. If you missed the first one on vaping and smoking cessation please check it out! Also, this article serves as part 3 to my IoT Smart Pantry project. You can check out part 2 right here!
-Nick
If you tuned in last time, you'll recall we had just written an attractive JavaScript-powered interface for the Nutritionix API. By entering a UPC number into a field on the page and pressing enter, we could contact the Nutritionix database and retrieve a food product's nutritional information.
However, our webpage was confined to the result frame of a JSFiddle window. Today we'll liberate our page, not just from that frame but from the browser altogether. Not only will it look more attractive on its own, but it will also have access to all of the hardware drivers that it will need to manage the robotic portion of this project. To achieve this, we're going to need two separate tools:
To pull from Node's splash page, Node is a "JavaScript runtime built on Chrome's V8 JavaScript engine." In other words, Node.js uses the [guts](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) of the Chrome Web Browser to run JavaScript code locally on your machine. When the project began in 2009, it got a lot of attention not just from JavaScript programmers (who were now capable of leaving the client-side to write web servers and networking tools), but also from critics of the established HTTP servers, many of which lacked the capability for asynchronous I/O.
Installing Node on our machine will allow us to write JavaScript code that has hardware access and can do things like control motors and read from barcode scanners. Unfortunately, Node, being designed as a server-side runtime, does away with the Document object. That Document object is what JavaScript interacted with to give us an attractive GUI for our webpage. To get that back, we're gonna need...
NWjs is a [Chromium](https://en.wikipedia.org/wiki/Chromium_(web_browser))-based app runtime for creating native applications using web technologies. By essentially wrapping our JavaScript, HTML5 and CSS in a Chromium window, NWjs allows us to keep all of the work we did creating a webpage and add onto that work with node modules. When we open our page in NWjs, it will no longer live in a browser window and will act like any other application. You can even package and distribute your NWjs apps as executables!
My goal for this session was simple: move our webpage from JSFiddle to a local NWjs project and replace the UPC entry field with automatic entry from the scanner. Several of you suggested after my last post that I should put the scanner in serial mode and incorporate it properly (not just emulating a keyboard). Along with that suggestion, I also got a lot of questions about building GUIs for embedded hardware. I think by demonstrating that we can read serial data into our NWjs app and adjust our GUI accordingly, I can answer both of those suggestions.
The first step was putting the scanner in serial mode. By default, our USB Barcode Scanner (which I'm learning now is a retired part? Say it ain't so!) comes configured to enumerate as a USB keyboard and transmit the barcode values as keystrokes. This is a pretty good solution for point-of-sale computers and makes it compatible with the widest range of software, but in our application it's actually more convenient if we don't have to highlight the desired text field before scanning an item. Luckily, the scanner comes with a programming manual and switching the device to serial mode was as easy as scanning a few barcodes. After opening a serial terminal on my computer and receiving a UPC at 9600/8-N-1, I was satisfied that the scanner was programmed and ready for action.
My first order of business was to install Node. This couldn't have been less painful under Windows 10 and I hear similar reports about installation under a variety of other platforms. The Node Project Site is beautifully maintained and their installation packages and instructions are clear and concise. Downloading and running the Windows installer was all I had to do to get Node.js running on my machine. The next step was to use the included Node Package Manager to install the serialport node module.
The serialport node module allows Node to access the hardware serial ports on your machine so you can talk to things like barcode scanners and Arduino boards. The constructors and methods that it adds are familiar and easy to work with – things like "read," "write," "open," "close" and "flush" can all be used in your JavaScript the same way you might use them in the Arduino language. This Node module also includes a number of parsers that make managing incoming data a breeze.
To install the serialport module, I opened the Node command prompt and typed:
npm install -g serialport
...the package manager took care of the rest. I opened the Node.js console to double-check that the library was now available and operating correctly. After entering a few lines of code:
var SerialPort = require('serialport');
var port = new SerialPort('COM5', {
baudRate: 9600
});
...I observed that COM5 was indeed open and Node had access to the drivers it needed.
The next thing on my list was installing NWjs. I say installing but there isn't much "installation" to do, just download the appropriate archive from the NWjs project page and extract it into a convenient location. Make sure you download the SDK version; it includes the developer console for Chromium so you can see if your JavaScript is throwing errors while your app is running!
Now I'm going to save you some trouble and warn you that you will probably have to build a native module for serialport in order for it to access the necessary drivers. Check out this stub on building native modules using nw gyp for more information, but don't be intimidated. I was able to use the compilation notes from this excellent GitHub repo to build for Windows 10.
A quick peek at the NWjs docs reveals that making a basic app is very easy. The whole process centers around creating a JSON file that tells NWjs how to display our app. The only mandatory fields in this JSON document are the name of the project and the main html page. The package.json file for my application looks like this:
{
"name": "NutritionApp",
"main": "index.html",
"window": {
"toolbar": false
}
}
That's all there is to it! This document tells NWjs everything it needs to know about your project. Place this document into a folder for your project along with the html, css, JavaScript and any other assets that your app might require. In my case, everything lived on JSFiddle so I needed to get it into discrete files in my project folder. I copied everything from the HTML frame into a new Notepad++ document and saved it as "index.html". Of course I had to add the proper HTML doctype declaration, the head and body tags and links to my script and css files, because JSFiddle took care of those things under the hood. I also copied each of the other frames into their respective documents (script.js and style.css) and then downloaded the minified versions of my included libraries (JQuery and JQuery-knobs) and placed them in the project folder. When I was finished, the folder looked like this:
Running our newly-created project is easy, but I needed to make a few modifications.
First, the "Enter a UPC" text input was removed from the html document and replaced with the words "Scan a UPC." Next, I began to integrate the serialport module into my JavaScript file by creating a port object:
var SerialPort = require('serialport');
var port = new SerialPort('COM5', {
parser: SerialPort.parsers.readline('\r'),
baudRate: 9600
});
The parser that I attached to this port is called the readline parser and when it receives a designated line-end character, it returns any data in the buffer preceding it. I designated '\r' (ascii 13 or Carriage Return) as the line-end character because this is what the barcode scanner sends to mark the end of a barcode. From here, it only takes a few more lines to make it work:
port.on('data', function (data) {
upcGet(data, postResults);
});
The port.on method designates a function to run whenever the parser has data from the serial port. I simply wrote an anonymous function which calls my existing upcGet() function, the same one I was calling before when the enter key was pressed. Now, when the application starts up it will display our GUI and attempt to open the serial port. If it can open the port, it will wait until some data shows up with a '\r' on the end of it and send that data to my upcGet function to be used in a call to the Nutritionix API. Let's see if it works.
To launch a NWjs app in Windows, all you have to do is drag the project folder onto the nw.exe icon. When I did that, I got this:
It looks exactly like it did in JSFiddle, except now it's free-floating in its very own window object. Now to connect our barcode scanner to the computer and reset the app... Let's see if it works:
Indeed it does!!! After scanning the UPC on my bag of Swedish Fish, my application returned the following results:
That's a success! And it means that this project is on track for a full-featured GUI that has total control over the physical hardware. Check in next time to see what I do with it. If you have any questions, I'll be hanging out in the comments section below. If you were one of the people who asked about connecting the barcode scanner using serial, I'd love to know if I answered your questions in this post! Thanks for reading!
To be honest, when I saw Swedish fish being mentioned, I was sure it was gonna be surströmning.
I use node.js to control a LOT of hardware projects - I highly recommend looking into Electron over NWjs. We've moved on at our company from NW to Electron and haven't looked back. I feel like it's a lot easier to work with and powers a lot of applications in heavy use (Slack, Atom, Visual Studio Code, Insomnia, etc).
Thanks for the update on the barcode scanner. I'll have to look into the behavior of the barcode scanner converted into serial mode to see if that would work for starting the project back up.
The concept I was working on involved the tracking of barcoded physical items on designated, barcoded shelves. I wanted to prototype using a thermal printer and barcode scanner so a user could scan an item, scan the shelf and automate a placeholder printout/receipt that listed the user, date/time of the pull, and the barcode of the object that usually lives that physical space. Since there is no need for outside communication to attach any relevance or extra meaning to the digits of the barcode, I figured I shouldn't need more than a basic Arduino to get it running... Yeah, there are probably commercial products that do exactly what I want to build, but where's the fun in that?