Node.js IoT – Create Local Module for CPU Sensor

CPU Sensor Local Module

We’re back and ready to do some refactoring of our CPU sensor so we can learn about Node.js modules and how to create them.  Building small, focused modules is one of the key tenets of the Node.js philosophy as summarized in The Node Way:

Building small, single-purpose modules is at the heart of the Node.js philosophy. Borrowing from Unix, Node.js encourages composing the complex and powerful out of smaller, simpler pieces. This idea trickles down from entire applications (using the best tool for the job vs. a full suite) to how the tools themselves are built.

This architectural strategy also aligns well with the Single responsibility principle used in object-oriented programming design which I have used as a C# and Java programmer.

Review of where we left off

If you have not read my first post in this series on Building a CPU Sensor, you might want to go ahead and do so now as it provides instructions for preparing your development environment and getting the first iteration of the CPU sensor created.  In our last post we built a Cross Platform CPU sensor that enabled us to retrieve CPU loading numbers from either Windows or *nix-based systems.  As a review, here is the source code of that finished project:

'use strict';
const os = require('os');
const cpu = require('windows-cpu');

const delaySeconds = 1;
const decimals = 3;

function loop() {
  cpuLoad((error, result) => {
    if (error) {
      console.log('CPU load - error retrieving');
    } else {
      console.log('CPU load is ' + result);
    }
  });

  setTimeout(loop, delaySeconds * 1000);
}

loop();

function cpuLoad(cb) {
  if (process.platform === 'win32') {
    cpu.totalLoad((error, cpus) => {
      if (error) {
        return cb(error);
      }
      // Average the CPU loads since may be multiple cores on the machine.
      let sum = cpus.reduce((a, b) => {
        return a + b;
      });
      let avg = sum / cpus.length;
      cb(null, avg);
    });
  } else {
    let linuxCpuLoad = os.loadavg()[0] * 100;
    linuxCpuLoad = linuxCpuLoad.toFixed(decimals);
    cb(null, linuxCpuLoad);
  }
}

Refactor CPU load function into a separate module

Let’s break out the CPU load in a separate module to start us down the path of small, single purpose modules.

Create a file called cpu-load.js in the root of your project directory and add the following code to this file:

'use strict';
const os = require('os');
const cpu = require('windows-cpu');

module.exports = function (decimals, cb) {
  // Decimals is used for linux CPU load only since windows-cpu rounds to whole numbers.
  decimals = decimals || 0;
  if (process.platform === 'win32') {
    cpu.totalLoad((error, cpus) => {
      if (error) {
        return cb(error);
      }
      // Average the CPU loads since may be multiple cores on the machine.
      let sum = cpus.reduce((a, b) => {
        return a + b;
      });
      let avg = sum / cpus.length;
      cb(null, avg);
    });
  } else {
    let linuxCpuLoad = os.loadavg()[0] * 100;
    linuxCpuLoad = linuxCpuLoad.toFixed(decimals);
    cb(null, linuxCpuLoad);
  }
};

In order to make the function available as a module for other Node.js programs, we use the module.exports command in line 5 to export the function. This article does a good job of delving deeper and explaining how module.exports works in Node.js.

Notice that we also include the require statement in this new module for the windows-cpu npm module on line 3.  This module is self-contained with everything it needs to function.  We will longer need to include the require statement for windows-cpu in our main program.

Additionally, we add a decimals parameter in our function on line 5 so we can control how many decimals appear to the right of the decimal point for the CPU load.

Be sure to save the changes you made to the  cpu-load.js file you just created.

Update main program file to invoke our new module

We are now ready to update our main program file and call our new module!  Here is what the updated version looks like:

'use strict';
const cpuLoad = require('./cpu-load.js');
const delaySeconds = 1;
const decimals = 2;

function loop() {
  cpuLoad(decimals, (error, result) => {
    if (error) {
      console.log('CPU load - error retrieving');
    } else {
      console.log('CPU load is ' + result);
    }
  });

  setTimeout(loop, delaySeconds * 1000);
}

loop();

Our new main program is much simplified since we have moved the cpuLoad functionality to a separate module/file.  You can see that we simply use a require statement on line 2 to reference the module we created above.

Simulate an npm module

As a final step, we will utilize some npm magic to make our module appear to be an npm module coming from the npm public repository.  In a future tutorial, we will learn how to create and publish a real module to npm.  Let’s go!

Create new module folder and files

First, we’ll create a directory parallel to our project to host the module.  Let’s call it universal-cpu-load and position it in our folder hierarchy as follows:

P:.
\---node
    +---cpu-sensor
    \---universal-cpu-load

Navigate to the new universal-cpu-load folder you just created:

p:\node\cpu-sensor> cd ..\universal-cpu-load

Run the following command to create a package.json file. You can either accept the default values when prompted or enter values as you see fit:

p:\node\universal-cpu-load> npm init

Next, install the windows-cpu npm package in the new module folder and save the package as a dependency in your package.json file.

p:\node\universal-cpu-load> npm install windows-cpu --save

We are ready to add the function to create our new Node module!  Create an index.js file in the root of the new module folder and enter the following:

'use strict';
const os = require('os');
const cpu = require('windows-cpu');

module.exports = function (decimals, cb) {
  // Decimals is used for linux CPU load only since windows-cpu rounds to whole numbers.
  decimals = decimals || 0;
  if (process.platform === 'win32') {
    cpu.totalLoad((error, cpus) => {
      if (error) {
        return cb(error);
      }
      // Average the CPU loads since may be multiple cores on the machine.
      let sum = cpus.reduce((a, b) => {
        return a + b;
      });
      let avg = sum / cpus.length;
      cb(null, avg);
    });
  } else {
    let linuxCpuLoad = os.loadavg()[0] * 100;
    linuxCpuLoad = linuxCpuLoad.toFixed(decimals);
    cb(null, linuxCpuLoad);
  }
};

Link the module so it is available to the original Node.js program

Here’s where the npm simulation magic comes into play.  Run an npm link command from the root of the module directory you just created to create a symbolic link from your global npm packages folder to your module:

p:\node\universal-cpu-load> npm link

Next, change the directory to your main application and run another npm link command containing the name of your module:

p:\node\cpu-sensor> npm link universal-cpu-load

After issuing this command, look in the node_modules directory of your cpu-sensor project folder.  You will see a symbolic link to the universal-cpu-load folder.  This is what happened behind the scenes as a result of utilizing the npm link command.

You can learn more about the npm link technique we employed above in this article.

Use the newly created module in your main program

Awesome – we’re ready for the final step of using our newly created module that appears to come directly from the public npm repository. Here’s an updated version of our program:

'use strict';
const cpuLoad = require('universal-cpu-load');
const delaySeconds = 1;
const decimals = 2;

function loop() {
  cpuLoad(decimals, (error, result) => {
    if (error) {
      console.log('CPU load - error retrieving');
    } else {
      console.log('CPU load is ' + result);
    }
  });

  setTimeout(loop, delaySeconds * 1000);
}

loop();

Notice that we can now use a require statement in line 2 to call our module without using a relative path (like we would for any standard npm module).  Run the program and verify that it works as expected.

Come back again for our next tutorial.  We will create a data visualization solution to monitor our CPU sensor in real-time and learn more valuable skills along the way!

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
Visual Studio Code Jumpstart for Node.js Developers
Node.js Learning through Making – Build a CPU Sensor
Node.js IoT – Build a Cross Platform CPU Sensor

One thought on “Node.js IoT – Create Local Module for CPU Sensor

Leave a Reply

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