Node.js Learning through Making – Build a CPU Sensor

cpu sensor
We are back with our LTM (Learning through Making) tutorials and ready to hit the ground running and write some real Node.js code!  In this series, we will learn about Node.js in the context of creating IoT (Internet of Things) projects.  We will build a “CPU Sensor” in this first project since CPU loading/utilization is a “sensor” we can measure, record, and ultimately stream to other locations.  In future tutorials, we will harness the power of Node.js to interact with physical sensors that live outside of our computing environment.

VS Code iconI’ll be providing instruction from the premise that you are using Visual Studio Code as your editor and ultimately running the Node.js code on a Raspberry Pi.  If your environment varies from this, have no fear as you will still learn important Node.js programming constructs and skills alongside us. You will maximize the fun factor 😎, however, (especially as our LTM tutorial series progresses) if you include a Raspberry Pi in the mix. The following resources provide background information pertinent to this current tutorial:

Prep the development environment

Let’s get started and prepare our development environment to help us in our quest to write solid clean code—and write it in an expeditious fashion.

As a first step, follow my VS Code jumpstart for Node.js developers to get your environment set up including IntelliSense, JavaScript linting using ESLint, etc.

Next, let’s create a folder for the project.  We will conduct all of these initial steps from our laptop/desktop machine rather than from the Raspberry Pi. In my case, I have a “P” drive that is mapped to a Samba file share running on my Raspberry Pi. (The setup of the Samba file share is described here.)

Go ahead and create a folder in the following location ⇒  p:\node\cpu-sensor

We will now complete some steps to maximize the usefulness of our VS Code environment as, once again, described in my VS Code jumpstart for Node.js developers.  You can write all of your code in another text editor such as Notepad or Notepad++, but you will lose many bonus benefits that can help you in your software development endeavors.

Here are the VS Code configuration steps (also described in my jumpstart document) we will need to complete since we are starting a new Node project:

  • eslint –init (Run this command or copy the .eslintrc.json file from an existing project to the root of your new project.)
  • TypeScript definition files – must be installed for each new project.
    npm install -g typings
    typings install node --ambient
  • jsconfig.json configuration file – must be created or copied to the new project root from an existing project.  Here’s mine:
    {
        "compilerOptions": {
            "target": "es6",
            "module": "commonjs"
        }
    }

Create basic logging loop

Go ahead and launch VS Code and open the project folder using File | Open Folder… from the menu.

Let’s first start by creating a file called index.js in the root of our Node project.

We’re now going to write some code! You can copy and paste the code provided in these tutorials, but I highly recommend that you type it in manually.  Why? You will learn Node at a much deeper level.  In my experience, I find that I often *think* I understand a given programming syntax; nonetheless, I benefit significantly more by typing in the code, running the program, and sometimes (or often times 😉) failing.  You’ll learn a great deal more by troubleshooting your mistakes, and you’ll emerge as a better programmer.

Enter the following for your first Node program:

'use strict';
const delaySeconds = 1;

function loop() {
  console.log('Hello');
}

setInterval(loop, delaySeconds * 1000);

Let’s talk about what we are doing in the code above.  We start at the top with a 'use strict'; statement. I recommend that you use this at the top of all of your Node programs.  This ensures that your program runs your Node (JavaScript) code in strict mode, a restricted variant of JavaScript.  This helps catch some errors and also helps improve the performance of your code by guiding you in best practices.

We declare a constant called delaySeconds which helps us define how many seconds to delay between invocations of the code in the loop. The JavaScript setInterval and related setTimeout function express their times in milliseconds which is helpful for high precision. In my many years of working with massive sensor deployments in the industry, I have found a common blunder is to add too many or too few zeroes when specifying times in milliseconds, and I can assure you this can lead to some unexpected results.😀  We help keep ourselves in between the guardrails by configuring our loop time in seconds since we don’t need finer-grained time resolution than a second for our current endeavors.

Next, we provide a loop function and print a simple “Hello” to the console. The setInterval function is configured to invoke the statements in our loop function every second.

Run our newly created program

Let’s see our code come to life!  From your laptop/desktop, start a command prompt and run the following:

P:\node\cpu-sensor> node index.js

You should see your code begin to execute and print “Hello” every second.  Enter Ctrl+C to exit the loop.

If you are using a Raspberry Pi, you can also verify that this same code runs in that environment too.  Remote into the RasPi and launch a terminal session.  Enter the following commands:

$ cd share/node/cpu-sensor
$ node index.js

Lo and behold, you should see the same Node.js code running successfully on the RasPi as well!  Enter Ctrl+C to exit the loop.

The highly observant and astute among us may recognize that there is an initial delay of one second before the first “Hello” appears on the console.  Let’s remedy that so the code inside our loop executes immediately when the program begins.

Create logging loop without initial delay

Jump back into your laptop/desktop environment and change the code so it now looks like this:

