0% found this document useful (0 votes)
38 views93 pages

Nodebots Javascript and Robotic in The Real World

Uploaded by

gotowaringt
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
38 views93 pages

Nodebots Javascript and Robotic in The Real World

Uploaded by

gotowaringt
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 93

Nodebots - Javascript and robotics in the real

world
Wilson Mendes
This book is for sale at http://leanpub.com/nodebots-javascript-and-robotic-in-the-real-world

This version was published on 2020-05-12

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.

© 2016 - 2020 Wilson Mendes


Contents

About the author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

Nodebots and microcontrollers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4


What are nodebots? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Microcontrollers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
NodeJS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Managing dependencies with NPM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Firmata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Johnny Five . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

First project: Build Checker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20


What is a pipeline build . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Creating a Build Checker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Anatomy of a build checker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Materials needed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Creating the CI/CD build information request . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Tuning the architecture of our application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Creating unit tests for build checker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

Second project: Fire alarm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41


Anatomy of a fire alarm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Materials needed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Controlling the Flame Sensor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Evolving our initial code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Integrating with Piezo for audible warning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Sending SMS to your phone using Twilio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

Supporting Your Code on Multiple Operating Systems . . . . . . . . . . . . . . . . . . . . . . . 64


Adding Continuous Integration Servers to Your Project . . . . . . . . . . . . . . . . . . . . . 64
Code coverage for your code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
CONTENTS

Appendix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Breadboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Piezo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Resistors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
LED (Light-emitting diode) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Sensors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Protective Conductor (Ground or Ground Wire) . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Jumper Wires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Push Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

Next steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
About the author
GDE (Google Developer Expert) AngularJS, GDG Salvador organiser, passionate about technology
and active in communities focused on web development, including AngularJS, JavaScript, HTML5,
CSS3, workflow, web performance, security and Internet of things. Participates in the organisation
of events, lectures at conferences in Brazil and in other countries and contributes to few open source
projects.
Introduction
This book is for anyone who wants to take the first steps on Nodebots or has an interest in deliving
into some concepts that are poorly demonstrated on the subject.
It will be shown contents with simple and low-cost sensors, however relating the sensors with
real integrations of a nodebots application, such as integration between external APIs from events
reading some sensor data, among others.
In addition, the book is covering important topics in the test questions, showing good practices for
testing coverage and how to test with quality and performance, with integrations in external services
for automation of code quality and build validation tasks.
Another point is the evolution of the application with a focus on the architecture itself. How to use
software architecture standards in any type of project and how to take advantage of each of them
applying to your system.
Acknowledgements
I would like to say thank initially my mother (who has played the role of mother and father for
many times), this wonderful person who teaches me many of the values that I take with me until
today, being my example to this day and my brother Leonardo Araújo who was always with me.
I would like to thank my wife, Nilana Rocha, for all her support. It was one of the crucial factors
and without a doubt, this book would not be a reality. Thank you so much for everything.
A great time to remember all my friends from Lingu�gil Group and #horaExtraSSA from
Salvador-BA. You were people who motivated me to be the professional that I am today, encouraging
and motivating me to seek and share knowledge. Thank you all. For some Recife’s folks who are
amazing people and presented me with cheese curd, macaxeira (or aipim?) and much love.
Finally, I would like to thank Nelson Glauber¹ and Fagner Brack² for their support in reviewing
the contents of this book and for everyone who Have contributed directly or indirectly to the
composition of this book, such as Rafael Gomes³, Juliano Bersano⁴, Sarah Atkinson⁵ and several
others who supported me in this process and suggested improvements at this stage.
Thank you all.
Wilson Mendes
¹https://twitter.com/nglauber
²https://twitter.com/fagnerbrack
³https://twitter.com/gomex
⁴https://twitter.com/julianobersano
⁵https://twitter.com/SarahvAko
Nodebots and microcontrollers
What are nodebots?
NodeBots is a term used to define the concept of control over open hardware, an electronic hardware
designed and offered in the same manner and with the same licenses as free code software, sensors
and other electronic components using NodeJS. And you can use several elements: from sensors,
servo motors⁶, wheels, motion detectors, cameras, LED displays, audio players and more.
In some moments the concept of Nodebots is directly connected to the concept of IoT - Internet of
Things. Internet of Things * is a technological revolution in order to connect everyday electronic
devices such as home appliances and even industrial machines and devices with internet access and
other technical innovations in important fields such as home automation from Sensors.
The whole idea of NodeBots evolved according to increasing capabilities in NodeJS and the effort
of some developers like Nikolai Onken, J��rn Zaefferer, Chris Williams, Julian Gautier and Rick
Waldron who worked to develop the various modules we use in NodeBots applications nowadays.
The node-serialport⁷ module, created by Chris Williams, was the kick-start because it allows access
to devices using low-level serial ports.
Julian Gautier then implemented Firmata⁸, a protocol used to access microcontrollers, such as
Arduinos, via code using JavaScript for communication between physical components.
Rick Waldron went a little further. Using the Firmata library as a base, he created a framework to
assist in building Nodebots and Internet stuff called Johnny-Five.
The Johnny-Five framework makes it easy to control various components, from LEDs to various
other types of sensors in a simple and practical way. This is what many NodeBots now use to achieve
some awesome feats!

Microcontrollers
When we talk about nodebots, we are indirectly mentioning microcontrollers. A microcontroller is
a smaller and simpler computer that has a simple programmable physical circuit board (we’ll call it
pins, inputs, etc.) that can detect multiple inputs and outputs.
An Arduino is one of the several types of microcontrollers, being one of the most common for
experiments and validations between software and hardware integration. There are other types of
microcontrollers too, which can be powered by Node including:
⁶https://pt.wikipedia.org/wiki/Servomotor
⁷https://www.npmjs.com/package/serialport
⁸https://www.npmjs.com/package/firmata
Nodebots and microcontrollers 5

• Raspberry Pi⁹;
• Tessel¹⁰;
• Espruino¹¹;
• BeagleBone¹²;

In this book, I will use Arduino UNO¹³ in the examples, but feel free to use the microcontroller of
your choice.

NodeJS
NodeJS is a JavaScript execution runtime built on the Javascript engine Javascript V8, enabling the
use of Javascript in other environments beyond the web and with an important aspect of non-
blocking input model usage and Event-driven data output. It aims to help programmers to create
high-scalability applications such as web servers with concurrent connections, asynchronous scripts
and even integration with electronic components, that is our case.
It was created by Ryan Dahl in 2009, and its development is maintained by the community and the
Node Foundation, of which companies such as IBM, Google, Red Hat, Joyent, among others.

Installing on Windows
Installing NodeJS on Windows is quite simple. One way is to visit the official project website¹⁴ and
download the installer in the format, click on the installation options and finish the installation.
When finished open your command prompt by accessing the Windows command prompt from the
“Run> cmd” command and, after starting the program, enter the following command:

1 $ node -v

It should display at the prompt the current version of NodeJS on your terminal. This completes the
installation in the Windows operational system environment.

Installing on Linux and Mac OS X


For Linux and Mac OS X systems you can use various formats such as downloading the node on
the site (as we did on Windows installation), via package manager by operational system, but a way
to unify the installation format for UNIX platforms is to use the NVM - Node Version Manager¹⁵
which is a version manager of NodeJS based on bash script.
It’s something very simple to install as well. You can install locally via Curl or Wget, respectively:
⁹https://www.raspberrypi.org/
¹⁰https://tessel.io/
¹¹http://www.espruino.com/
¹²http://beagleboard.org/bone
¹³https://www.arduino.cc/en/Main/ArduinoBoardUno
¹⁴https://nodejs.org/en/download/
¹⁵https://github.com/creationix/nvm
Nodebots and microcontrollers 6

1 $ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.35.3/install.sh | bash

Wget:

1 $ wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.35.3/install.sh | ba\


2 sh

Packages like CURL and WGET may not be installed on your operating system by
default. If necessary, check the best method for your operating system or access the NVM
repository on Github to check the installation steps or possible problem solutions.

Next, you should open your file that stores the default configuration of your terminal, which can be
located in ∼/.bash_profile, ∼/.zshrc, ∼/.profile or ∼/.bashrc, and add these lines at the end
of this configuration file so that you will load NVM next time you access the command line.

1 export NVM_DIR="$HOME/.nvm"
2 [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm

With this, as soon as you recharge your terminal the NVM will be available. Now just install the
version of NodeJS of your choice. In this book, we will use version 12.16.2.

1 $ nvm install 12.16.2


2 $ nvm use 12.16.2
3 $ nvm alias default 12.16.2

After these commands NVM will download the specific version of NodeJS, making it accessible via
the terminal. To verify that the command completed successfully, enter the command:

1 $ node -v

The result should be v12.16.2. If this was the return of your command, you’re all set for the next
steps. If you have any problems make sure that the NVM loading code has been inserted into the
configuration file of your terminal and start another instance of your terminal.
A file with the commands in this topic was created for the installation of NVM and Node with the
version used in this book. If you want to use it, please download nvm-install.sh¹⁶ file in this gist link.
¹⁶https://gist.github.com/willmendesneto/4c951413bacbb8850837a53bcdada30d
Nodebots and microcontrollers 7

Managing dependencies with NPM

Starting your project and knowing the package.json file


As a first step, let’s create the "hello-world" folder and add some initial information in the project.
For this we will use the command npm init¹⁷.

Npm init command

To continue you will need to answer some basic questions about the project, such as: - Package
name; - Project version; - Description of the project; - Name of the project’s main file. This will
be produced at the end of your project, after all minification, obfuscation and other optimisation
procedures of your javascript code;
You can rest assured that none of them is mandatory. If you do not know or do not want to respond
now, just hit the “Enter” key until the end or run this command with -y flag, that means you are
answering yes for all those questions. It will then create a package.json file with this information
from your repository.
¹⁷https://docs.npmjs.com/cli/init
Nodebots and microcontrollers 8

Command output

Adding Packages
Now that we have our package.json¹⁸ with all the basic configurations of our repository, we will
install our first package to integrate with our project!
NPM functions are the official package manager for NodeJS applications, being the largest ecosystem
of libraries and open source modules in the world.
With it you can add packages in your application from the command npm install¹⁹, informing the
name of the package published in the official site of npm and the format that we want to save the
Package in the application. We have some options for adding packages, which are:

• locally as a development dependency: the package will be installed locally and accessible in
the node_modules folder and the package information will be saved in the devDependencies
key of your JSON file. To use this option add the flag --save-dev;
• locally as a dependency of your project: the package will be installed locally and accessible in
the node_modules folder and the package information will be saved in the dependencies key
of your JSON file. To use this option add the flag --save;
• globally: the package will be installed with global scope and accessible in any other project. To
use this option add the flag --global;
¹⁸https://docs.npmjs.com/files/package.json
¹⁹https://docs.npmjs.com/cli/install
Nodebots and microcontrollers 9

Packages saved as a development dependency and globally won’t be used when you
publish your project, so use these options carefully.

For our initial project we will install the Johnny-Five framework as one of the development
dependencies by running the command:

1 $ npm install --save johnny-five

You may notice that we now have some new files in our project folder. Firstly, the folder node_-
modules was created and inside it, we have our package installed successfully.

Listing the folder node_modules

Another new content information were added in the package in the dependencies block of our
package.json file.

1 {
2 "name": "hello-world",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.js",
6 "scripts": {
7 "test": "echo \"Error: no test specified\" && exit 1"
8 },
9 "author": "",
10 "license": "ISC",
11 "dependencies": {
12 "johnny-five": "^1.4.0"
13 }
14 }
Nodebots and microcontrollers 10

NPM has other standard commands and we can use them in our application. If you would
like to know more about these commands, go to the page about these commands in the
official NPM documentation²⁰.

Adding NPM Commands


NPM is a very interesting and flexible tool, with the possibility of creating specific commands
executed from npm run your-command.
In our example, we will create a sample command to run our code. Let’s open our package.json in
our editor/IDE ideally and add the following code:

1 {
2 "name": "hello-world",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.js",
6 "scripts": {
7 "dev": "echo \"This is our 'dev' NPM task\"",
8 "test": "echo \"Error: no test specified\" && exit 1"
9 },
10 "author": "",
11 "license": "ISC",
12 "dependencies": {
13 "johnny-five": "^1.4.0"
14 }
15 }

Notice that in line 7 we add our new NPM command through the JSON scripts field. Notice that we
already had the “test” command which is a standard npm command and it’s in the same area as our
new command, as this is the default area for adding commands to be executed by NPM.
To run the command, just access the terminal or command prompt and type npm run dev. The result
will return as follows:
²⁰https://docs.npmjs.com/misc/scripts
Nodebots and microcontrollers 11

Npm run dev output

Arduino

Arduino … ardu-WHAT?
Arduino is an open-source platform based on easy-to-use and plug-and-play hardware and integra-
tion with sensors from the software. Being a fully malleable and open platform anyone can use it in
projects of the most diverse as simple data checks received by light sensors, temperature, humidity,
home automation and more.
Some advantages are:

• Cost: the value of an Arduino is very low. Currently, the cost of an Arduino UNO²¹ is something
around $ 50.00 and Arduino Nano²² costs between $ 15.00 and $ 20.00, being more or less
according to the model of your choice;
• Cross-platform: Arduino is compatible with all operating systems and platforms;
• Simple: It does not require of those who will manipulate it a vast knowledge in electronics. Just
have a basic notion of development and you can already do things pretty cool;

About Open source hardware


Open source hardware is an electronic hardware with the same culture as free code software. This
term is used for the first time to reflect the idea of open and public information about hardware,
such as diagrams, product structures and layout data of a printed circuit board.
With the growth of programmable logic devices, the sharing of open logic schemes has also spread.
In this case, the hardware specifications are available for everyone, which means you can create or
evolve your hardware from that content without any problem.
²¹https://www.arduino.cc/en/Main/ArduinoBoardUno
²²https://www.arduino.Cc/en/Main/ArduinoBoardNano
Nodebots and microcontrollers 12

Installing Arduino IDE


Installing the Arduino IDE is quite simple. Just go to the official project website²³ and on the main
page, we can find all the download options per operating system. Check your operating system and
download the installer.

Official website of the Arduino project

There is a page on the Arduino Project Wiki with solutions to the most common problems²⁴, if you
have any kind of inconvenience with the installation and first setup Of the Arduino IDE.

Initial Arduino Setup


You can code using your preferred editor or IDE and start this step using the interchange²⁵
package. The purpose of the following content is to facilitate the Arduino setup for
developers who are having the first contact with the Arduino platform.

After the installation of the Arduino IDE, we will now access the program and verify its operation.
Firstly we realize that the Arduino IDE has some examples integrated as a mediator and facilitator
²³https://www.arduino.cc/en/Main/Software
²⁴https://github.com/arduino/Arduino/wiki/Building-Arduino
²⁵https://github.com/johnny-five-io/nodebots-interchange
Nodebots and microcontrollers 13

for those who have never had contact with the platform. To check the complete list of examples, just
access "File> Examples".
Note that in the example codes are written in [C language](https://pt.wikipedia.org/wiki/C_(pro-
gram language% C3% A7% C3% A3o) and not in Javascript, but nothing prevents you from running
the example code based in other programming languages.

Accessing the Arduino IDE Sample List

Let’s now connect our Arduino board into our operating system. Arduino IDE has already stored
the configuration of my Arduino, which appears in the item “Board: Arduino Genuino/UNO”, but
in the first time will appear in the “Port” option, which has the complete listing of serial ports based
Nodebots and microcontrollers 14

in your operational system.

Integrating the Arduino board to the operating system

The name will appear with the prefix “/dev/cu.” and will have the name of the Arduino, facilitating
the integration. Choose the port which your Arduino is connected and that’s all: the connection was
successful.

Firmata
Firmata is a protocol for communicating with microcontrollers software on a computer (or
smartphone/tablet, etc). The protocol can be implemented in the firmware of any microcontroller
architecture, such as any computer software package.
Our next step after installing the Arduino IDE is to add the Firmata protocol in our Arduino. Let’s
open our Arduino IDE and access the "Files> Examples> Firmware> StandardFirmata" option.
Nodebots and microcontrollers 15

Accessing the Firmware Sample Options in Arduino IDE

With the Arduino plugged into your computer you can run the following code and wait for the IDE
shows the message that everything happened successfully.
Nodebots and microcontrollers 16

Everything is OK. Firmata running

Johnny Five
Johnny-Five is an open source framework that allows you to control micro-controllers and compo-
nents using very similar functions that would be used if you were programming only for the Arduino
platform itself, but using JavaScript and implementing Firmata protocol for communication with
software and computer host.
This allows you to write a custom firmware without having to create your own protocol and objects
for the programming environment you are using. In summary, Johnny-Five is a node package that
allows you to program micro controllers using JavaScript!
Nodebots and microcontrollers 17

Adding johnny Five to the project


Like any good NodeJS package, adding Johnny-five to the project is a pretty simple task. For this
we will use the command that we have already seen, npm install, and install johnny five locally,
saving as a dependency of project development.

1 $ npm install --save johnny-five

After this command, NPM will create the folder node_modules and within it, we will have our
johnny-five package installed and accessible in the context of our project.
We can also check that our package.json has changed. In it were added the information of the name
of our NodeJS package and the installed version, as we can see in the code below.

1 {
2 "name": "hello-world",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.js",
6 "scripts": {
7 "test": "echo \"Error: no test specified\" && exit 1"
8 },
9 "author": "",
10 "license": "ISC",
11 "dependencies": {
12 "johnny-five": "^1.4.0"
13 }
14 }

Creating a Hello World


Now that all the setup of our project has been done, let’s create our example code, and nothing better
than the good old “Hello World”, welcoming you to the Nodebots world.
Let’s create a simple code. First, we will create the index.js file at the root of our project and import
the Johnny-five package using the require command.

1 ...
2 const five = require('johnny-five');
3 ...

This command makes the request of the package, making it accessible to our project. Now we’ll meet
our first class Jonny Five: the Board.
The Board class returns to the application an object that represents the physical board itself in the
electronics. All device objects depend on this object to be initialized.
Nodebots and microcontrollers 18

1 ...
2 const board = new five.Board();
3 ...

The board object has a .on() method, which is commonly used in Javascript applications for creating
event handlers. This method accepts 2 parameters:

• Name of the event;


• function to be executed when the event is triggered;

In this example, we will call this method with the ready option, which verifies when the code is
already accessing the physical card used.

1 ...
2 board.on('ready', () => {
3 console.log('Hello World!');
4 });
5 ...

And the final content of our index.js has been pretty tight, as you can see below.

1 const five = require('johnny-five');


2 const board = new five.Board();
3
4 board.on('ready', () => {
5 console.log('Hello World!');
6 });

Now we can run our code via the command line by typing the command:

1 $ node index.js

And the result returned will be the message “Hello World”, as you can see in the figure below.
Nodebots and microcontrollers 19

Running the command node index.js

If you want to make it easier, we can use npm start, one of the standard NPM commands we can
create in our package.json, making it accessible via the command line.

1 {
2 "name": "hello-world",
3 "version": "1.0.0",
4 "description": "",
5 "main": "index.js",
6 "scripts": {
7 "start": "node index.js",
8 "dev": "echo \"This is our 'dev' NPM task\"",
9 "test": "echo \"Error: no test specified\" && exit 1"
10 },
11 "author": "",
12 "license": "ISC",
13 "dependencies": {
14 "johnny-five": "^1.4.0"
15 }
16 }

After adding the command, just type in the npm start command line and the result will be the same
message as the previous command.
In this topic, you have seen the integration between our Javascript code and the hardware. In the next
chapters, we will see more examples showing in a simple and fun way how to integrate Nodebots
into our daily life.
First project: Build Checker
What is a pipeline build
In some presentations, I realised that this term is not known for many people, even those who are
more familiar with approaches such as continuous integration and continuous delivery.
Build pipeline is a concept that was built in the middle of 2005 and it’s based on the idea of task
parallelization, separating each step into small acceptance criteria for the application. It is worth
remembering that these steps can be automatic or manual.

Creating a Build Checker


Whenever we get in touch with the Arduino, for example, we make the example of flashing the
LED’s, commonly known as blink.
In this example, I will show you a more attractive way of approaching this example for our everyday
life, based on models such as Hubot²⁶ and Retaliation²⁷ to check our build pipeline and find out the
health of our application using Arduino + NodeJS + Johnny-Five in an introduction to NodeBots.

Anatomy of a build checker


The project was based on CCmenu²⁸, a project created by ThoughtWorker Erik Doernenburg to
check and show the status of a particular project on a seamless integration server.
In our case we get the idea for something physical, using open hardware and NodeJS. Our application
will consume an XML with the information returned by Travis-CI. From this data we check the
current state of the application and return it using some artifices like Arduino and LEDs to warn
our team that something wrong happened with our build and we must correct as soon as possible.

Materials needed
For this project we will use:
²⁶https://hubot.github.com/
²⁷https://github.com/codedance/Retaliation
²⁸http://ccmenu.org/
First project: Build Checker 21

• 1 Protoboard: A protoboard is nothing more than a plate with holes and conductive connec-
tions for mounting experimental electrical circuits, without the need for welding. A simple
protoboard costs between $ 5.00 to $ 10.00 and can be found in any electric store;
• 2 LEDs (light-emitting diode): 1 red to signal the broken build and 1 green to signal that the
build was successfully completed. A LED costs less than $ 0.50 and can be found at any electrical
store;
• Arduino with 2 GND (ground) ports;

Material needed for Build Checker

GND ports are called “ground” ports. They are electrical conductors that connect to the Earth - that
is, to the Electric Earth. Since it is always neutral and (theoretically) present in every electric circuit,
it is always taken as a reference point for the measurement of potentials, containing zero volts.
Now just plug the 2 LEDS, linking as follows:

• Successful build on the number 12 door + a GND port of our Arduino;


• Error build on port number 10 + a GND port of our Arduino;

The following image illustrates the assembly of the components with the Arduino.
First project: Build Checker 22

Connecting the Arduino: ports and LEDs

Controlling the LED


Knowing the components that we will use, we now go to our initial code that will control our LED.
First, we will create the folder src, where will be the code of our application.
Let’s create the folder for our build-checker project and navigate to the created folder.

1 $ mkdir build-checker
2 $ cd build-checker

We will start our application by typing the npm init command with the -y flag which means that all
the answers that were previously made will be answered and registered with the default response.
After this, we will install the johnny-five package locally as a dependency in the folder of our project.
First project: Build Checker 23

1 $ npm init -y
2 $ npm install --save johnny-five

Inside the folder of our project we will create the folder src and inside it, our file index.js where
will be our content.

1 $ mkdir src
2 $ touch src/index.js

In the index.js file we will import the Johnny-five package using the require command and create
our protoboard instance to add the LED.

1 ...
2 const five = require('johnny-five');
3 const board = new five.Board();
4 ...

For the first activity with the component, we will use a new Johnny Five class called LED. To use this
class we need to pass the value of the pin that the LED is connected to the Arduino.

1 ...
2 const led = new five.Led(12);
3 ...

Our first functional code will look something like this.

1 const five = require('johnny-five');


2 const board = new five.Board();
3
4 board.on('ready', () => {
5 const ledSuccess = new five.Led(12);
6 const ledError = new five.Led(10);
7
8 ledSuccess.blink();
9 ledError.blink();
10 });

Now let’s put our code to work on our Arduino. Inside the folder of our project, we will enter in our
command prompt/terminal the following command:
First project: Build Checker 24

1 $ node src/index.js

After this command, our prompt/command line is showing that the command has run successfully
and the result will be that our two LEDs will be flashing.
Quite simple, is not it? In the next topic, we will think a little more about our architecture.

Creating the CI/CD build information request


We already have our build checker abstraction, let’s now add the last functionality that is to read
the build info on our continuous delivery server and/or continuous integration server.
From there we created a request to read the content via HTTP GET in SNAP-CI²⁹, our continuous
integration service. SNAP-CI uses a build pipeline concept that is very interesting and one of its pros
is the faster feedback, giving the possibility of parallelism or not, and definition of steps for the total
build. For more information on Build Pipeline, I recommend reading Martin Fowler’s Continuous
Integration article³⁰.
We go to the SNAP-CI website, we log in and register a project. If you do not have a registration
you will have to create one, but it is very fast.

Adding repositories in Snap-CI

When registering the project it will appear in the upper part, on the right, a field with the name
“CCTray” that, when we click, it directs to the XML file with the information of the build.
²⁹https://snap-ci.com
³⁰http://www.martinfowler.com/articles/continuousIntegration.html
First project: Build Checker 25

Viewing pipelines created in Snap-CI

And these are the information that we will consult with our build-checker.

1 <Projects>
2 <Project
3 name="brasil-de-fato/news-service (master) :: CONTRACT-TEST"
4 activity="Sleeping"
5 lastBuildLabel="77"
6 lastBuildStatus="Success"
7 lastBuildTime="2016-01-21T18:46:48Z"
8 webUrl="https://snap-ci.com/brasil-de-fato/news-service/branch/master/logs/defaultPi\
9 peline/77/CONTRACT-TEST"/>
10 </Projects>

Analysing the information we realise that the only information we should validate for our build
is the data from the lastBuildStatus field. It is returned if the build was successfully completed,
successfully completed, or is happening at the time of validation.
The lastBuildStatus field can contain:

• Success: the last job on the server has been successfully completed;
• Failure: the last job on the server was terminated with error;
• Pending: the task is happening right now on the server;
• Exception and Unknown: Something unexpected occurred with the current task on the server.
The reasons are the most diverse, as the server had a swing in the middle of the task or he
never ran a certain task yet, so he does not have the information;

Let’s now add our URL containing the CCTray information from our project and create it with the
request for this information. For this, we will add the package NodeJS request³¹, an HTTP client
that was developed in order to facilitate the creation of HTTP or HTTPS requests. To add the new
packages we will enter the following command.
³¹https://github.com/request/request
First project: Build Checker 26

1 $ npm install --save request

The use of the request is very simple, accepting 2 parameters being the first the URL and
the second the function that will manipulate the requested information. For more details,
see full documentation of the request package in Github³².

Now let’s add the package in our project and create the request using the RequestJS module.

1 ...
2 const request = require('request');
3 const five = require('johnny-five');
4 const board = new five.Board();
5 ...

And we’ll add the CCTray address that we’ve copied from our continuous integration server into
our code and create the HTTP request to read the information.

1 // For educational purposes this code is using this format


2 // For computational resource issues use the URL as a string
3 // instead of manipulating the Array in the final project
4 const CI_CCTRACKER_URL = [
5 'https://snap-ci.com',
6 'willmendesneto',
7 'generator-reactor',
8 'branch',
9 'master',
10 'cctray.xml'
11 ].join('/');

Let’s explain a bit about RequestJS callback. It returns 3 parameters:

• error: an object with the error information that happened. If the request does not return any
error it has the default value null;
• response: object with the request response information;
• body: String with the information of the body of the return of the requisition;

In our code then we will analyse the return of the requisition and create the appropriate treatments.
The first treatment will be the verification of the error and in case we have an error we will treat
the error.

³²https://github.com/request/request#table-of-contents
First project: Build Checker 27

1 ...
2 request(CI_CCTRACKER_URL, function(error, response, body) {
3 if (error) {
4 console.log('Something is wrong in our CI/CD =(');
5 return;
6 }
7 ...
8 });

If the response does not return any error, we will treat the message to turn the LEDs on and off for
visual feedback.

1 ...
2 if(body.indexOf('Failure') !== -1) {
3 console.log('Your CI/CD is broken! Fix it!!!!');
4 ledSuccess.off();
5 ledError.on();
6 } else {
7 console.log('Your CI/CD is ok!');
8 ledSuccess.on();
9 ledError.off();
10 }
11 ...

With the request created, we will only create a gap between each request, using a simple
setInterval. We will use a time of 500 milliseconds for code validation, but this value can be
changed to whatever is ideal for you.

1 setInterval(() => {
2 request(CI_CCTRACKER_URL, function(error, response, body) {
3 ..
4 });
5 }, 500);

And the final content of our src/index.js was as follows:


First project: Build Checker 28

1 const request = require('request');


2 const five = require('johnny-five');
3 const board = new five.Board();
4
5 // For educational purposes this code is using this format
6 // For computational resource issues use the URL as a string
7 // instead of manipulating the Array in the final project
8 const CI_CCTRACKER_URL = [
9 'https://snap-ci.com',
10 'willmendesneto',
11 'generator-reactor',
12 'branch',
13 'master',
14 'cctray.xml'
15 ].join('/');
16
17 board.on('ready', () => {
18
19 const ledSuccess = new five.Led(12);
20 const ledError = new five.Led(10);
21
22 setInterval(() => {
23 request(CI_CCTRACKER_URL, function(error, response, body) {
24 if (error) {
25 console.log('Something is wrong in our CI/CD =(');
26 return;
27 }
28
29 if(body.indexOf('Failure') !== -1) {
30 console.log('Your CI/CD is broken! Fix it!!!!');
31 ledSuccess.off();
32 ledError.on();
33 } else {
34 console.log('Your CI/CD is ok!');
35 ledSuccess.on();
36 ledError.off();
37 }
38
39 });
40 }, 500);
41 });

It will query the data at a preconfigured interval and check the current state of the build, based on
First project: Build Checker 29

information from all pipelines. If there is no word “Failure” in the response to the request, something
wrong has happened and our build checker will turn on the red light, otherwise, the green light will
remain on, signalling that everything is ok.

Tuning the architecture of our application


Now that we’ve finished the first step and have seen our working with our components, let’s think
about improving our architecture.
We can see that we have some rather loose values, which do not say much if we do not access
the Johnny-Five framework documentation, such as numbers 12 and 10. For these and other
configurations we will create a configuration.js file with Project information.
The initial contents of this file will be:

1 // src/configuration.js
2 module.exports = {
3 LED: {
4 SUCCESS: 12,
5 ERROR: 10
6 },
7 CI_CCTRACKER_URL: 'https://snap-ci.com/willmendesneto/generator-reactor/branch/mas\
8 ter/cctray.xml',
9 INTERVAL: 1000
10 };

Now let’s change our src/index.js to use our file with the default settings of our application.

1 const five = require('johnny-five');


2 const CONFIG = require('./configuration');
3 const board = new five.Board();
4
5 board.on('ready', () => {
6
7 const ledSuccess = new five.Led(CONFIG.LED.SUCCESS);
8 const ledError = new five.Led(CONFIG.LED.ERROR);
9
10 setInterval(() => {
11 request(CONFIG.CI_CCTRACKER_URL, function(error, response, body)
12 {
13 ...
14 });
15 }, CONFIG.INTERVAL);
16 });
First project: Build Checker 30

Our code is starting to get a little more expressive, do you agree? But is there still something we can
improve on? Of course yes!
We are talking about build checker, but we have no abstraction for this operation. The idea is that our
final code is just an initialization of our app, with all relevant information within this abstraction.
Let’s then create our file with the LED information abstractions and the HTTP request by accessing
the settings.

1 const CONFIG = require('./configuration');


2 const request = require('request');
3 const five = require('johnny-five');
4
5 let intervalId = null;
6 function BuildChecker() {
7 this.ledSuccess = new five.Led(CONFIG.LED.SUCCESS);
8 this.ledError = new five.Led(CONFIG.LED.ERROR);
9 };
10
11 BuildChecker.prototype.stopPolling = () => {
12 clearInterval(intervalId);
13 };
14
15 BuildChecker.prototype.startPolling = () => {
16 const self = this;
17
18 intervalId = setInterval(() => {
19 request.get(CONFIG.CI_CCTRACKER_URL, function(error, response, body) {
20 if (error) {
21 console.log('Somethink is wrong with your CI =(');
22 return;
23 }
24
25 if(body.indexOf('Success') !== -1) {
26 console.log('Your CI is ok!');
27 self.ledSuccess.on();
28 self.ledError.off();
29 } else {
30 console.log('Somethink is wrong with your CI =(. Fix it!!!!');
31 self.ledSuccess.off();
32 self.ledError.on();
33 }
34
35 });
First project: Build Checker 31

36 }, CONFIG.INTERVAL);
37 };
38
39 module.exports = BuildChecker;

And our src/index.js file will only invoke and start our code so that the LEDs start blinking.

1 const BuildChecker = require('./build-checker');


2 const five = require('johnny-five');
3 const board = new five.Board();
4
5 board.on('ready', () => {
6 buildChecker = new BuildChecker();
7 buildChecker.startPolling();
8 });

Finishing this separation of concepts, we improve the readability, maintainability and several other
variants of our application. It is worth mentioning that this is a good practice and that, in the course
of the book, we will always be thinking about improvements to our final code.

Creating unit tests for build checker


This time something simple, but without good information about it is like adding drive tests on
Nodebots applications. Unit testing is not something new, but you do not find content on this topic
in Arduino, robots and hardware applications open easily, so we’ll cover a bit on this topic in this
book.
Unit testing is just one of several ways to test your software and have a reliability in the end product.
Based on the Test Pyramid³³, this is the way you should organise the testing of your application.
³³http://martinfowler.com/bliki/TestPyramid.html
First project: Build Checker 32

Pyramid of tests

We’ll talk only about unit tests, if you’d like to know more about all layers, read the post
“TestPyramid”³⁴ by Martin Fowler.
The idea of the unit test is to validate and certify that your code is doing what it intends to do, giving
feedback on the errors quickly before we deploy our project to production.
One aspect that nobody explains very well is about the tests in Nodebots, which has as the main
objective, in this case, to create the electrical simulations and with mocks and stubs thus simulating
the communication between components.
Let’s now create a folder for our unit tests with the name test.

1 $ mkdir test

The unit tests will use the test framework MochaJS³⁵, SinonJS³⁶ for spies, stubs and mocks and
ShouldJS³⁷ for assertions. Let’s then install these packages as a dependency of project development.

³⁴http://martinfowler.com/bliki/TestPyramid.html
³⁵https://mochajs.org
³⁶http://sinonjs.org
³⁷https://shouldjs.github.io
First project: Build Checker 33

1 $ npm install --save-dev mocha sinon should

A fundamental NodeJS package in this step is mock-Firmata³⁸, created by Rick Waldron to make
testing setup in Johnny-Five applications easier. The integration is very simple, you just need to
load and create your fake component of the board in the test, as we can see in our setup file of
test/spec-helper.js tests.

1 require('should');
2 const mockFirmata = require('mock-firmata');
3 const five = require('johnny-five');
4
5 const board = new five.Board({
6 io: new mockFirmata.Firmata(),
7 debug: false,
8 repl: false
9 });

Let’s then add a simple test to check the integration of our tests. First, we will create a file with some
MochaJS settings inside the test folder. This will be the initial content of our mocha.opts.

1 --reporter spec
2 --recursive
3 --require test/spec-helper.js
4 --slow 1000
5 --timeout 5000

A quick explanation of the configuration information used:


--reporter spec: A type of reporter* used to display messages of test information; --recursive:
flag to identify that the tests should run recursively inside the folder; --require test/spec-helper.js:
setup file to be loaded before running the unit tests; --low 1000: Maximum time in milliseconds of
tolerance between tests. If this time exceeds this time will be shown the total time of that test with a
differentiated colour so that we can make the necessary changes; --timeout 5000: Maximum time
in milliseconds of tolerance for the completion of each assertion. If this time exceeds this time our
tests will return with an error message;
We will create a file with the name test/index.js with a fairly simple assertion.

³⁸https://github.com/rwaldron/mock-firmata
First project: Build Checker 34

1 describe('Test validation', () => {


2 it('1 + 1 = 2', () => {
3 (1 + 1).should.be.equal(2);
4 });
5 });

Our tests use the describe and it scope methods. Describe is used to group scenarios, in our
case Test validation. It is the identification of one of the test points. Note also that we have the
should.be.equal method that will compare if the first value is equal to the second.

Configuring the test suite in the repository

Let’s go to our prompt/terminal and enter the following command.

1 $ ./node_modules/bin/mocha

We will then see this information at our prompt/terminal and our initial setup was a success!
Now, let’s create the scenarios for our tests. Let us then define the scenarios that we must cover in
our tests:

• Initial information when creating the BuildChecker instance;


• When we start polling and the server sends information from a successfully completed build;
• When we start polling and the server sends information from a finished build with failures;
• When we stop our polling and we will not do more requests of data for our server;

One way to validate when the build checker should flash the LED is to create a stub for the request
using the node-request to validate the response by expected types (success and error) and use some
spies for the LEDs.
First project: Build Checker 35

For this simulation, we will create some fixtures with the server responses model for success and
error. Let’s then create our folder with the data inside our folder containing our tests.

1 $ mkdir test/fixtures
2 $ touch test/fixtures/success.xml test/fixtures/error.xml

And i will add the information for each file.

1 <!-- test/fixtures/error.xml -->


2 <Projects>
3 <Project
4 name="Error-project"
5 activity="Sleeping"
6 lastBuildLabel="22"
7 lastBuildStatus="Failure"
8 lastBuildTime="2016-01-04T02:20:25Z"
9 webUrl="https://google.com"/>
10 </Projects>

1 <!-- test/fixtures/success.xml -->


2 <Projects>
3 <Project
4 name="Success-project"
5 activity="Sleeping"
6 lastBuildLabel="36"
7 lastBuildStatus="Success"
8 lastBuildTime="2016-03-22T21:09:02Z"
9 webUrl="https://google.com"/>
10 </Projects>

Let’s then create the scenario to validate our code. Some things we should keep in mind about our
unit testing framework is that its beforeEach method, which happens before every it method. We
will use as documentation of each step that must occur to reproduce the specific scenario.
Let’s then explain more about the contents of this file and why of each test. We create the tests of
the instance of our BuildChecker and its initial attributes.
First project: Build Checker 36

1 const BuildChecker = require('../src/build-checker');


2 const five = require('johnny-five');
3 const request = require('request');
4 const sinon = require('sinon');
5
6 describe('BuildChecker', () => {
7
8 beforeEach(() => {
9 buildChecker = new BuildChecker();
10 });
11
12 it('should have the led success port configured', () => {
13 (buildChecker.ledSuccess instanceof five.Led).should.be.equal(true);
14 });
15
16 it('should have the led error port configured', () => {
17 (buildChecker.ledError instanceof five.Led).should.be.equal(true);
18 });
19
20 });

Now we will validate when we stop our polling. Let’s now use the spy method of the sinon to check
if the code used the clearInterval method to end with the requests. For this, we will check if the
global.clearInterval was used once, by accessing the boolean calledOnce, which is an internal
counter added by the sinon.spy method for the tests.

1 ...
2 describe('#stopPolling', () => {
3 beforeEach(() => {
4 sinon.spy(global, 'clearInterval');
5 buildChecker.stopPolling();
6 });
7
8 it('should remove interval', () => {
9 global.clearInterval.calledOnce.should.be.true;
10 });
11 });
12 ...

And now the server scenarios responding successfully and failed. For this, we will assign our data
from the fixtures folder to variables.
First project: Build Checker 37

1 ...
2 const fs = require('fs');
3 const successResponseCI = fs.readFileSync(__dirname + '/fixtures/success.xml', 'utf8\
4 ');
5 const errorResponseCI = fs.readFileSync(__dirname + '/fixtures/error.xml', 'utf8');
6 ...

Note that in each of the success and failure cases we are using the sinon.useFakeTimers method,
which is a way to simulate events linked to timer objects in Javascript.
When we call the clock.tick the method with the information contained in the configuration file,
we simulate time and time changes at the time of testing, which helps us force the call to the polling
event, which uses setInterval.

1 ...
2 clock = sinon.useFakeTimers();
3 clock.tick(CONFIG.INTERVAL);
4 ...

We now use the syntax stub method for the node-request package. With this, we can change the
return when the request.get method is called. In this case, we can simulate the response of each
request, based on the information of our fixtures.

1 ...
2 sinon.stub(request, 'get').yields(null, null, successResponseCI);
3 ...

An important point in our tests is to remember to restore all the stub and fakeTimers information.
We will use the afterEach method that is always called after each it method, and we will add our
objects by calling the restore method added by sinon.

1 ...
2 afterEach(() => {
3 request.get.restore();
4 clock.restore();
5 });
6 ...

Based on our test scenarios, this is the test content of our build checker file:
First project: Build Checker 38

1 // test/build-checker.js
2
3 const BuildChecker = require('../src/build-checker');
4 const CONFIG = require('../src/configuration');
5 const five = require('johnny-five');
6 const request = require('request');
7 const sinon = require('sinon');
8 const fs = require('fs');
9 const successResponseCI = fs.readFileSync(__dirname + '/fixtures/success.xml', 'utf8\
10 ');
11 const errorResponseCI = fs.readFileSync(__dirname + '/fixtures/error.xml', 'utf8');
12 let clock = null;
13
14 describe('BuildChecker', () => {
15
16 beforeEach(() => {
17 buildChecker = new BuildChecker();
18 });
19
20 it('should have the led success port configured', () => {
21 (buildChecker.ledSuccess instanceof five.Led).should.be.equal(true);
22 });
23
24 it('should have the led error port configured', () => {
25 (buildChecker.ledError instanceof five.Led).should.be.equal(true);
26 });
27
28 describe('#stopPolling', () => {
29 beforeEach(() => {
30 sinon.spy(global, 'clearInterval');
31 buildChecker.stopPolling();
32 });
33
34 it('should remove interval', () => {
35 global.clearInterval.calledOnce.should.be.true;
36 });
37 });
38
39 describe('#startPolling', () => {
40 beforeEach(() => {
41 sinon.spy(global, 'setInterval');
42 buildChecker.startPolling();
43 });
First project: Build Checker 39

44
45 afterEach(() => {
46 global.setInterval.restore();
47 });
48
49 it('should creates polling', () => {
50 global.setInterval.calledOnce.should.be.true;
51 });
52
53 describe('When the CI server send success response', () => {
54 beforeEach(() => {
55 clock = sinon.useFakeTimers();
56 sinon.stub(request, 'get').yields(null, null, successResponseCI);
57 sinon.spy(buildChecker.ledSuccess, 'on');
58 sinon.spy(buildChecker.ledError, 'off');
59 buildChecker.startPolling();
60 clock.tick(CONFIG.INTERVAL);
61 });
62
63 afterEach(() => {
64 request.get.restore();
65 clock.restore();
66 });
67
68 it('should turn on the success led', () => {
69 buildChecker.ledSuccess.on.calledOnce.should.be.true;
70 });
71
72 it('should turn off the error led', () => {
73 buildChecker.ledError.off.calledOnce.should.be.true;
74 });
75
76 });
77
78 describe('When the CI server send error response', () => {
79 beforeEach(() => {
80 clock = sinon.useFakeTimers();
81 sinon.stub(request, 'get').yields(null, null, errorResponseCI);
82 sinon.spy(buildChecker.ledError, 'on');
83 sinon.spy(buildChecker.ledSuccess, 'off');
84 buildChecker.startPolling();
85 clock.tick(CONFIG.INTERVAL);
86 });
First project: Build Checker 40

87
88 afterEach(() => {
89 request.get.restore();
90 clock.restore();
91 });
92
93 it('should turn off the success led', () => {
94 buildChecker.ledSuccess.off.calledOnce.should.be.true;
95 });
96
97 it('should turn on the error led', () => {
98 buildChecker.ledError.on.calledOnce.should.be.true;
99 });
100
101 });
102
103 });
104
105 });

This is just one of several unit testing formats for your application. With this, we finish our first
project with unit tests based on our possible scenarios, but if you want to download or fork the
final code, access the build checker project repository in Github³⁹. Let’s go to our next project with
Nodebot and Johnny-five?
³⁹https://github.com/willmendesneto/build-checker
Second project: Fire alarm
Our second example project will be that of an intelligent fire alarm. Our fire alarm will check for
the temperature and, in the case of a fire, it will activate the audible alarm and send an SMS to the
registered cell phone.
A simple example, but that shows some interesting integration points, such as integration with API’s,
data reading of a temperature sensor and integration with the Piezo sound sensor.

Anatomy of a fire alarm


The project was based on a simple fire alarm home system. By default systems of this type do a
check from time to time and activate the alarm some different temperature pattern is encountered.

Materials needed
For this project we will use:

• 1 Protoboard: A protoboard is nothing more than a plate with holes and conductive connections
for mounting of experimental electrical circuits, without the need of welding;
• 1 Piezo alarm sensor: It will be used for sound feedback to the end user, in our case to the tenant
(s) of the property. This sensor is very simple and costs less than $ 2.00 can be found in any
electric store;
• 1 temperature sensor: Johnny-Five works with a wide range of temperature sensors. In this
case, we will use the LM35 temperature sensor. This sensor is very simple and costs less than
$ 1.50 can be found in any electric store;
Second project: Fire alarm 42

Piezo and Temperature Sensors

• For a complete list of supported sensors, go to Johnny’s Temperature Sensor Wiki⁴⁰ page and
check the list of sensor reference codes.

Some sensors require a specific voltage port for their correct operation (some sensors call VND). In
our case we will use a 5 volt port for the temperature sensor.
Other sensors may need an analog port. Analog ports are used for the sensor to send voltage data
to the Arduino so we can read and interpret your information.
For this project we will mount the sensors in the Arduino as follows:

• Piezo alarm sensor: Attach the black piezo wire to the GND port and the red wire to our
arduino’s number 3 port;
• LM35 temperature sensor: attach the grounding pin to the GND port, the voltage pin on the
5-volt port and the data output pin on the analog port “A0”;

The following image illustrates the assembly of the integrated components with the Arduino.
⁴⁰https://github.com/rwaldron/johnny-five/wiki/thermometer
Second project: Fire alarm 43

Integration of components used in Fire Alarm


Second project: Fire alarm 44

Controlling the Flame Sensor


With the LM35 sensor connected to the Arduino board, let’s now read the ambient temperature
information. For this we will use the Thermometer class of Johnny five. When we create a new
object Thermometer, we must pay attention to some parameters:

• controller: Name of the sensor used. You can refer to the complete list of sensors supported
by Johnny Five in the Thermometer class documentation⁴¹;
• pin: The pin information used for the analog connection on the Arduino. It is used in analog
sensors for reading the temperature information;
• toCelsius: An optional method that we can rewrite to handle the analog data and transform
it into the temperature format of your preference. In our case, we will use the Celsius format;

Based on this information, the main file of our fire alarm will be:

1 const five = require('johnny-five');


2 const board = new five.Board();
3
4 function FireAlarm() {
5 return new five.Thermometer({
6 controller: 'LM35',
7 pin: 'A0'
8 });
9 };
10
11 board.on('ready', function() {
12 const temperatureSensor = new FireAlarm();
13
14 setInterval(function() {
15 console.log('celsius: %d', temperatureSensor.celsius);
16 });
17 });

Let’s then validate the functionality of our code with the Arduino platform. Inside the folder of our
project, we will enter in our command line/prompt/terminal the following command:

1 $ node src/index.js

And this will be the result of our code.


⁴¹https://github.com/rwaldron/johnny-five/wiki/thermometer
Second project: Fire alarm 45

Reading sensor temperature information using javascript

The code is quite simple, as you can see. In the next topic, we will think a little more about our
architecture and how to evolve this code for something easier to maintain.

Evolving our initial code


Our initial code is functional, but evolving this code to the next steps is somewhat complex. To
facilitate the next steps of our project, we will make some adjustments in our initial code.
You may notice that we have several configurations, such as the controller name and pin of the
sensor. To improve the handling of these settings that are constant, that is, they do not change
throughout the life of our application, we will add them in a file with our specific settings.
This will be added to our src/configuration.js file.

1 // src/configuration.js
2 module.exports = {
3 FIRE_ALARM: {
4 // https://github.com/rwaldron/johnny-five/wiki/thermometer
5 CONTROLLER: 'LM35',
6 PIN: 'A0'
7 },
8 INTERVAL: 1000
9 };

This is the contents of our src/fire-alarm.js file. Notice that we are now invoking the external
configuration code and adding the values in the CONFIG variable. This step is interesting because we
unlink the basic settings of our class project, which now has the responsibility of dealing with the
sensors of the fire alarm project.
Second project: Fire alarm 46

1 // src/fire-alarm.js
2 const CONFIG = require('./configuration');
3 const five = require('johnny-five');
4 let intervalId = null;
5
6 function FireAlarm() {
7 this.temperatureSensor = new five.Thermometer({
8 controller: CONFIG.FIRE_ALARM.CONTROLLER,
9 pin: CONFIG.FIRE_ALARM.PIN
10 });
11 };
12
13 FireAlarm.prototype.stopPolling = () => {
14 clearInterval(intervalId);
15 };
16
17 FireAlarm.prototype.startPolling = () => {
18 const self = this;
19 intervalId = setInterval(function() {
20 console.log('celsius: %d', self.temperatureSensor.celsius);
21 }, CONFIG.INTERVAL);
22 };
23
24 module.exports = FireAlarm;

And our main src/index.js file will have a simpler content, having the responsibility of initiating
the project and polling the FireAlarm class instance.

1 // src/index.js
2 const FireAlarm = require('./fire-alarm');
3 const five = require('johnny-five');
4 const board = new five.Board();
5
6 board.on('ready', () => {
7 fireAlarm = new FireAlarm();
8 fireAlarm.startPolling();
9 });

When we run our code from the command, we will see the same result at our command line/prompt.

1 $ npm start
Second project: Fire alarm 47

With these changes, we have a code of easy maintenance and much more readability to be used in
our application. Of course, this is one of the several approaches that can be used in your project, but
the focus of this topic is to pass on the idea of always thinking about how to improve our project.

Integrating with Piezo for audible warning


Twilio is a service that allows developers to embed voice, VoIP and SMS messages into applications
from a RESTful API that provides the voice and SMS capabilities for applications.
The alarm sensor we are going to use is Piezo because it is a simple component to handle and quite
cheap, having its value around $ 1.00.
The integration of Piezo into our project is somewhat trivial since Johnny Five already has the
five.Piezo class.

1 ...
2 this.piezo = new five.Piezo(3);
3 ...

This class accepts the pin number that is bound to the sensor and when activating we have the play
method, which accepts an object with the information:

• time: interval between each note;


• song: array that accepts arrays of 2 positions with the information of musical note and the time;

You can see an example of the play method being used in the code below.

1 self.piezo.play({
2 song: [
3 ['G5', 1/4]
4 ],
5 tempo: 200
6 });

In our project, we are going to make the integration divided into 2 parts. First, we will add the piezo
instance in the constructor of our class so that the piezo is accessible in the other methods.
Second project: Fire alarm 48

1 ...
2 function FireAlarm() {
3 this.piezo = new five.Piezo(3);
4 ...
5 }
6 ...

With our instance added and accessible, let’s use it in our startPolling method. Let’s add
another validation by accessing the piezo.isPlaying boolean that contains the piezo initialization
information in our project and, if the temperature is over the threshold and the piezo is accessible,
we will trigger our audible alarm. With this, the method will remain as the following code.

1 ...
2 FireAlarm.prototype.startPolling = () => {
3 self = this;
4 intervalId = setInterval(function() {
5 if (self.temperatureSensor.celsius >= CONFIG.FIRE_ALARM.LIMIT && !self.piezo.isP\
6 laying) {
7 self.piezo.play({
8 song: [
9 ['G5', 1/4],
10 [null, 7/4]
11 ],
12 tempo: 200
13 });
14 console.log(`Up to the limit: ${self.temperatureSensor.celsius}`);
15 } else {
16 console.log(`That's ok: ${self.temperatureSensor.celsius}`);
17 }
18 }, CONFIG.INTERVAL);
19 };
20 ...

Our final FireAlarm code will contain the following content:


Second project: Fire alarm 49

1 const CONFIG = require('./configuration');


2 const request = require('request');
3 const five = require('johnny-five');
4 let intervalId = null;
5
6 function FireAlarm() {
7 this.piezo = new five.Piezo(3);
8 this.temperatureSensor = new five.Thermometer({
9 pin: CONFIG.FIRE_ALARM.PIN
10 });
11 };
12
13
14 FireAlarm.prototype.stopPolling = () => {
15 clearInterval(intervalId);
16 };
17
18 FireAlarm.prototype.startPolling = () => {
19 self = this;
20 intervalId = setInterval(function() {
21 if (self.temperatureSensor.celsius >= CONFIG.FIRE_ALARM.LIMIT && !self.piezo.isP\
22 laying) {
23
24 self.piezo.play({
25 song: [
26 ['G5', 1/4],
27 [null, 7/4]
28 ],
29 tempo: 200
30 });
31
32 console.log(`Up to the limit: ${self.temperatureSensor.celsius}`);
33 } else {
34 console.log(`That's ok: ${self.temperatureSensor.celsius}`);
35 }
36
37 }, CONFIG.INTERVAL);
38 };
39
40 module.exports = FireAlarm;

With this, we have the first feedback for the users of our application. The next step will be to add
the SMS sending functionality using the Twilio API.
Second project: Fire alarm 50

Sending SMS to your phone using Twilio


Twilio is a service that allows developers to incorporate voice, VoIP and SMS messaging into
applications from a RESTful API that provides voice and SMS capabilities to applications. Client
libraries are available in multiple languages ​​and, of course, have a client for NodeJS called
twilio-node⁴².
Integrating SMS into our application will be very simple. We go to the site to Twilio and we will
enable the SMS service.

SMS setup page in Twilio

It has some paid numbers if you really want to use some product. In our case, we will use the number
generated by the service itself and create a trial account on the platform.
⁴²https://twilio.github.io/twilio-node
Second project: Fire alarm 51

Adding a phone number on Twilio

Now, with Twilio set up, let’s start integrating with our fire alarm. First let’s add the phone
information, SSID of your account, and Twilio’s authentication token. Let’s add this information
in our src/configuration.js.

1 module.exports = {
2 FIRE_ALARM: {
3 LIMIT: 30,
4 PIN: 'A0',
5 PHONE_NUMBER: ''
6 },
7 TWILIO: {
8 PHONE_NUMBER: '',
9 ACCOUNT_SSID: '',
10 AUTH_TOKEN: ''
11 },
12 INTERVAL: 1000
13 };

And let’s continue with the integration of Twilio into our code by accessing and reading the
information added in the src/configuration.js file with the Twilio node package.

1 // fire-alarm.js
2
3 const CONFIG = require('./configuration');
4 const request = require('request');
5 const five = require('johnny-five');
6 const twilio = require('twilio');
7 let intervalId = null;
8
9 const client = new twilio.RestClient(CONFIG.TWILIO.ACCOUNT_SSID, CONFIG.TWILIO.AUTH_\
10 TOKEN);
11
Second project: Fire alarm 52

12 function FireAlarm() {
13 this.piezo = new five.Piezo(3);
14 this.temperatureSensor = new five.Thermometer({
15 pin: CONFIG.FIRE_ALARM.PIN
16 });
17 };
18
19 FireAlarm.prototype.stopPolling = () => {
20 clearInterval(intervalId);
21 };
22
23 FireAlarm.prototype.startPolling = () => {
24 self = this;
25 intervalId = setInterval(function() {
26 if (self.temperatureSensor.celsius >= CONFIG.FIRE_ALARM.LIMIT && !self.piezo.isP\
27 laying) {
28
29 self.piezo.play({
30 song: [
31 ['G5', 1/4],
32 [null, 7/4]
33 ],
34 tempo: 200
35 });
36
37 client.messages.create({
38 body: 'Something is wrong with your fire alarm. Please, call the local fire \
39 brigade.',
40 to: CONFIG.FIRE_ALARM.PHONE_NUMBER,
41 from: CONFIG.TWILIO.PHONE_NUMBER
42 });
43
44 console.log(`Up to the limit: ${self.temperatureSensor.celsius}`);
45 } else {
46 console.log(`That's ok: ${self.temperatureSensor.celsius}`);
47 }
48 }, CONFIG.INTERVAL);
49 };
50
51 module.exports = FireAlarm;

Let’s now run our code by typing the command npm start and this will be the information printed
at our prompt/command line.
Second project: Fire alarm 53

When we execute our code and the temperature exceeds the configured limit, in addition to the
Piezo sensor that will trigger the alarm our customer of Twilio will be activated and will send us
an SMS using the previously added configurations. Then you will receive the following message on
your cell phone.

SMS sent by Twilio API

With this we have seen the complete integration of our Fire Alarm. Of course, this is the first step,
you can evolve the code and add new features. As we already know: the sky is the limit!
But what about the unit tests? We know it’s working, but we have to make sure the code has an
acceptable level of quality even to evolve our project and add new features. Let’s now create our
unit tests.

Creating unit tests for Fire Alarm


As commented on in the “Building unit tests for build checker” content, unit testing is just one of
several ways to test your software and have reliability in the final product with some automatic
validations before deploying our project to production.
Let’s now create a folder for our unit tests with the name test. By default, we will instantiate the
[MochaJS]test framework (https://mochajs.org), SinonJS⁴³ for spies, stubs and mocks and ShouldJS⁴⁴
for assertions. Let’s then install these packages as a dependency of project development.

1 $ mkdir test
2 $ npm install --save-dev mocha sinon should

For our tests, we will reuse the contents of test/spec-helper.js and test/mocha.opts that we
created for our build checker project. It uses the mock-Firmata package⁴⁵ to setup the tests in our
Johnny-Five application.

⁴³http://sinonjs.org
⁴⁴https://shouldjs.github.io
⁴⁵https://github.com/rwaldron/mock-firmata
Second project: Fire alarm 54

1 //test/spec-helper.js
2 require('should');
3 const mockFirmata = require('mock-firmata');
4 const five = require('johnny-five');
5
6 const board = new five.Board({
7 io: new mockFirmata.Firmata(),
8 debug: false,
9 repl: false
10 });

And this will be the initial content of our mocha.opts.

1 --reporter spec
2 --recursive
3 --require test/spec-helper.js
4 --slow 1000
5 --timeout 5000

A quick explanation of the configuration information used:


--reporter spec: The type of reporter used to display the test information messages; --recursive:
flag to identify that the tests should run recursively within the folder; --require test/spec-helper.js:
setup file to be loaded before running the unit tests; --low 1000: Maximum time in milliseconds of
tolerance between tests. If this time exceeds this time will be shown the total time of that test with a
differentiated colour so that we can make the necessary changes; --timeout 5000: Maximum time
in milliseconds of tolerance for the completion of each assertion. If this time exceeds this time our
tests will return with an error message;
Now, let’s create the scenarios for our tests. Let us then define the scenarios that we must cover in
our tests:

• Initial application settings;


• Initial information when creating the instance of FireAlarm;
• When we start the polling and the sensor signals that the temperature is within the acceptable
limit;
• When we start the polling and the sensor signals that the temperature is above the acceptable
limit;
• When the piezo sensor is activated;
• When the Twilio SMS API is triggered;

One way to validate when the fire alarm should trigger the audible alarm is to create a stub for
trigger and use some spies for the Twilio API.
Second project: Fire alarm 55

As a first step, we will create the tests of our configuration file, which we will divide between
the temperature sensor information and the Twilio API. We will make the request for our
src/configuration.js and we will check the information of the pin to be connected to the sensor,
the value of the interval to be used to check the sensor information and the acceptable temperature
limit of the environment in which FireAlarm will be working and your phone will be set to receive
SMS.

1 const CONFIG = require('../src/configuration');


2
3 describe('Configuration', () => {
4
5 describe('Temperature information', () => {
6
7 it('should have the sensor port configured', () => {
8 CONFIG.FIRE_ALARM.should.have.property('PIN').which.is.a.String()
9 });
10
11 it('should have the sensor alarm limit configured', () => {
12 CONFIG.FIRE_ALARM.should.have.property('LIMIT').which.is.a.Number()
13 });
14
15 it('should have the user phone configured', () => {
16 CONFIG.FIRE_ALARM.should.have.property('PHONE_NUMBER').which.is.a.String()
17 });
18 });
19 });

Now we evaluate the information that will be passed to Twilio, which in our case will be the SSID
key, authentication token and the telephone number provided by Twilio.

1 ...
2 describe('SMS information', () => {
3
4 it('should have account ssid configured', () => {
5 CONFIG.TWILIO.should.have.property('ACCOUNT_SSID').which.is.a.String()
6 });
7
8 it('should have auth token configured', () => {
9 CONFIG.TWILIO.should.have.property('AUTH_TOKEN').which.is.a.String()
10 });
11
12 it('should have the user phone configured', () => {
13 CONFIG.TWILIO.should.have.property('PHONE_NUMBER').which.is.a.String()
Second project: Fire alarm 56

14 });
15 });
16 ...

Our src/configuration.js test file will look like this.

1 const CONFIG = require('../src/configuration');


2
3 describe('Configuration', () => {
4
5 describe('Temperature information', () => {
6
7 it('should have the sensor port configured', () => {
8 CONFIG.FIRE_ALARM.should.have.property('PIN').which.is.a.String()
9 });
10
11 it('should have the sensor alarm limit configured', () => {
12 CONFIG.FIRE_ALARM.should.have.property('LIMIT').which.is.a.Number()
13 });
14
15 it('should have the user phone configured', () => {
16 CONFIG.FIRE_ALARM.should.have.property('PHONE_NUMBER').which.is.a.String()
17 });
18 });
19
20 describe('SMS information', () => {
21
22 it('should have account ssid configured', () => {
23 CONFIG.TWILIO.should.have.property('ACCOUNT_SSID').which.is.a.String()
24 });
25
26 it('should have auth token configured', () => {
27 CONFIG.TWILIO.should.have.property('AUTH_TOKEN').which.is.a.String()
28 });
29
30 it('should have the user phone configured', () => {
31 CONFIG.TWILIO.should.have.property('PHONE_NUMBER').which.is.a.String()
32 });
33 });
34
35 it('should have the interval polling information', () => {
36 CONFIG.should.have.property('INTERVAL').which.is.a.Number()
37 });
Second project: Fire alarm 57

38
39 });

Let’s then create the scenario to validate the code of our FireAlarm. One of the things we should
keep in mind about our unit testing framework is that its beforeEach method, which happens before
each it method, is going to be used as documentation for playback of each specific scenario.
Let’s then explain more about the contents of this file and why of each test. We create the tests of
the instance of our FireAlarm and its initial attributes.

1 const proxyquire = require('proxyquire');


2 const CONFIG = require('../src/configuration');
3 const five = require('johnny-five');
4 const request = require('request');
5 const sinon = require('sinon');
6
7 describe('FireAlarm', () => {
8
9 beforeEach(() => {
10 this.sandbox = sinon.sandbox.create();
11 this.createMessagesSpy = this.sandbox.spy();
12 const restClientMessage = {
13 messages: {
14 create: this.createMessagesSpy
15 }
16 };
17
18 twilioMock = {
19 RestClient: function() {
20 return restClientMessage
21 }
22 };
23
24 const FireAlarm = proxyquire('../src/fire-alarm', {
25 twilio: twilioMock
26 });
27 fireAlarm = new FireAlarm();
28 });
29
30 afterEach(() => {
31 this.sandbox.restore();
32 });
33
Second project: Fire alarm 58

34 it('should have the termometer sensor configured', () => {


35 (fireAlarm.temperatureSensor instanceof five.Thermometer).should.be.equal(true);
36 });
37
38 });

Let’s validate when we stop our polling. Let’s now use the spy method of the sinon to check if
the code used the clearInterval method to end with the requests. For this, we will check if the
global.clearInterval was used once, by accessing the boolean calledOnce, which is an internal
counter added by the sinon.spy method for the tests.

1 ...
2 describe('#stopPolling', () => {
3 beforeEach(() => {
4 this.sandbox.spy(global, 'clearInterval');
5 fireAlarm.stopPolling();
6 });
7
8 it('should remove interval', () => {
9 global.clearInterval.calledOnce.should.be.true;
10 });
11 });
12 ...

And now the scenarios that happen when the sensor is started. For this, we will assign some spies
for the setInterval functions and for the piezo sensor. Our first validation will be to certify that the
range is being started when we call the fireAlarm.startPolling() method.

1 ...
2 describe('#startPolling', () => {
3 beforeEach(() => {
4 this.piezoPlaySpy = this.sandbox.spy();
5
6 const piezoStub = {
7 isPlaying: false,
8 play: this.piezoPlaySpy
9 };
10 this.sandbox.stub(fireAlarm, 'piezo', piezoStub);
11
12 this.sandbox.spy(global, 'setInterval');
13 fireAlarm.startPolling();
14 });
Second project: Fire alarm 59

15
16 afterEach(() => {
17 global.setInterval.restore();
18 });
19
20 it('should creates polling', () => {
21 global.setInterval.calledOnce.should.be.true;
22 });
23 });
24 ...

To validate the temperature case above the limit we will use the clock.tick method with the
information contained in the configuration file, simulating the time and time changes at the time of
the test, which helps us to force the event Polling, which uses setInterval.
In this scenario we use the stub to simulate the return of the sensor with a value above the acceptable
and we make sure that the Piezo sensor and the Apollo of Twilio were triggered.

1 ...
2 describe('When the temperature is up to the limit', () => {
3
4 beforeEach(() => {
5 clock = this.sandbox.useFakeTimers();
6
7 this.sandbox.stub(fireAlarm, 'temperatureSensor', {
8 celsius: CONFIG.FIRE_ALARM.LIMIT + 1
9 });
10 fireAlarm.startPolling();
11 clock.tick(CONFIG.INTERVAL);
12 });
13
14 afterEach(() => {
15 clock.restore();
16 });
17
18 it('should trigger piezo sensor alarm', () => {
19 this.piezoPlaySpy.calledOnce.should.be.true;
20 });
21
22 it('should send the SMS to user', () => {
23 this.createMessagesSpy.calledOnce.should.be.true;
24 });
25
Second project: Fire alarm 60

26 });
27 ...

Now we will validate the ambient temperature scenario in acceptable levels. The checks use the
same approaches as the previous one, but in this case, we make sure that the piezo sensor and the
Twilio API were not triggered.

1 ...
2 describe('When the temperature is NOT up to the limit', () => {
3
4 beforeEach(() => {
5 clock = this.sandbox.useFakeTimers();
6
7 this.sandbox.stub(fireAlarm, 'temperatureSensor', {
8 celsius: CONFIG.FIRE_ALARM.LIMIT - 1
9 });
10
11 fireAlarm.startPolling();
12 clock.tick(CONFIG.INTERVAL);
13 });
14
15 afterEach(() => {
16 clock.restore();
17 });
18
19 it('should NOT trigger piezo sensor alarm', () => {
20 this.piezoPlaySpy.calledOnce.should.be.false;
21 });
22
23 it('should NOT send the SMS to user', () => {
24 this.createMessagesSpy.calledOnce.should.be.false;
25 });
26
27 });
28 ...

Based on our test scenarios, this is the test content of our fire alarm file:
Second project: Fire alarm 61

1 const proxyquire = require('proxyquire');


2 const CONFIG = require('../src/configuration');
3 const five = require('johnny-five');
4 const request = require('request');
5 const sinon = require('sinon');
6
7 describe('FireAlarm', () => {
8
9 beforeEach(() => {
10 this.sandbox = sinon.sandbox.create();
11 this.createMessagesSpy = this.sandbox.spy();
12 const restClientMessage = {
13 messages: {
14 create: this.createMessagesSpy
15 }
16 };
17
18 twilioMock = {
19 RestClient: function() {
20 return restClientMessage
21 }
22 };
23
24 const FireAlarm = proxyquire('../src/fire-alarm', {
25 twilio: twilioMock
26 });
27 fireAlarm = new FireAlarm();
28 });
29
30 afterEach(() => {
31 this.sandbox.restore();
32 });
33
34 it('should have the termometer sensor configured', () => {
35 (fireAlarm.temperatureSensor instanceof five.Thermometer).should.be.equal(true);
36 });
37
38 describe('#stopPolling', () => {
39 beforeEach(() => {
40 this.sandbox.spy(global, 'clearInterval');
41 fireAlarm.stopPolling();
42 });
43
Second project: Fire alarm 62

44 it('should remove interval', () => {


45 global.clearInterval.calledOnce.should.be.true;
46 });
47 });
48
49 describe('#startPolling', () => {
50 beforeEach(() => {
51 this.piezoPlaySpy = this.sandbox.spy();
52
53 const piezoStub = {
54 isPlaying: false,
55 play: this.piezoPlaySpy
56 };
57 this.sandbox.stub(fireAlarm, 'piezo', piezoStub);
58
59 this.sandbox.spy(global, 'setInterval');
60 fireAlarm.startPolling();
61 });
62
63 afterEach(() => {
64 global.setInterval.restore();
65 });
66
67 it('should creates polling', () => {
68 global.setInterval.calledOnce.should.be.true;
69 });
70
71 describe('When the temperature is up to the limit', () => {
72
73 beforeEach(() => {
74 clock = this.sandbox.useFakeTimers();
75
76 this.sandbox.stub(fireAlarm, 'temperatureSensor', {
77 celsius: CONFIG.FIRE_ALARM.LIMIT + 1
78 });
79 fireAlarm.startPolling();
80 clock.tick(CONFIG.INTERVAL);
81 });
82
83 afterEach(() => {
84 clock.restore();
85 });
86
Second project: Fire alarm 63

87 it('should trigger piezo sensor alarm', () => {


88 this.piezoPlaySpy.calledOnce.should.be.true;
89 });
90
91 it('should send the SMS to user', () => {
92 this.createMessagesSpy.calledOnce.should.be.true;
93 });
94
95 });
96
97 describe('When the temperature is NOT up to the limit', () => {
98
99 beforeEach(() => {
100 clock = this.sandbox.useFakeTimers();
101
102 this.sandbox.stub(fireAlarm, 'temperatureSensor', {
103 celsius: CONFIG.FIRE_ALARM.LIMIT - 1
104 });
105
106 fireAlarm.startPolling();
107 clock.tick(CONFIG.INTERVAL);
108 });
109
110 afterEach(() => {
111 clock.restore();
112 });
113
114 it('should NOT trigger piezo sensor alarm', () => {
115 this.piezoPlaySpy.calledOnce.should.be.false;
116 });
117
118 it('should NOT send the SMS to user', () => {
119 this.createMessagesSpy.calledOnce.should.be.false;
120 });
121 });
122 });
123 });

With that we finished our first project with unit tests, covering all our possible scenarios. In the next
chapter, we’ll look at other services that make life easier for us by sending the code to production,
such as seamless integration servers, code coverage, and complexity in an automated way.
Supporting Your Code on Multiple
Operating Systems
In this stage of the book, we will then validate and verify the coverage of tests in our project on
different operating systems, as well as enable different web services for workflow improvements,
such as continuous integration tools and code coverage.
This step is very important because these tools help us in the security process of our code, checking
different criteria of acceptance of our application in an automated way.

Adding Continuous Integration Servers to Your Project


Like all quality projects, our Nodebots project will be concerned with some other aspects, such as
automating the test suite, build and other tasks relevant to our project.
For this, we will rely on the help of a continuous integration server. There are several in the market,
being free or paid, and in this stage of the book, we will know a little more about the operation and
configuration of two of them: Travis-CI and Appveyor.

Travis-CI: Checking Your Code on Linux and OSX


Knowing that currently, the most used operating systems are Unix/Linux, Windows and OSX we
will create checks for each of them and for this the Travis-CI comes into play.
It is one of the most famous services of continuous integration⁴⁶ and assists in the process of
integrating the new features or bug fixes of the code of the current project in several Environments,
and can even deploy for production if all the validation steps are correct.
Let’s go to the official project site travis-ci⁴⁷ and enable access using our Github account. Click on
the “Sign up” button and enable access to your repositories.
⁴⁶http://blog.caelum.com.br/integracao-continua/
⁴⁷https://travis-ci.org/
Supporting Your Code on Multiple Operating Systems 65

Travis-CI Service Site

After this step, you will be redirected to a new page with all your repositories. To add a new one
just click on the “+” icon next to the text “My Repositories”.

Page of a repository configured in Travis-CI

Now, you will be redirected to a new page with all your repositories. To add a new one just click on
the “+” icon next to the text “My Repositories”.
This next step is very simple since the page has a tutorial showing each of the steps to enable the
integration of Travis-CI with its repository in Github, as we can see in the image below.
Supporting Your Code on Multiple Operating Systems 66

Synchronizing repositories with the service

On the same page, all your repositories will be listed so you can choose and enable Travis-CI
integration with your project. To enable it, just click the grey button with an “X” and when it changes
colour to green it means that everything went as expected and its repository is synchronised with
Travis-CI.
Travis-CI is fully configurable and you can add information from a wide range of commands, from
commands to be invoked before, during or after the build, and even configure the types of operating
systems that the tasks should take place.
These settings will be in the .travis.yml file that will be in the root folder of our project. Let’s
explain a bit more about configuring these tasks in Travis-CI.

Activating travis webhook

First, in the .travis.yml file, we will add the os field, with the appropriate information of the
operating systems used for our tests.

1 ...
2 os:
3 - linux
4 - osx
5 ...

We will also add the "node_js" field, which will be our information about the NodeJS versions that
Supporting Your Code on Multiple Operating Systems 67

the tasks should be used in our tasks. In our case, we will only add one version, but we could add
several others based on our support needs, for example.

1 ...
2 node_js:
3 - '12.16.2'
4 ...

Our continuous integration server is nothing more than a container with a complete operating
system. So we can also configure environment variables in it. In this case, we will add the variable
NO_SERIALPORT_INSTALL, specifying that we should not install the ‘serialport’ package in this case
because it is a test that uses a mock of a physical board.
NOTE: The idea of this book is to focus on the concepts directly related to Nodebots and integrations
with the javascript repository created, so I will not explain the concept of containers. If you want
to know more about this concept used by Travis-CI, visit the official Docker project website⁴⁸.

1 ...
2 env:
3 - NO_SERIALPORT_INSTALL=1
4 ...

We can also define the set of tasks that will be used before and after our Travis script. In this case,
we will use before for the commands that must occur before our main script and after for the
commands that must occur after the Travis commands, as you can see in the following code snippet:

1 ...
2 before_script:
3 - 'npm install'
4
5
6 after_script:
7 - 'make test'
8 ...

In this case, we are installing our dependencies and running our tests. All this in a very simple and
well-defined way. The contents of our .travis.yml file with all the changes will be as follows:

⁴⁸https://www.docker.com
Supporting Your Code on Multiple Operating Systems 68

1 language: node_js
2 os:
3 - linux
4 - osx
5 node_js:
6 - '12.16.2'
7 before_script:
8 - 'npm install'
9
10
11 after_script:
12 - 'make test'
13 env:
14 - NO_SERIALPORT_INSTALL=1

We can see that the Travis-CI build is a bit different now since we are running the same setup on
Linux and OSX operating systems, identified by the icons of each operating system.

List of used operating systems

With the integration tested, let’s then put the Travis-ci badge in our README.md file in the repository.
With this, you will see an image with the status of the build.

1 [![Build Status](https://travis-ci.org/willmendesneto/build-checker.png?branch=maste\
2 r)](https://travis-ci.org/willmendesneto/build-checker)

With this, we have finished our integration with Travis-CI continuous integration server and we
have our entire suite of tests running on Linux and OSX systems. In this next step we will configure
the same tasks, but to be verified by the Windows operating system, using another continuous
integration server called Appveyor.

Appveyor: Checking Your Code on Windows


Many projects are developed on Unix-based operating systems by default and adding support for
Windows was considered a big challenge for some, since building a Windows test environment was
not trivial, requiring the purchase of software licenses.
Supporting Your Code on Multiple Operating Systems 69

The continuous integration service Appveyor⁴⁹ is one of the solutions used for testing projects hosted
on GitHub in Windows environments, facilitating this process and ensuring that our code is cross-
Platform⁵⁰, running on major operating systems.
Adding Appveyor support to our project is a fairly simple task. We will then visit the official website
of the project and create a login with our information.

Appveyor Site

On the login page, we have some options listed with support for some of the major code repositories
on the internet. In this option we will use Github, to facilitate the next steps, but it is worth
remembering that you can use any of the options supported for login.
⁴⁹https://www.appveyor.com/
⁵⁰https://en.wikipedia.org/wiki/Cross-platform
Supporting Your Code on Multiple Operating Systems 70

Logging in to Appveyor

With your user created and your access working, the main page after login is a page listing all of
your repositories based on a category located on the left side of the site. In our case, we will choose
the build-checker project and click on the ‘Add’ button to add the support to our project.
We will then see the page of our project with the information specific to it, currently.

Now that we have our project configured we will create our appveyor.yml file, where our test settings
will be. This file is very similar to Travis-CI in some ways.
The contents of our appveyor.yml file with all the changes will be as follows:
We will add the version of NodeJS used in the environment field of our configuration file.
Supporting Your Code on Multiple Operating Systems 71

1 ...
2 environment:
3 matrix:
4 - nodejs_version: "12"
5 ...

The platform field will be used to describe the platforms used. Note that in this case instead of having
the differentiation between operating systems, we have between operating system platforms (x86
and 64x).
This is also interesting if there is a need to have a notion of the difference in performance,
performance and other aspects of the same application on different platforms.

1 ...
2 platform:
3 - x86
4 - x64
5 ...

The install field will list our initial commands. Note that ps is a command to install NodeJS with the
one specified in the file.
After this step we cleared the cache using the npm cache clean command for security measure in
order to avoid possible false positives in our tests and, after completing this command, we will then
install our dependencies using the npm install command.

1 ...
2 install:
3 - ps: Install-Product node $env:nodejs_version
4 - npm cache clean --force
5 - npm install
6 ...

The test_script field will have the list of our commands to execute at the time of running our tests.
We are directly accessing the node_modules folder and invoking the tests from them with the node_-
modules/.bin/Istanbul cover node_modules/mocha/bin/_mocha -- -R dot command, because we
use the make test command in our npm test.

1 ...
2 test_script:
3 Run the test
4 - cmd: node_modules/.bin/istanbul cover node_modules/mocha/bin/_mocha -- -R dot

As our case does not require the creation of a build, we will add the information in our file with
the off value and we will configure our build to be finalised as soon as possible by adding the
fast_finish field with the value true.
Supporting Your Code on Multiple Operating Systems 72

1 build: off
2 matrix:
3 fast_finish: true
4 ...

The final content of our appveyor.yml file with all changes will be as follows:

1 Fix line endings on Windows


2 init:
3 - git config --global core.autocrlf true
4 environment:
5 matrix:
6 - nodejs_version: "12"
7 platform:
8 - x86
9 - x64
10 install:
11 - ps: Install-Product node $env:nodejs_version
12 - npm cache clean --force
13 - npm install
14 test_script:
15 Run the test
16 - cmd: node_modules/.bin/istanbul cover node_modules/mocha/bin/_mocha -- -R dot
17 build: off
18 matrix:
19 fast_finish: true

Notice that in this case, we have the list of our differentiated build by platforms on the listing page.

List of platforms used by the service


Supporting Your Code on Multiple Operating Systems 73

With the new integration tested, we will then update the README.md file from the repository with
the Appveyor badge. With this, you will see an image with the status of the build, as well as what
we have inserted previously.

1 [![Build Windows Status](https://ci.appveyor.com/api/projects/status/github/<nome-do\


2 -seu-usuario-ou-organização>/<nome-do-seu-repositório>?svg=true)](https://ci.appveyo\
3 r.com/project/<nome-do-seu-usuario-ou-organização>/<nome-do-seu-repositório>/branch/\
4 master)

Notice that we have two tags in this code snippet. Replace this information as follows:

• <your-user-or-organisation-name>: name of your user or organisation;


• <your-repository-name>: name of your repository;

For example, based on the example repository, our badge will have the following content.

1 [![Build Windows Status](https://ci.appveyor.com/api/projects/status/github/willmend\


2 esneto/build-checker?svg=true)](https://ci.appveyor.com/project/willmendesneto/build\
3 -checker/branch/master)

As you might realise adding support for multiple operating systems and platforms is quite a simple
task with Appveyor. The next steps in the book will be more focused on improving the automation
of checking our code coverage.

Code coverage for your code


Once our repository is communicated with continuous integration services, we will now add new
tools. This time the focus is on the coverage of our code, checking if everything is being properly
validated in an automated way even before we continue with the other development stages of our
Nodebots.

Checking the code coverage of our project: Getting to know


Istanbul
Istanbul⁵¹ is a NodeJS package for verifying code coverage in our repository using various parameters
such as code-line coverage, functions, declarations, and reverse engineering⁵².
Let’s then add this package to our project using the following command.

⁵¹http://gotwarlost.github.io/istanbul
⁵²Https://en.wikipedia.org/wiki/Reverseengineering
Supporting Your Code on Multiple Operating Systems 74

1 $ npm install --save-dev istanbul

To verify that it is integrated into our repository, simply type in our prompt/command line.

1 $ ./node_modules/.bin/istanbul help
Supporting Your Code on Multiple Operating Systems 75

Output from command istanbul help

As we previously integrated MochaJS⁵³ into our repository when we created the project tests, we
can simply type the following command at our command prompt.

⁵³https://mochajs.org/
Supporting Your Code on Multiple Operating Systems 76

1 $ ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha

The return will be the same as the image below. You may notice that we now have some new
information in the footer of test messages, such as percentages of rows, functions, branches, and
declarations of methods, classes, or objects.
Supporting Your Code on Multiple Operating Systems 77

Output of the mocha command

Notice that we now have a new folder called coverage with some files and all this information listed
in our command line. We will use them in the next steps for integration with the Coveralls service.
Supporting Your Code on Multiple Operating Systems 78

Integrating Continuous Integration Server with Coveralls


With the code coverage information collected, we will then integrate a new service called coveralls⁵⁴.
It will be used to integrate code coverage data and make it visible by adding a badge in our README.md.

Coveralls service website

The login is very simple and you will have to enable integration with your Github. After this step,
you will see a list with all your repositories registered in Github. Click the button to the left of your
listed repository and wait for the message "Off" to become "On".
⁵⁴https://coveralls.io/
Supporting Your Code on Multiple Operating Systems 79

Adding repositories

Note that with the repository enabled, we now have a link to the details page. By clicking this link
we will be directed to a page with all the initial information for the project setup in coveralls. For
our solution, we will use the option to add coveralls information to the .coveralls.yml file.

We will then copy this content from the file option on the setup page and create the new file in
our project. Within our local repository, we will type the following command via prompt/command
line.

1 $ touch .coveralls.yml

We will open this file in our editor and we will add the content to this file. After this step, we will add
the NodeJS package to our list of development dependencies to integrate the coveralls infrastructure
into our project by typing the following command.
Supporting Your Code on Multiple Operating Systems 80

1 $ npm install --save-dev coveralls

Once we submit a new code, we can see that we have the percentage of code coverage information
visible on the coveralls website in the area of our repository. With this we can follow all variations
of code coverage, we create validations and more.

After that, we can add a new badge with the code coverage information for our project in the
README.md the file contained in the project repository. The badge pattern is quite simple:

1 [![Coverage Status](https://coveralls.io/repos/<nome-do-seu-usuario-ou-organização>/\
2 <nome-do-seu-repositório>/badge.svg?branch=master)](https://coveralls.io/r/<nome-do-\
3 seu-usuario-ou-organização>/<nome-do-seu-repositório>?branch=master)

Notice that we have two tags in this code snippet. Replace this information as follows:

• <your-user-or-organisation-name>: name of your user or organisation;


• <your-repository-name>: name of your repository;

For example, based on the example repository, our badge will have the following content.

1 [![Coverage Status](https://coveralls.io/repos/willmendesneto/build-checker/badge.sv\
2 g?branch=master)](https://coveralls.io/r/willmendesneto/build-checker?branch=master)

After adding and saving this code, the final result to be rendered will be something similar to the
following image.
Supporting Your Code on Multiple Operating Systems 81

And with this, we conclude our integration with the coveralls service. This is just a simple example
of one of the many features of this service and I strongly recommend that you read the coveralls
documentation⁵⁵ so that you have a greater This service.

Checking code complexity with PlatoJS


PlatoJS is a NodeJS package that will help us in some validations of our nodebots code. It creates
a report using some data generated through static analysis of the code of our project that shows
us some information such as code complexity, maintenance difficulty, lines of code, possible
implementation errors, among other relevant data.

If you would like to know more about PlatoJS, please visit the repository in the Github of
the project⁵⁶

Its installation is very easy. Just type the command:

1 $ npm install --save-dev plato

And after this step, the plato was installed locally as a development dependency in our node_-
modules folder of our project. Our next step is to add a new NPM command. Now we will have the
code-analysis command that will trigger the plate to our project.

1 {
2 ...
3 "scripts": {
4 "start": "nodemon ./src/index.js -e js,json --watch ./src",
5 "test": "make test",
6 "code-analysis": "plato -r -d report src test"
7 },
8 ...
9 }

And to trigger the PlatoJS, just type:

1 $ npm run code-analysis

And after this command will be created a folder of name report with the information of the analysis
of our repository.
⁵⁵https://coveralls.zendesk.com/hc/en-us
⁵⁶https://github.com/es-analysis/plato
Supporting Your Code on Multiple Operating Systems 82

Within this folder, we will have several files with the information returned from the PlatoJS analysis
that we can see more details by accessing the index.html file in our browser.
This page will have information on each file and graphs showing data such as level of complexity
and lines of code, as we can see in the figure below.
Supporting Your Code on Multiple Operating Systems 83

Report page with information extracted by PlatoJS


Appendix
Breadboard
These are great units for making temporary circuits and prototyping when you need prototype a
circuit quickly and temporarily.
Breadboard are boards that can simulate electronic component connections and since they are not
permanent, it is easy to remove a component if you make a mistake, or just start over and do a new
project.

Breadboard

Piezo
Piezo is a sensor that emits a beep, such as a horn. This signal can be created in an application
Nodebots from a numeric value or an abstraction of sound/music notes, which makes their
Appendix 85

manipulation simpler.
Typical uses of buzzers include alarm devices, timers, and confirmation of user input such as a mouse
click or keystroke.

Piezo
Appendix 86

Resistors
The resistors are widely used in electronics as one of the first electronic components that users have
the first contact and one of the most used. They are small enough in pill form with stripes in most
cases. Because resistance is an essential element of nearly every electronic circuit, you’ll use them
in just about every circuit that you build.
A resistor is an electronic component that limits the flow of electrons dissipating energy in the form
of heat, since the electricity has to struggle to flow through something with a high resistance. It uses
a large amount of energy and converting it into heat.

Resistors

LED (Light-emitting diode)


LED is an abbreviation for LED (Light-emitting diode), a semiconductor device that converts
electricity into light.
Appendix 87

Light-emitting diode

Sensors
A device that converts real-world (analog) data into data that a computer can understand using
ADC and converting data from Analog to Digital format. We will use sensors to detect events or
environment changes and we will send it for you to read in our application.

Protective Conductor (Ground or Ground Wire)


It is the input wire of an electric conductor that has the function of “grounding” all the devices that
need to use its potential as a reference or its electrical properties.
In power systems, the grount connector has the functions of electrical reference for voltage,
protection systems, control for overload/power and equipment protection.
Appendix 88

Jumper Wires
It is a short insulated wire with bare (stripped of insulation) ends. You use them to connect two
points in a breadboard circuit.

Jumpers

Push Button
It is a simple switch mechanism for controlling some aspect of a machine or a process.

Push button
Next steps
Finishing now the contents of this book with some rather didactic examples we already see the power
of Javascript allied to robotics in our applications. These are examples that can be incorporated into
our daily lives and teachings to carry out various other ideas that will arise.
I hope you have enjoyed the contents of this book and the examples as much as I have had the
pleasure of sharing this content. If you have questions, questions or even a conversation, please
contact us at [email protected].
Thank you very much!

You might also like