'use strict';
const delaySeconds = 1;

function loop() {
  console.log('Hello');
  setTimeout(loop, delaySeconds * 1000);
}

loop();

With these changes, the loop in our code will run straight away when the program is fired up.  We accomplish this objective using the setTimeout function (in lieu of the setInterval function) to recursively invoke the loop program again after the delay period has elapsed.  This also provides the added benefit of guaranteeing that our code does not begin executing multiple times if our loop statements take longer to run than the delay time configured for the loop.  We will use this looping code construct moving forward for our IoT projects.

Run the program again on your laptop/desktop and/or your Raspberry Pi to ensure it works and so you can observe that we have successfully removed the initial delay.  We’re moving pretty fast at one second so you can change the delay time to five seconds or so if you want to thoroughly convince yourself that we have removed the initial delay.

Add the CPU sensor

Excellent – now that we have the basic looping and timing infrastructure in place, we are ready to add our “CPU sensor”.  This is where the fun really starts to kick in!  We’re going to use CPU loading as our sensor and measure and record it to the console.  Let’s go!

Modify your code so it looks like the following:

'use strict';
const os = require('os');
const delaySeconds = 1;

function loop() {
  let cpuLoad = os.loadavg()[0];
  console.log('CPU load is ' + cpuLoad);
  setTimeout(loop, delaySeconds * 1000);
}

loop();

I have highlighted the key lines of code that we have changed.

On line 2, we are using the Node require function to load a module assign that module to the os variable for later use.  The require function is similar in nature to the import statement used in Python and the using statement provided in C# for making code written elsewhere available to the current program.  The “os” module is built into Node so we don’t need to “npm install” it to have it available for use. We will discuss “npm install” as part of our next tutorial.

On line 6, we invoke the os.loadavg function to retrieve the current load average of the CPU.  As described in the documentation, this function returns an array containing the 1, 5, and 15 minute load averages.  We use the [0] at the load average function to retrieve the first value in the array in order to return a one minute load CPU load average.

Save your work and run the modified index.js file on your desktop/laptop machine.  If you run this on Windows, you’re going to be disappointed for now because it returns a zero all the time.😢  Don’t shed too many tears there, friend, since we’ll get that resolved in our next tutorial.

Go ahead and run the same code on your Raspberry Pi and you should see Node pumping out CPU sensor values other than zero.  It will look something like this:

CPU sensor output

Break CPU sensor value retrieval into a function and format value as a percent

Let’s do a little bit of refactoring and also format our CPU as a percent since most people think in terms of their CPU load as a percentage number such as 34 rather than .34.

'use strict';
const os = require('os');
const delaySeconds = 1;
const decimals = 3;

function loop() {
  console.log('CPU load is ' + cpuLoad());
  setTimeout(loop, delaySeconds * 1000);
}

function cpuLoad() {
  let cpuLoad = os.loadavg()[0] * 100;
  return cpuLoad.toFixed(decimals);
}

loop();

As you can see, we created a cpuLoad function and added code in this function to multiply the result we received by 100.  Additionally, we used the JavaScript toFixed function to format our result to 3 digits to the right of the decimal point.

Fire up the modified version of this Node program on your RasPi terminal and watch it print in the new format.  We have created something cool here for our first project!

Stress the Raspberry Pi CPU and watch the CPU sensor numbers climb

We’re having fun and watching our CPU sensor print values to our RasPi console, but the numbers are probably not changing much.  Let’s go ahead and introduce a load on the RasPi so we can watch the CPU load increase.

Open a new terminal window and install the sysbench utility so we can do some load testing:

sudo apt-get install sysbench

Accept all of the defaults when prompted.

Start your Node CPU sensor program in the other terminal, if it is not already started:

node index.js

Back in the other terminal, enter the following command to crank up the CPU loading on your RasPi:

sysbench --test=cpu --cpu-max-prime=20000 run

Watch your CPU sensor carefully. It is generating one minute averages, as opposed to spot data, so you will see a slight delay before the CPU load value starts taking off in a big way.

After watching your CPU load climb, enter Ctrl+C to terminate the sysbench program testing.  Observe how the CPU sensor value slowly drops back down as the CPU is provided relief from the intensive activity introduced by sysbench.  When you are ready, enter Ctrl+C to terminate the Node CPU sensor program too.

We successfully wrote a Node.js program and we’re even smarter than we started.  Stay tuned for next time as we build out our CPU sensor even further!

Follow @thisDaveJ on Twitter to stay up to date on the latest tutorials and tech articles.

Related Articles

Beginner’s Guide to Installing Node.js on a Raspberry Pi
Using Visual Studio Code with a Raspberry Pi (Raspbian)
Visual Studio Code Jumpstart for Node.js Developers
Learning through Making – Getting Started with Node.js

Share

One thought on “Node.js Learning through Making – Build a CPU Sensor

Leave a Reply

Your email address will not be published. Required fields are marked *