You are on page 1of 98

Building a Simple Rover

Book 5 of the Arduino Short Reads Series

Gary Hallberg

North Border Tech Training


First Edition
Copyright © 2021 Gary Hallberg and North Border Tech Training
All reserved. This book or any portion thereof may not be reproduced or used in any manner whatsoever
without the express written permission of the publisher except for the use of brief quotations in a book
review.
First Published, 2021
Contents
About the Arduino Short Reads Series .................................................................................................. 5
Foreword .............................................................................................................................................. 6
Prerequisites for this Book .................................................................................................................... 7
Download the Code ............................................................................................................................... 8
Chapter 1: The Chassis, Motors, Power and Basic Components ............................................................ 9
The Chassis........................................................................................................................................ 9
The Geared DC Motor ..................................................................................................................... 10
Motor Control Electronics ............................................................................................................... 10
A Capable Power Source ................................................................................................................... 11
Over-voltage of DC Motors .............................................................................................................. 12
The Sensors ..................................................................................................................................... 13
Summary ......................................................................................................................................... 14
Chapter 2: The Motor Drive System .................................................................................................... 15
Parts needed for this Chapter .......................................................................................................... 15
Powering a DC Motor from the Arduino .......................................................................................... 15
Transistors ...................................................................................................................................... 16
Speed Control of a DC motor ........................................................................................................... 17
Controlling the Direction of a DC Motor.......................................................................................... 18
The Arduino Motor Shield ...............................................................................................................20
Experiment 1: Basic Drive ............................................................................................................... 24
Summary ......................................................................................................................................... 27
Chapter 3: The Application Framework .............................................................................................. 28
Parts needed for this Chapter .......................................................................................................... 28
Selecting the Rover Functions ......................................................................................................... 28
The Arduino Prototyping Shield ...................................................................................................... 28
Experiment 2: The Application Framework..................................................................................... 29
Summary ......................................................................................................................................... 37
Chapter 4: Line Following ................................................................................................................... 38
Parts needed for this Chapter .......................................................................................................... 38
The TCRT5000 Line Following Sensor ............................................................................................ 38
Experiment 3: Testing the TCRT5000 Line Following Sensor ......................................................... 40
Line Following Principles ................................................................................................................ 43
Experiment 4: Line Following Rover ............................................................................................... 45
Summary ......................................................................................................................................... 51
Chapter 5: Object Avoidance ............................................................................................................... 52
Parts needed for this Chapter .......................................................................................................... 52
Time of Flight Principles ................................................................................................................. 52
The HC-SR04 Ultrasonic Range Finding Sensor ............................................................................. 53
Sweep Sensors ................................................................................................................................. 55
Servo Motors ................................................................................................................................... 55
Servo Operation .............................................................................................................................. 56
The SG90 9G Servo ......................................................................................................................... 57
The Servo Power Supply .................................................................................................................. 58
Experiment 5: Calibrating the Sweep Sensor ................................................................................... 58
Object Avoidance Algorithms .......................................................................................................... 61
Experiment 6: Obstacle Avoiding Rover .......................................................................................... 62
Summary ......................................................................................................................................... 71
Chapter 6: Remote Control using Bluetooth ....................................................................................... 72
Parts needed for this Chapter .......................................................................................................... 72
What is Bluetooth? .......................................................................................................................... 72
The HC-05 Bluetooth Module ......................................................................................................... 72
The Processing IDE ......................................................................................................................... 74
Experiment 7: Designing and Programming the App ...................................................................... 75
Wiring the HC-05 ............................................................................................................................ 89
Experiment 8: Connecting to the Rover via Bluetooth ..................................................................... 90
Summary ......................................................................................................................................... 95
Epilogue .............................................................................................................................................. 96
About the Author ................................................................................................................................ 97
About the Arduino Short Reads Series

The idea underpinning the Arduino short reads series is to provide a comprehensive, easy to follow
tutorial set and reference guide for anybody wanting to learn about Arduino and basic electronics.
Having a short reads series means that students and hobbyists can select key topics of interest in the
field with minimal outlay. The series aims to provide an easy introduction to all topics of interest and
expand on these topics to provide a broader and deeper understanding of that focus area. The books
are currently only available in Kindle format to provide a very inexpensive package backed up by video
content and interactive social media.
The series is aimed at youngsters and adults interested in getting into electronics and it takes a modern
approach, combining the use of the inexpensive software driven Arduino controller board, with a
multitude of sensors and discreet electronic components. The experiments in this series of books are
easy to follow, inexpensive to implement and compelling for all interested in STEM education. I hope
to inspire anybody looking for a future career in technology or to simply to have fun.
The first book of this series looks at the Arduino microcontroller and explains its operation and purpose.
Experiments look to show you how to set up the programming environment and drive LEDs as well as
read input from switches, thermistors and photocells. Book 1 will give you a solid foundation of how
some basic electronic components work and how to use them to make simple designs.
Books 6 to 8 in this Arduino short reads series are still being written but the series focuses on the
following:

• Book 1 – First Steps with Arduino (published)


• Book 2 – Working with Displays (published)
• Book 3 – Controlling Motors (published)
• Book 4 – Range Finding, Object Detection and Object Avoidance (published)
• Book 6 – Arduino Special Functions
• Book 7 – Communications with Arduino
• Book 8 – The Arduino Leonardo

If you find this series of books useful then please leave your review and rating on
Amazon.
Follow North Border Tech Training on Facebook and Twitter for the latest news and
insights as to how this series will develop.
Foreword

Book 1 of this series sets out to provide a basic understanding of the Arduino platform, how to program
it and how to interface simple electronics. This book takes a different tack to the others in so far that it
is more project based. Those who have followed the previous books will be able to apply that knowledge,
but there is also enough information presented to allow those who are new to this series to understand
and follow the experiments provided you have a basic understanding of Arduino.
In this book we will build a simple autonomous vehicle. We will also look at utilizing Arduino shields
for the first time. The code for the rover will be very modular such that the code for any operational
function can be substituted with more sophisticated code. You will have a great test bed for developing
your skills and applying them to a popular area of STEM education that is both fun and rewarding.
Our rover will have the ability to follow lines, avoid objects and we will build a PC based app with
‘Processing’ to control the rover via Bluetooth.
I have no doubt the skills learnt here will allow you to develop more engaging and useful projects.
Without further delay let us get into the content.
Prerequisites for this Book

This book assumes you have read Book 1 of the series (‘First Steps with Arduino’) or you already have
some experience of the Arduino platform, how to set up the IDE and undertake some basic Arduino
programming tasks. Basic functions such as ‘digitalRead’ and ‘digitalWrite’ are not covered here
but are explained in Book 1. We will call on some of the material covered in Books 3 and 4 as motor
control and object avoidance play a key part in our rover build, and so I will touch on some key aspects
of these subjects so that anybody starting with this book of the series, and does not have the relevant
knowledge, can still follow the experiments.
Download the Code

You can download the Arduino Code for the experiments in this book from GitHub using the link below.

https://github.com/ghallberg-nbtt/congenial-tribble
I recommend that you do this as I have removed some of the comments from the code in the book
listings to aid readability in print form. I strongly recommend that you comment your code heavily to
facilitate understanding and to become familiar with best engineering practice.
Chapter 1: The Chassis, Motors, Power and
Basic Components

In this chapter we will look at the foundation for your rover. You will need a chassis, a pair of geared
DC motors and a decent battery power source. We will also look at some of the motor control electronics
and sensors that you will need. We will not look at these in much detail here but will cover them all in
the following chapters.

The Chassis
I will be using an extremely common and inexpensive chassis. This ships with geared DC motors
suitable for the chassis and the kit has the benefit of component mounting holes already in place. A
photograph of the kit is shown in Figure 1-1.

Figure 1-1: The Rover Chassis Kit I will be using


Image source: The author
This type of chassis has 2 driving wheels, each powered by a DC motor. The caster is mounted at the
rear of the chassis. The battery bank will be replaced with more powerful rechargeable lithium-ion
batteries.
There are similar 4 wheeled versions of this chassis but bear in mind the extra motors will increase cost
and draw more power. Either way, these chassis have mounting holes pre-drilled for sensors and other
electronics and will make our build easier. I have no doubt the more adventurous reader will want to
build their own chassis.

The Geared DC Motor


You need to consider the DC motor. The type I will be using ships with the chassis kit. They need to be
geared, but do not need to be particularly powerful. My motors work from 3VDC to 6VDC and draw
about 160mA. The point is they need to be geared so that the rotational speed of about 2000rpm can
be converted to torque at the wheels. A gear reduction of about 1:48 is fine. An image of my DC motor
type is shown in Figure 1-2.

Figure 1-2: A Geared DC Motor


Image source: The Web (public domain)

Motor Control Electronics


Space is at a premium on any chassis. Therefore, I chose to make use of Arduino shields to stack the
main electronics elements and contain them within the footprint of the Arduino board. A shield is
simply a functional electronic module that is built to match the form factor of the Arduino board and
has header pins soldered to the underside such that it can be inserted into the headers of the Arduino.
I will be using the Arduino motor shield and we will cover this in detail in Chapter 2. It contains the H-
Bridge needed to control the speed and direction of our motors. Figure 1-3 shows the motors shield
mounted to the Arduino Uno.
Figure 1-3: The Arduino Motor Shield and Arduino Uno
Image source: The author
I will be using a second shield mounted on top of the motor shield. We will need an area for some
development electronics, and we can use an Arduino prototyping shield that has a small integral
breadboard. This small board has just enough space for our needs.

A Capable Power Source


Standard alkaline batteries will not last long when used to power the rover. A good option is to use
rechargeable Lithium batteries. I will be using 2 x 18650 cells that deliver 7.4V and 2600mAh. These
are shown in Figure 1-4.
Figure 1-4: The 18650 Rechargeable Lithium Cells
Image source: The author

Over-voltage of DC Motors
A pair of 18650 cells would deliver 7.4V. This has the potential to supply a voltage higher than the
maximum rated for the 6V motors. In general, we will not be running the motors at full speed and so
the average voltage supplied to the motors will be much less than the maximum rated voltage. However,
it is good practice to measure the output voltage with a meter with the battery under load. The load is
applied by switching on the unit. In my setup the output voltage of the battery was 8V without load and
6.4V under load, so I have no issue running the motors at this voltage. If you want to drop the voltage,
and I will for the servo in Chapter 5, you can use a diode or a few diodes as in Figure 1-5. When you pass
a current through the diode the voltage across that diode will drop by about 0.6V. Therefore, you can
use a few diodes to drop the voltage. You need to ensure that the diode can safely pass the maximum
current of the circuit. Our DC motors draw about 160mA and so a 1N4001 diode is fine to use.
Figure 1-5: Using Diodes to drop a Supply Voltage
Image source: The author

The Sensors
Figure 1-6 shows the collection of sensors that I will be using. We will cover these in detail in later
chapters. There is the HC-05 Bluetooth module, a pair of TCRT5000 based sensors for line following
and an HC-SR04 ultrasonic range finding sensor for obstacle avoidance.

Figure 1-6: The Sensors for our Rover


Image source: The author
It is a good exercise to test fit the components before starting work on the coding, just to check whether
there is adequate space on your chosen chassis. My test fit is shown in Figure 1-7.
Figure 1-7: The Test Fit of the Rover Components
Image source: The author
You will need to devise a suitable way to mount the components onto the chassis. I have a set of nylon
M3 nuts, bolts and spacers that work very well. You can use hot glue to hold components in place. Hot
glue works very well and can be peeled away when dry. There are many mounting brackets available for
the HC-Sr04 and you will need one of these to hold the sensor in place when mounted on the servo
motor we will use to sweep the sensor.

Summary
In this chapter we explored the desired functionality of the rover and took a brief look at all the major
components. You were introduced to the concepts of Arduino shields.
Chapter 2: The Motor Drive System

In this chapter we will look at how to drive the chassis by controlling the speed and direction of each
motor. We will explore the power electronics needed to protect your Arduino board. We will look at the
H-bridge to control direction and how to use Pulse Width Modulation to control the speed of each
motor.

Parts needed for this Chapter


• Arduino Uno or clone board
• Arduino motor shield or clone shield
• Robot chassis with geared DC motors
• Battery power source
• Switch to isolate the battery power
• Jumper wires including Dupont wires

Powering a DC Motor from the Arduino


We must consider that motors are powerful devices, and the Arduino I/O pin can only supply and sink
40mA of current. A small DC motor will draw about 160mA. In book 2 we used a transistor to sink
current from several LEDs to protect our Arduino board. Here we will use a similar circuit.
First, let us look at the circuit needed to interface a motor to an Arduino board. This is shown in Figure
2-1. We will then go on to explain its operation.
Figure 2-1: Interfacing Electronics for the DC Motor
Image source: The author

Transistors
You can consider a transistor as a voltage-controlled switch. They also have applications as amplifiers,
but we will not cover that area in this book. Transistors come in many forms, but we will be using the
most common Bipolar Junction Transistor (BJT). Figure 2-2 shows the symbol for a BJT transistor.
They come in two forms NPN and PNP. This example uses the NPN type.

Figure 2-2. Symbols for BJT Transistors


Image source: The author
Figure 2-1 shows a typical configuration for a transistor acting as a voltage-controlled switch.
Operation is quite simple. When the Arduino pin is set ‘LOW’, there will be no voltage applied to the base
of the transistor. In that situation, no current will be allowed to flow from the collector of the transistor
to the emitter. If we set the Arduino pin to a ‘HIGH’ state, a voltage of about 5V will be applied to the
base of the transistor. When this occurs current can flow freely between the collector and the emitter of
the transistor and so powering the motor. The current needed to turn on the transistor is small. The
1KΩ resistor means this current will be in the order of 4mA and well within the capability of the Arduino
pin. A small transistor can pass in the order of 600mA between the collector and the emitter and so can
cope with the 160mA of the small motor I will be using. If we used a PNP transistor in place of the NPN
transistor, the operation would be the opposite. A ‘LOW’ signal will turn on the transistor and a ‘HIGH’
signal will turn it off.
The capacitor across the motor is there to smooth the voltage supply. The diode is needed to protect the
electronics around the motor. The motor is an ‘inductive’ load. What this means is that when the supply
voltage is suddenly removed the motor will generate a significant reverse polarity voltage spike. The
diode can suppress this spike and so prevent damage to the electronics. This diode is called a flyback
diode.
We will come back to the transistor when we take a closer look at the H-Bridge.

Speed Control of a DC motor


Ideally, we would want to have the ability to set an analog voltage onto a pin and control the speed of
the motor in the normal way by increasing and decreasing that voltage. The issue here is that we cannot
write an analog voltage to a pin of an Arduino Uno. The Arduino Duo does have a Digital to Analog
converter on board, but the Uno does not. However, we do have available a clever technique called Pulse
Width Modulation (PWM). This can be used to trick the eyes and brain into thinking the brightness of
a component such as an LED is being controlled by an analog voltage. PWM can also be used to control
the speed of a DC motor.
So, what is PWM? Essentially, what we do is allocate a regular period over which we want the Arduino
pin to be in a ‘HIGH’ state. The Pulse Width is a measure of the time that the pin is set ‘HIGH’ over that
period. We call this the duty cycle of the waveform. Figure 2-3 illustrates the scenario of 0%, 25%, 50%,
75% and 100% duty cycle.
Figure 2-3: A Diagram showing PWM Duty Cycles
Image source: The author
We can see that for a duty cycle of 0%, the pin is set ‘LOW’, for 25% the pin is ‘HIGH’ 25% of the time and
so on and so forth until we have a duty cycle of 100% when the pin is constantly ‘HIGH’. So, we can see
that the output voltage is not analog but is either on or off. What this means is that we accelerate the
motor for a short period and before it gets to full speed, cut the power off and the momentum keeps the
motor spinning and we simply ‘kick it on’ with the next pulse. The narrower the pulse, the slower the
motor will spin. The PWM period within the Arduino is about 2ms. This equates to a frequency of
500Hz.
You can identify the PWM capable pins of the Arduino board by the tilde symbol (‘~’) next to the pin
number.

Controlling the Direction of a DC Motor


We cannot generate a negative voltage from the Arduino board to reverse the direction of the motor.
But what we can do is reverse the polarity of the supply by using a device called a H-Bridge and so we
will look at the H-Bridge and explore its operation.
Figure 2-4 shows diagrammatically how a H-Bridge works.
Figure 2-4: H-Bridge Operation
Image source: The author
The H-Bridge is a clever, but simple device. It consists of 4 voltage-controlled switches. The supply and
motor are connected as in Figure 2-4. When all switches are open, the motor is unpowered, but able to
freely spin. When a top switch is closed, along with the opposite bottom switch, the motor will run in
either direction based on which pair of switches is closed. You can use an effect called regenerative
braking when both bottom switches are closed. In this mode, when power is removed from the motor,
it acts as a generator whilst still spinning. If we sink the generated current to ground, we will place a
large negative torque on the motor. It will slow quickly, and the motor will be resistive to torque applied
to the shaft.
You may have noticed a potential problem with the H-Bridge. If for any reason both top and bottom
switches on the same side are simultaneously closed, then there will be a short circuit between the
supply and ground. Clearly not good! Real devices have disable/enable inputs that will disable the
supply until the switches are set. Real devices also have built in flyback diodes that protect the device
so connectivity is much simpler. H-Bridges also incorporate transistors as switches to cope with the
power requirements of motors. Figure 2-5 shows a typical H-Bridge circuit using BJT transistors and
flyback diodes.
Figure 2-5: A Transistor based H-Bridge Circuit
Image source: The author

The Arduino Motor Shield


All this theory comes together in the form of the Arduino motor shield. One of which is pictured in
Figure 2-6.

Figure 2-6: The Arduino Motor Shield


Image source: The web (public domain)
At the heart of the motor shield is a L298 H-bridge. This device can power up to 2 DC motors, each
drawing up to 2A. It can also be used to drive a single stepper motor or a pair of servo motors. Figure
2-7 shows the motor shield layout in more detail.

Figure 2-7: The Layout of the Arduino Motor Shield


Image source: The Web (public domain) with annotations by the author
There are screw terminals for the external power supply and the 2 motors. In addition, there are several
TinkerKit connections that we will not use. TinkerKit is a set of electronic modules and sensors that
make a system for STEM education.

• 2 TinkerKit connectors for two Analog Inputs (in white), connected to A2 and A3
• 2 TinkerKit connectors for two Aanlog Outputs (in orange in the middle), connected to PWM
outputs on pins D5 and D6
• 2 TinkerKit connectors for the TWI interface (in white with 4 pins), one for input and the other
one for output
The shield has fixed pin connections to the Arduino board as shown in Table 2-1.
Function pins per Ch. A pins per Ch. B
Direction D12 D13
PWM D3 D11
Brake D9 D8
Current Sensing A0 A1

Table 2-1: The Arduino Motor Shield Pin Connections


The motor shield can drive two brushed DC motors by connecting the two wires of each one in the (+)
and (-) screw terminals for each channel A and B. In this way you can control its direction by setting
the DIR A and DIR B pins ‘HIGH’ or ‘LOW’. You can control the speed by varying the PWM A and PWM
B duty cycle values. The Brake A and Brake B pins, if set ‘HIGH’, will effectively brake the DC motors
rather than let them slow down by cutting the power. You can measure the current going through the
DC motor by reading the L298 current sensing pins. On each channel will be a voltage proportional to
the measured current, which can be read as a normal analog input, through the function
‘analogRead()’ on the analog input A0 and A1. The shield is calibrated to be 3.3V when the channel
is delivering its maximum possible current of 2A.
You must be cautious when powering the motor shield. The power that is applied to the motor shield is
passed to the Arduino Vin pin. If this voltage exceeds +12VDC, the Arduino board will be damaged. If
the motors are in fact above 9VDC, it is recommended that you break this connection and power the
Arduino from its own supply. This is done by cutting the copper pad located on the underneath of the
motor shield. For our project we want one power source, and the motors I will be using run at 6VDC,
so I will keep this connection intact. The connection is shown in Figure 2-8.

Figure 2-8: The Arduino Motor Shield Vin Connector


Image source: The author
We need to develop some basic functions to control the motor shield. We only need to drive each motor
forward, backward and brake. By braking the left motor and driving the right (and vice versa) we can
turn the rover. The motor control functions are shown in Listing 2-1.
const int MOTOR_A_DIRECTION = 12;
const int MOTOR_A_BRAKE = 9;
const int MOTOR_A_SPEED = 3;
const int MOTOR_B_DIRECTION = 13;
const int MOTOR_B_BRAKE = 8;
const int MOTOR_B_SPEED = 11;

void forward (String motor, int speed) {


if (motor == "right") {
digitalWrite(MOTOR_A_DIRECTION, LOW); //Forward direction of Ch A
digitalWrite(MOTOR_A_BRAKE, LOW); //Disengage the brake for Ch A
analogWrite(MOTOR_A_SPEED, speed); //Drive motor on Ch A at set speed
} else {
digitalWrite(MOTOR_B_DIRECTION, LOW); //Forward direction of Ch B
digitalWrite(MOTOR_B_BRAKE, LOW); //Disengage the brake for Ch B
analogWrite(MOTOR_B_SPEED, speed); //Drive motor on Ch B at set speed
}
}

void reverse (String motor, int speed) {


if (motor == "right") {
digitalWrite(MOTOR_A_DIRECTION, HIGH); //Reverse direction of Ch A
digitalWrite(MOTOR_A_BRAKE, LOW); //Disengage the brake for Ch A
analogWrite(MOTOR_A_SPEED, speed); //Drive motor on Ch A at set speed
} else {
digitalWrite(MOTOR_B_DIRECTION, HIGH); //Reverse direction of Ch B
digitalWrite(MOTOR_B_BRAKE, LOW); //Disengage the brake for Ch B
analogWrite(MOTOR_B_SPEED, speed); //Drive motor on Ch B at set speed
}
}

void brake (String motor) {


if (motor == "right") {
digitalWrite(MOTOR_A_BRAKE, HIGH); //Engage the brake for Ch A
} else {
digitalWrite(MOTOR_B_BRAKE, HIGH); //Engage the brake for Ch B
}
}

Listing 2-1: Our Motor Control Functions


Listing source: The author
First, we declare some constant values that are the motor shield pin assignments. This just makes our
code more readable and easier to troubleshoot.
Looking closer at the function to drive a motor forward and backward, we need to pass the identity of
the motor, whether it be the right-hand or left-hand motor, and the speed. The PWM speed will be
identified by 255 as the maximum and 0 as the minimum, but in realty the lowest speed at which the
vehicle will move will be in between these values to overcome inertia and friction. The forward and
reverse functions first set the direction of the motor by setting the I/O pins ‘HIGH’ or ‘LOW’. The brake
is then released by setting the respective I/O pin to ‘LOW’. We then issue a PWM ‘analogWrite’
command the drive the motor at the set speed.
The braking function needs to know which motor it needs to brake and then simply sets the respective
I/O pin to the ‘HIGH’ state.
Looks simple! We can now put this into practice in the first experiment.
Experiment 1: Basic Drive
In this experiment we will test the functions by assembling the basic drive components onto our chassis
and having the vehicle drive forward, reverse, forward left, reverse left, forward right, reverse right,
slow forward, slow reverse and stop. With a bit of luck, the vehicle will end up roughly in the same place
as it started. You will need to attach the motor shield to the Arduino and mount them on the chassis. If
you do not have nuts and bolts, then hot glue is good. The motors and drive system will need to be
mounted along with the power supply. You will need to connect the motors and power to the motor
shield as shown in Figure 2-8.

Figure 2-8: Connecting Motors and Power to the Motor Shield


Image source: Created with Fritzing
An image of my build is shown in Figure 2-9. I added a power switch between the battery box and the
power input to the motor shield. I chose to solder a switch in place, but you can use a switch with screw
terminals or use an area on the breadboard to connect 2 wires together to improvise a switch.
Next type in or copy the Sketch to drive our rover as shown in Listing 2-2.
/*
Basic Drive Functions for the Rover
Copyright 2020 Gary Hallberg
Licensed under MIT https://github.com/ghallberg-nbtt/congenial-
tribble/blob/master/LICENSE
*/

const int MOTOR_A_DIRECTION = 12;


const int MOTOR_A_BRAKE = 9;
const int MOTOR_A_SPEED = 3;
const int MOTOR_B_DIRECTION = 13;
const int MOTOR_B_BRAKE = 8;
const int MOTOR_B_SPEED = 11;

void setup() {
//Set digital control pins as output
pinMode (MOTOR_A_DIRECTION, OUTPUT);
pinMode (MOTOR_A_BRAKE, OUTPUT);
pinMode (MOTOR_B_DIRECTION, OUTPUT);
pinMode (MOTOR_B_BRAKE, OUTPUT);
//Ensure motors are stopped
brake ("right");
brake ("left");
delay (4000); // Pause before starting sequence
}

void loop() {
//forward for 2 seconds at full speed
forward ("right", 255);
forward ("left", 255);
delay (2000);
//reverse for 2 seconds at full speed
reverse ("right", 255);
reverse ("left", 255);
delay (2000);
//forward hard left turn at full speed
forward ("right", 255);
brake ("left");
delay (2000);
//reverse hard left turn at full speed
reverse ("right", 255);
brake ("left");
delay (2000);
//forward hard right turn at full speed
forward ("left", 255);
brake ("right");
delay (2000);
//reverse hard right turn at full speed
reverse ("left", 255);
brake ("right");
delay (2000);
//forward for 2 seconds at low speed
forward ("right", 80);
forward ("left", 80);
delay (2000);
//reverse for 2 seconds at low speed
reverse ("right", 80);
reverse ("left", 80);
delay (2000);
// stop
brake ("right");
brake ("left");
delay (2000);
}

void forward (String motor, int speed) {


if (motor == "right") {
digitalWrite(MOTOR_A_DIRECTION, LOW); //Forward direction of Ch A
digitalWrite(MOTOR_A_BRAKE, LOW); //Disengage the brake for Ch A
analogWrite(MOTOR_A_SPEED, speed); //Drive motor on Ch A at set speed
} else {
digitalWrite(MOTOR_B_DIRECTION, LOW); //Forward direction of Ch B
digitalWrite(MOTOR_B_BRAKE, LOW); //Disengage the brake for Ch B
analogWrite(MOTOR_B_SPEED, speed); //Drive motor on Ch B at set speed
}
}

void reverse (String motor, int speed) {


if (motor == "right") {
digitalWrite(MOTOR_A_DIRECTION, HIGH); //Reverse direction of Ch A
digitalWrite(MOTOR_A_BRAKE, LOW); //Disengage the brake for Ch A
analogWrite(MOTOR_A_SPEED, speed); //Drive motor on Ch A at set speed
} else {
digitalWrite(MOTOR_B_DIRECTION, HIGH); //Reverse direction of Ch B
digitalWrite(MOTOR_B_BRAKE, LOW); //Disengage the brake for Ch B
analogWrite(MOTOR_B_SPEED, speed); //Drive motor on Ch B at set speed
}
}

void brake (String motor) {


if (motor == "right") {
digitalWrite(MOTOR_A_BRAKE, HIGH); //Engage the brake for Ch A
} else {
digitalWrite(MOTOR_B_BRAKE, HIGH); //Engage the brake for Ch B
}
}

Listing 2-2: The Sketch to Drive our Rover


Listing source: The author
There is not much to review here. In the ‘setup’ function we set the digital I/O pins to output, ensured
both motors had the brake applied and added a delay before the sequence starts.
In the main ‘loop’ function we simply call the appropriate directional control function to move the
rover forward, reverse it or turn it. You can see a video demonstration of my rover here
(https://youtu.be/IA67DLk0hu4). If you watch the video, you can see that the rover is a little ‘wild’. A
surface with more grip would make the movement more controlled. You will also see that with many of
these cheaper chassis units, the left and right motors run at different speeds. We will use sensors later
to compensate for this differential. Finally, my slow speed was set at a PWM value of 80. You will need
to experiment with your chassis to work out your minimum speed value.

Summary
In this chapter you learnt about the power constraints of the Arduino board and why you need extra
electronics to control the motor. You learnt how to use a transistor as a voltage-controlled switch to
provide a means to power the motor. You learnt about the principle of Pulse Width Modulation and
how it can be used to control the speed of the motor. You also learnt about the H-Bridge and how this
device can be used to control the direction of a DC motor. On the programming side you learnt how to
write functions to control the motor and how they can be called in turn to drive and turn a vehicle in all
directions.
Chapter 3: The Application Framework

Our rover has different operating modes. These are line following, obstacle avoidance and remote
control. We need a software framework to select between these modes and provide modularity such
that it is easy to swap out the software for any given operational mode and so provide a decent test bed
for improving and enhancing your rover in the future. In this chapter we will build the application
framework.

Parts needed for this Chapter


• The rover assembly from chapter 2
• 1 x Arduino prototyping shield
• 1 x LED
• 1 x 470Ω resistor
• 1 x pushbutton switch
• Jumper wires

Selecting the Rover Functions


There are several cool ways we can select the function of the rover. We could use the Bluetooth receiver
and select the function from a PC or smartphone. We could have a pushbutton on the rover with a digital
display. We could use an infrared remote as well. I am planning to use an Arduino prototyping shield
and the space is at a premium and we must allow some room for the Bluetooth receiver. Therefore, to
save space, I will use small pushbutton with an LED indicator. The code will cycle through the functions
on each button press. Table 3-1 summarizes each function with the associated LED action.
Function Code Selection LED Action
Standby 1 1 flash
Line Following 2 2 flashes
Obstacle Avoidance 3 3 flashes
Remote Control 4 4 flashes

Table 3-1: The Rover Function Selection

The Arduino Prototyping Shield


Since space is at a premium on the chassis, I chose to use an Arduino prototyping shield that can be
stacked on top of the motor shield. This shield provides a small breadboard area where we can fit some
extra electronics, but the size of the breadboard is limited. Figure 3-1 shows an example of such a
prototyping shield.
Figure 3-1: An Arduino Prototyping Shield
Image source: The Web (public domain)
The breadboard is usually supplied detached from the shield. The shield has a copper matrix board that
allows you to solder components onto it and use the peripheral switches and LEDs if needed. However,
the breadboard has an adhesive pad on the underside, and I will use that to stick it to the shield. This
means the peripheral switches cannot be accessed easily so I will use discrete components on the
breadboard. The other factor is that I want to minimize the amount of soldering needed or eliminate it
all together.

Experiment 2: The Application Framework


Start by building the circuit on the mini breadboard as in Figure 3-2. We will use an internal pullup
resistor for the switch.
Figure 3-2: The Switch Circuit to change the Rover Functions
Image source: Created with Fritzing
When writing the Sketch, you can use the code from experiment 1, but remove all code from the ‘loop’
function. The new Sketch is shown in Listing 3-1.
/*
The Application Framework
Copyright 2020 Gary Hallberg
Licensed under MIT https://github.com/ghallberg-nbtt/congenial-
tribble/blob/master/LICENSE
*/

const int MOTOR_A_DIRECTION = 12;


const int MOTOR_A_BRAKE = 9;
const int MOTOR_A_SPEED = 3;
const int MOTOR_B_DIRECTION = 13;
const int MOTOR_B_BRAKE = 8;
const int MOTOR_B_SPEED = 11;
const int SWITCH = 2;
const int LED = 4;
// variables will change:
boolean previousButton = HIGH; // Previous button state
boolean currentButton = HIGH; // Current button state
int function = 1; //Holds the selected function
boolean printOnce = false; //We only want to print to SM on button press

void setup() {
//Set digital control pins as output
pinMode (MOTOR_A_DIRECTION, OUTPUT);
pinMode (MOTOR_A_BRAKE, OUTPUT);
pinMode (MOTOR_B_DIRECTION, OUTPUT);
pinMode (MOTOR_B_BRAKE, OUTPUT);
pinMode (SWITCH, INPUT_PULLUP);
pinMode (LED, OUTPUT);
//Ensure motors are stopped
brake ("right");
brake ("left");
digitalWrite (LED, LOW); //Turn off LED
delay (2000); // Pause before starting
Serial.begin (9600);
}

void loop() {
//read switch and debounce
currentButton = digitalRead(SWITCH); // Read the switch state
if (previousButton != currentButton) // If switch pressed
{
delay(5); // Wait 5ms
currentButton = digitalRead(SWITCH); // Read switch again
}

if (previousButton == HIGH && currentButton == LOW) //Detect a btn press


{
printOnce = false;
//Increment function selection
function++; // increment function by 1
if (function == 5) {
function = 1; //allow the function to cycle 1 thru 4
}
//Flash LED
switch (function) {
case 1:
//Standby
//1 LED flash
flashLED (function);
break;
case 2:
//Line follower
//2 LED flashes
flashLED (function);
break;
case 3:
//Object avoidance
//3 LED flashes
flashLED (function);
break;
case 4:
//Bluetooth control
//4 LED flashes
flashLED (function);
break;
}
}
previousButton = currentButton; // Reset button value
//Act on function selection
switch (function) {
case 1:
//Standby
brake ("right");
brake ("left");
if (!printOnce) {
Serial.println ("Function 1 - Standby");
}
printOnce = true;
break;
case 2:
//Line follower
if (!printOnce) {
Serial.println ("Function 2 - Line Follower");
}
printOnce = true;
break;
case 3:
//Object avoidance
if (!printOnce) {
Serial.println ("Function 3 - Object Avoidance");
}
printOnce = true;
break;
case 4:
//Bluetooth control
if (!printOnce) {
Serial.println ("Function 4 - Bluetooth Control");
}
printOnce = true;
break;
}
}

void forward (String motor, int speed) {


if (motor == "right") {
digitalWrite(MOTOR_A_DIRECTION, LOW); //Forward direction of Ch A
digitalWrite(MOTOR_A_BRAKE, LOW); //Disengage the brake for Ch A
analogWrite(MOTOR_A_SPEED, speed); //Drive motor on Ch A at set speed
} else {
digitalWrite(MOTOR_B_DIRECTION, LOW); //Forward direction of Ch B
digitalWrite(MOTOR_B_BRAKE, LOW); //Disengage the brake for Ch B
analogWrite(MOTOR_B_SPEED, speed); //Drive motor on Ch B at set speed
}
}

void reverse (String motor, int speed) {


if (motor == "right") {
digitalWrite(MOTOR_A_DIRECTION, HIGH); //Reverse direction of Ch A
digitalWrite(MOTOR_A_BRAKE, LOW); //Disengage the brake for Ch A
analogWrite(MOTOR_A_SPEED, speed); //Drive motor on Ch A at set speed
} else {
digitalWrite(MOTOR_B_DIRECTION, HIGH); //Reverse direction of Ch B
digitalWrite(MOTOR_B_BRAKE, LOW); //Disengage the brake for Ch B
analogWrite(MOTOR_B_SPEED, speed); //Drive motor on Ch B at set speed
}
}

void brake (String motor) {


if (motor == "right") {
digitalWrite(MOTOR_A_BRAKE, HIGH); //Engage the brake for Ch A
} else {
digitalWrite(MOTOR_B_BRAKE, HIGH); //Engage the brake for Ch B
}
}

void flashLED (int numberOfFlashes) {


for (int i = 0; i < numberOfFlashes; i++) {
digitalWrite (LED, HIGH);
delay (250);
digitalWrite (LED, LOW);
delay (250);
}
}

Listing 3-1: The Sketch for the Application Framework


Listing source: The author
We have added a few new declarations.
const int SWITCH = 2;
const int LED = 4;
// variables will change:
boolean previousButton = HIGH; // Previous button state
boolean currentButton = HIGH; // Current button state
int function = 1; //Holds the selected function
boolean printOnce = false; //We only want to print to SM on button press
The constant values are for pin assignments for the LED and pushbutton switch. The Boolean values
for the ‘previousButton’ and ‘currentButton’ are there because the switch needs to be debounced
and I will explain why this is needed later. The integer variable ‘function’ holds the value of the
selected rover function and can be any value from 1 to 4. Finally, a Boolean flag (‘printOnce’) is
defined to ensure that any text is printed just one to the serial monitor rather than on every loop.
Essentially, we only want to print updated information when the operating function is changed.
The new code in the main ‘loop’ function comprises 2 ‘switch’ and ‘case’ statements. One will flash
the LED and the other will call the code for each rover function. The ‘switch’ and ‘case’ statement is
equivalent to nested ‘if else’ statements. The ‘switch’ in this our example is the value of the
operating function that is result of each button press. The ‘case’ is what action the code needs to take
for each value of the ‘switch’.
So, the main ‘loop’ starts by looking for a button press. The initial value of ‘function’ is 1. The code
will look for a logic ‘HIGH’ to ‘LOW’ transition of the pushbutton switch. There is then some code to
debounce the switch. We must bear in mind that the switch is a mechanical device, and the contacts
will bounce momentarily until they settle down. The code is looping so fast that these bounces are
detected and read as valid button presses. We would end up with a random function selection. We can
fix this issue in software. When the code detects a button press, the code simply waits 5ms. This is
enough time for the contacts to settle to ensure we have a reliable switch state. The state of the switch
is then read again after this delay.
//read switch and debounce
currentButton = digitalRead(SWITCH); // Read the switch state
if (previousButton != currentButton) // If switch pressed
{
delay(5); // Wait 5ms
currentButton = digitalRead(SWITCH); // Read switch again
}

if (previousButton == HIGH && currentButton == LOW) //Detect a btn press


{
//transition block
//place code here to act on button press
}
previousButton = currentButton; // Reset button value

You will notice that the final piece of code in the block sets the ‘previousButton’ to the
‘currentButton’. If we did not do this, the code in the ‘transition block’ would execute over the entire
time the switch is in a ‘LOW’ state rather than just the ‘HIGH’ to ‘LOW’ transition.
We have three tasks to do on the button press and the first ‘switch’ and ‘case’ needs to be placed
within the transition block indicated above. First, we increment the function and reset it to 1 if the value
is 5. We then call the first ‘switch’ and ‘case’ to flash the LED. We do this by calling a function and
passing the number of flashes as an argument. Finally, we want to set the ‘printOnce’ flag to ‘false’
as the button press indicates updated information we will print to the serial monitor.
printOnce = false;
//Increment function selection
function++; // increment function by 1
if (function == 5) {
function = 1; //allow the function to cycle 1 thru 4
}
//Flash LED
switch (function) {
case 1:
//Standby
//1 LED flash
flashLED (function);
break;
case 2:
//Line follower
//2 LED flashes
flashLED (function);
break;
case 3:
//Object avoidance
//3 LED flashes
flashLED (function);
break;
case 4:
//Bluetooth control
//4 LED flashes
flashLED (function);
break;
}
}

//function to flash the LED


void flashLED (int numberOfFlashes) {
for (int i = 0; i < numberOfFlashes; i++) {
digitalWrite (LED, HIGH);
delay (250);
digitalWrite (LED, LOW);
delay (250);
}
}

The next ‘switch’ and ‘case’ statement calls the respective code block that will control the rover
function. For the standby function we can simply apply the brakes to each motor. To test the code, we
simply print the selected function to the serial monitor and only do this once by check that the
‘printOnce’ flag is set to ‘false’ and then set it to ‘true’. It will be set to ‘false’ again on each button
press.
switch (function) {
case 1:
//Standby
brake ("right");
brake ("left");
if (!printOnce) {
Serial.println ("Function 1 - Standby");
}
printOnce = true;
break;
case 2:
//Line follower
if (!printOnce) {
Serial.println ("Function 2 - Line Follower");
}
printOnce = true;
break;
case 3:
//Object avoidance
if (!printOnce) {
Serial.println ("Function 3 - Object Avoidance");
}
printOnce = true;
break;
case 4:
//Bluetooth control
if (!printOnce) {
Serial.println ("Function 4 - Bluetooth Control");
}
printOnce = true;
break;
}

The code should generate an output like the one shown in Figure 3-3.
Figure 3-3: The Serial Monitor Output for the Application Framework
Image source: The author

Summary
In this chapter you used the ‘switch’ and ‘case’ statement to create a framework for each operating
function for the rover. You used a pushbutton switch and an LED to change and indicate the selected
the function. You learnt how to debounce a switch in software.
Chapter 4: Line Following

In this chapter we will look at how we can add a line following capability to the rover. This application
has uses in manufacturing to allow automated vehicles to follow a predefined route.

Parts needed for this Chapter


• The rover assembly from chapter 3
• 2 x TCRT5000 line following sensors
• Jumper wires

The TCRT5000 Line Following Sensor


I will be using an extremely common and inexpensive line following sensor based on the TCRT5000
reflective infrared (IR) module.
This IR reflective sensor utilizes a TCRT5000 to detect color and distance. It continuously emits IR
light and then detects the reflected light if there is a suitable target. This sensor is often used in line
following robots and data logging applications. This module is particularly useful as it can sense if a
surface is white or black. One of these sensors is pictured in Figure 4-1.
Figure 4-1: The TCRT5000 Line Following Sensor
Image source: The Web (public domain)
The measuring distance range of this sensor is from 1mm to 8mm, and the central point is about 2.5mm.
There is also an on-board potentiometer to adjust the sensitivity. Given the height of my chassis, the
module will need to be mounted low on the underneath. When the emitted infrared light has not been
reflected, or the strength is not large enough, the module will be in the off state. This is signified by a
logic ‘HIGH’ on the digital output. Figure 4-2 shows the unit in more detail.

Figure 4-2: A Diagram of the TCRT5000 Line Following Sensor


Image source: The author
There is in fact a digital and analog output and we will explore both in Experiment 3. Other than that,
the module just needs a supply and a ground connection. The basic feature are as follows:
• Supply Voltage: 3.3V~5V
• Detect distance: 1mm-8mm
• Digital Outputs ‘LOW’ when objects detected
• On-board indicator LED to show the results
• On-board potentiometer to adjust the sensitivity
• On-board LM393 voltage comparator chip

Experiment 3: Testing the TCRT5000 Line Following Sensor


In this experiment we will look at the basic operation of the TCRT5000 Line Following Sensor. First,
build the experiment as shown on Figure 4-3. Although we only need an Arduino Uno and the sensor
module, I chose not to disassemble the chassis to date and so connected the module to the prototyping
shield headers. If you do this, be sure to disconnect the motors.

Figure 4-3: The Layout to Test the TCRT5000 Line Following Sensor
Image source: The author
The Digital Output (DO) pin of the TCRT5000 sensor is connected to I/O pin 5 and the Analog Output
(AO) is connected to Analog in A2 of the Arduino. A0 and A1 are used by the motor shield although we
have not enabled them to date.
We will test the sensor by applying some black insulation tape to a piece of white paper. We want to
explore the distances at which the sensor triggers the DO output and look at the range of analog output
signals from the AO pin.
Next type in or copy the Sketch as shown in Listing 4-1.
/*
TCRT5000 Test
Copyright 2020 Gary Hallberg
Licensed under MIT https://github.com/ghallberg-nbtt/congenial-
tribble/blob/master/LICENSE
*/

const int DO_PIN = 5;


const int AO_PIN = 2;

void setup() {
pinMode (DO_PIN, INPUT); //Read DO from sensor
Serial.begin (9600);
}

void loop() {
int DO = digitalRead (DO_PIN);
int A0 = analogRead (AO_PIN);
if (DO == HIGH) {
Serial.print ("DO HIGH - sensor not triggered. ");
} else {
Serial.print ("DO LOW - sensor triggered. ");
}
Serial.print ("Analog Output: ");
Serial.println (A0);
delay (250);
}

Listing 4-1: The Sketch to Test the TCRT5000 Line Following Sensor
Listing source: The author
Make note of the height that the sensor triggers and adjust the potentiometer to explore how the
sensitivity affects the performance of the sensor. Even experiment how different colored materials
affect readings. My experimental setup is shown in Figure 4-4.
Figure 4-4: My Experimental Setup to Test the TCRT5000 Line Following Sensor
Image source: The author
I discovered that the sensor either triggers continuously when the pot is adjusted fully anti-clockwise
or does not trigger at all if the potentiometer is adjusted fully clockwise. I got the sensor to work
properly at a maximum height of 38mm. I was able to trim the potentiometer more clockwise and get
it to work at a suitable height for mounting on my chassis. I did not find much difference when the color
of the background material was changed from white to a darker color.
You can choose to use a black background and white line or vice versa simply by choosing which DO
output state is corresponds to that color.
Your serial monitor output should look like that in Figure 4-5.
Figure 4-5: The Serial Monitor Output when Testing the TCRT5000 Line Following Sensor
Image source: The author

Line Following Principles


There is nothing complex about creating a robot vehicle that can follow lines. All that is needed are 2
TCRT5000 sensors. Figure 4-6 illustrates the principle of control.
Figure 4-6: Controlling a Line Following Vehicle
Image source: The author
If neither sensor is triggered, the rover will drive forward. This way it can ‘search’ for a line to follow. If
the right-hand sensor triggers the rover will be veering to the left or the line is taking a right turn and
so the rover will turn right until both sensors are no longer triggered. Likewise, if the left-hand sensor
triggers. We can choose to halt the rover if both sensors trigger.
Experiment 4: Line Following Rover
For this experiment, I will configure the rover to follow a black line on a light background. I will use
PVC insulation tape to create the track. We will not need the analog output (AO), so this can be
disconnected. I mounted 2 sensors on the front of the rover and used the black line on white paper from
Experiment 3 to calibrate the sensors using the onboard LED. I used some simple nylon threaded
spacers to bolt the sensors to the vehicle chassis as in Figure 4-7. Remember to disconnect the motors
when powering the Arduino board with the USB cable. The sensors need to be spaced wider than the
line, but not too wide that the rover judders too much when tracking the line. A degree of
experimentation will be required.

Figure 4-7: The Rover with Line Following Sensors Fitted


Image source: The author
First connect the 2 sensors to the Arduino development shield as in Figure 4-8. The right-hand sensor
DO pin is connected to the Arduino I/O pin 5 and the left-hand to pin 6.
Figure 4-8: Connecting the Line Following Sensors to the Rover
Image source: The author
Next type in or copy the Sketch as in Listing 4-2. This Sketch builds on the work we did Experiment 2
so you should use that as a base to start from.
/*
Line Follower
Copyright 2020 Gary Hallberg
Licensed under MIT https://github.com/ghallberg-nbtt/congenial-
tribble/blob/master/LICENSE
*/

const int MOTOR_A_DIRECTION = 12;


const int MOTOR_A_BRAKE = 9;
const int MOTOR_A_SPEED = 3;
const int MOTOR_B_DIRECTION = 13;
const int MOTOR_B_BRAKE = 8;
const int MOTOR_B_SPEED = 11;
const int SWITCH = 2;
const int LED = 4;
const int RIGHT_SENSOR = 5;
const int LEFT_SENSOR = 6;
// variables will change:
boolean previousButton = HIGH; // Previous button state
boolean currentButton = HIGH; // Current button state
int function = 1; //Holds the selected function
boolean printOnce = false; // We only want to print to SM on button press

void setup() {
//Set digital control pins as output
pinMode (MOTOR_A_DIRECTION, OUTPUT);
pinMode (MOTOR_A_BRAKE, OUTPUT);
pinMode (MOTOR_B_DIRECTION, OUTPUT);
pinMode (MOTOR_B_BRAKE, OUTPUT);
pinMode (SWITCH, INPUT_PULLUP);
pinMode (LED, OUTPUT);
pinMode (RIGHT_SENSOR, INPUT);
pinMode (LEFT_SENSOR, INPUT);
//Ensure motors are stopped
brake ("right");
brake ("left");
digitalWrite (LED, LOW); //Turn off LED
delay (2000); // Pause before starting
Serial.begin (9600);
}

void loop() {
//read switch and debounce
currentButton = digitalRead(SWITCH); // Read the switch state
if (previousButton != currentButton) // If switch pressed
{
delay(5); // Wait 5ms
currentButton = digitalRead(SWITCH); // Read switch again
}

if (previousButton == HIGH && currentButton == LOW) // Detect press


{
printOnce = false;
//Increment function selection
function++; // increment function by 1
if (function == 5) {
function = 1; //allow the function to cycle 1 thru 4
}
//Flash LED
switch (function) {
case 1:
//Standby
//1 LED flash
flashLED (function);
break;
case 2:
//Line follower
//2 LED flashes
flashLED (function);
break;
case 3:
//Object avoidance
//3 LED flashes
flashLED (function);
break;
case 4:
//Bluetooth control
//4 LED flashes
flashLED (function);
break;
}
}
previousButton = currentButton; // Reset button value
//Act on function selection
switch (function) {
case 1:
//Standby
brake ("right");
brake ("left");
if (!printOnce) {
Serial.println ("Function 1 - Standby");
}
printOnce = true;
break;
case 2:
//Line follower
if (!printOnce) {
Serial.println ("Function 2 - Line Follower");
}
printOnce = true;
followLine();
break;
case 3:
//Object avoidance
if (!printOnce) {
Serial.println ("Function 3 - Object Avoidance");
}
printOnce = true;
break;
case 4:
//Bluetooth control
if (!printOnce) {
Serial.println ("Function 4 - Bluetooth Control");
}
printOnce = true;
break;
}
}

void forward (String motor, int speed) {


if (motor == "right") {
digitalWrite(MOTOR_A_DIRECTION, LOW); //Forward direction of Ch A
digitalWrite(MOTOR_A_BRAKE, LOW); //Disengage the brake for Ch A
analogWrite(MOTOR_A_SPEED, speed); //Drive motor on Ch A at set speed
} else {
digitalWrite(MOTOR_B_DIRECTION, LOW); //Forward direction of Ch B
digitalWrite(MOTOR_B_BRAKE, LOW); //Disengage the brake for Ch B
analogWrite(MOTOR_B_SPEED, speed); //Drive motor on Ch B at set speed
}
}

void reverse (String motor, int speed) {


if (motor == "right") {
digitalWrite(MOTOR_A_DIRECTION, HIGH); //Reverse direction of Ch A
digitalWrite(MOTOR_A_BRAKE, LOW); //Disengage the brake for Ch A
analogWrite(MOTOR_A_SPEED, speed); //Drive motor on Ch A at set speed
} else {
digitalWrite(MOTOR_B_DIRECTION, HIGH); //Reverse direction of Ch B
digitalWrite(MOTOR_B_BRAKE, LOW); //Disengage the brake for Ch B
analogWrite(MOTOR_B_SPEED, speed); //Drive motor on Ch B at set speed
}
}

void brake (String motor) {


if (motor == "right") {
digitalWrite(MOTOR_A_BRAKE, HIGH); //Engage the brake for Ch A
} else {
digitalWrite(MOTOR_B_BRAKE, HIGH); //Engage the brake for Ch B
}
}

void flashLED (int numberOfFlashes) {


for (int i = 0; i < numberOfFlashes; i++) {
digitalWrite (LED, HIGH);
delay (250);
digitalWrite (LED, LOW);
delay (250);
}
}

void followLine() {
//read state of sensors
boolean lineDetectedRight = digitalRead (RIGHT_SENSOR);
boolean lineDetectedLeft = digitalRead (LEFT_SENSOR);
if (!lineDetectedRight && !lineDetectedLeft) {
//Drive forward
forward ("right", 60);
forward ("left", 60);
} else if (lineDetectedRight && !lineDetectedLeft) {
//Turn right
brake ("right");
forward ("left", 60);
} else if (!lineDetectedRight && lineDetectedLeft) {
//Turn left
forward ("right", 60);
brake ("left");
} else {
//Both sensors trigger - stop!
brake ("right");
brake ("left");
}
}

Listing 4-2: The Sketch for the Line Following Function


Listing source: The author
Let us just focus on the additions to the code. We added 2 constants to represent the DO pins of the line
following sensors and made the digital I/O pins inputs.
const int RIGHT_SENSOR = 5;
const int LEFT_SENSOR = 6;

pinMode (RIGHT_SENSOR, INPUT);


pinMode (LEFT_SENSOR, INPUT);

Inside the ‘switch’ and ‘case’ for the rover function ‘2’, we added a call to a function with the following
line of code.
followLine();
The code within this function follows:
void followLine() {
//read state of sensors
boolean lineDetectedRight = digitalRead (RIGHT_SENSOR);
boolean lineDetectedLeft = digitalRead (LEFT_SENSOR);
if (!lineDetectedRight && !lineDetectedLeft) {
//Drive forward
forward ("right", 60);
forward ("left", 60);
} else if (lineDetectedRight && !lineDetectedLeft) {
//Turn right
brake ("right");
forward ("left", 60);
} else if (!lineDetectedRight && lineDetectedLeft) {
//Turn left
forward ("right", 60);
brake ("left");
} else {
//Both sensors trigger - stop!
brake ("right");
brake ("left");
}
}

First, we read the state of the right-hand and left-hand sensors and assign the values to Boolean
variables. If both sensors return a ‘LOW’ state, then the Boolean variable are both ‘false’ and we drive
the vehicle forward. Note that the speed is low. This should be set to just above the minimum that will
move the vehicle forward. If the speed is too high, momentum will cause the rover to overshoot and
veer off the line.
If the right-hand sensor triggers, then the Boolean variable ‘lineDetectedRight’ will be ‘true’
whilst ‘lineDetectedLeft’ will be ‘false’. The rover turns to the right. Likewise, if the left-hand
sensor triggers.
The only other possible state is when both sensors trigger, and the rover then stops.
You can see a video demonstration of my rover here (https://youtu.be/6StfzXZLizE).

Summary
In this chapter you learnt how the TCRT5000 infrared sensor works and how it can be used to make a
vehicle follow a line. You looked at the principles of line following using 2 sensors and put this into
practice by developing a simple Sketch.
Chapter 5: Object Avoidance

The ability of a rover or robot vehicle to avoid objects is standard in pretty much every shop bought kit.
However, from my experience I have found the functionality somewhat basic in every kit that I have
tested. This is probably the intention, as manufacturers would encourage you to reprogram their
products and enhance this functionality. What I propose we do in this chapter is take a step up from
the basic functionality and implement a more sophisticated object avoidance algorithm that will provide
very satisfactory results. But first we need to look at some of the key components and do some
calibration work.

Parts needed for this Chapter


• The rover assembly from chapter 4
• 1 x SG90 servo
• 1 x HC-SR04 ultrasonic proximity sensor
• 1 x HC-SR04 servo mounting bracket
• 1 x 1N4001 diode
• Jumper wires including Dupont wires

Time of Flight Principles


To detect an object, we need to use a range finding or proximity sensor. The sensor we will be using
utilizes the Time of Flight principle. Time of Flight range finding sensors emit light or sound from a
source toward a target. The target will reflect the radiation and the sensor will measure the time
between the transmission and reception. Knowing the speed of the radiation, whether it be light or
sound, the resultant delay can be divided by 2 and the result will be the distance from the sensor to the
target object. A light source can be infrared or laser, and a sound source is usually ultrasonic. Figure 5-
1 illustrates the principle.
Figure 5-1: The Time of Flight Principle
Image source: The author

The HC-SR04 Ultrasonic Range Finding Sensor


The HC-SR04 ultrasonic ranging module is extremely common and inexpensive. It is used in robotics
for object detection and is a great choice of distance sensor as it has a range of 2cm to 400cm with an
accuracy of up to 3mm and a measuring angle of 150. An image of the HC-SR04 is shown in Figure 5-2.
Figure 5-2: The HC-SR04 Ultrasonic Ranging Module
Image source: The Web (public domain)
You can see in Figure 5-2 that the HC-SR04 has 4 pins. Vcc is the 5V supply and GND is the ground.
The ‘Trig’ and ‘Echo’ pins are the input and output. The HC-SR04 does not generate a distance reading
directly in inches or centimeters. Instead, there is a sequence of events that are summarized by the
timing diagram shown in Figure 5-3.

Figure 5-3: The HC-SR04 Timing Diagram


Image source: The author
To start the measurement, the ‘Trig’ (trigger) pin of SR04 must receive a logic ‘HIGH’ for at least 10us.
After this pulse finishes, the sensor will transmit a cycle of 8 ultrasonic bursts at 40kHz and wait for
the reflected ultrasonic signals. When the sensor detects the reflected ultrasonic signals at the receiver,
it will set the ‘Echo’ pin to logic ‘HIGH’ for a period. The width of this pulse will be proportional to the
distance. The distance in centimeters or inches can be calculated using the following formulae.
𝑊𝑖𝑑𝑡ℎ 𝑜𝑓 𝑝𝑢𝑙𝑠𝑒 𝑖𝑛 𝑚𝑖𝑐𝑟𝑜𝑠𝑒𝑐𝑜𝑛𝑑𝑠
𝐷𝑖𝑠𝑡𝑎𝑛𝑐𝑒 𝑖𝑛 𝑐𝑚 =
58
𝑊𝑖𝑑𝑡ℎ 𝑜𝑓 𝑝𝑢𝑙𝑠𝑒 𝑖𝑛 𝑚𝑖𝑐𝑟𝑜𝑠𝑒𝑐𝑜𝑛𝑑𝑠
𝐷𝑖𝑠𝑡𝑎𝑛𝑐𝑒 𝑖𝑛 𝑖𝑛𝑐ℎ𝑒𝑠 =
148
The sensor will return a pulse width of between 150us and 25ms for 2cm and 400cm respectively, and
38ms if no object is detected. There are some excellent libraries available that support the SR-HC04
and you can utilize these if you like. However, we are going to use a handy Arduino function to measure
the width of the pulse from the HC-SR04.

Sweep Sensors
A sweep sensor is simply a sensor mounted on a servo motor that rotates from side to side while taking
distance readings. It can be used as a form of radar, as a ‘picture’ of the object environment can be built
up by associating a distance to the object with the angle at which the reading was taken. Figure 5-4
shows a HC-SR04 based sweep sensor mounted to one of my robot vehicles and we will be using a sweep
sensor with our rover.

Figure 5-4: A HC-SR04 based Sweep Sensor


Image source: The author

Servo Motors
Before we move on to some experimental work, we need to understand how servo motors work.
A servomotor is a rotary or linear actuator that allows for precise control of angular or linear position.
Amongst other things, they are found in radio-controlled models, automated door locks and the like.
Images of servos can be seen in Figure 5-5.

Figure 5-5: An Assortment of Servo Motors


Image source: The Web (public domain)
A servo can have a fixed angular rotation limit. This is usually 180 0. Other types can rotate freely. They
can generate great torque and hold their position while underload. For this reason, they can draw lots
of power so we will not be running them from the Arduino pins, but will provide an external power
supply.

Servo Operation
You will see that all servos have 3 wires. Usually red and brown are +5V and ground respectively, and
the positional control wire is usually orange. They work on the principle of PWM to define the angle.
They require a positional feedback mechanism, and the simplest type is a potentiometer built into the
motor casing.
PWM operation of the servo motor is illustrated in Figure 5-6. A PWM pulse is triggered every 20ms.
This is generally consistent across all DC servo motors and so makes programming easy with the use of
an Arduino library. If a 1ms pulse is present in the period, the servo rotates to 0 0. If the pulse is 1.5ms,
the servo rotates 900. Finally, if the pulse is 2ms, the servo rotates to 180 0. If the pulse is between these
values the servo rotates to the proportionate angle.
Figure 5-6: Servo PWM Pulses
Image source: The author
If you want the servo to hold its position and resist any rotational torque from the load, simply resend
the same pulse width every 20ms.
Controlling the servo with an Arduino would be difficult if we wrote the code from scratch. However,
there is a predefined library that can be imported that has the functions already encoded and so by
accessing them, the task of controlling the servo is easy. We will be using the built-in library called
‘Servo.h’ by Michael Margolis, so check you have that loaded by accessing ‘Manage Libraries’ from
the Arduino IDE.

The SG90 9G Servo


I will be using the SG90 9G servo for these experiments. This is an extremely common and inexpensive
servo used mainly for RC controlled models. Its operating voltage is 4.8VDC to 6VDC and it has a
stalling torque of 1.6Kg at 6V, so it certainly is capable for such a small servo. One of these servos is
shown in Figure 5-7. The red wire is the positive supply, the brown is ground, and the control wire is
orange. You will also see servos come with several attachment arms for various applications.
Figure 5-7: The SG90 9G Servo and Attachments Arms
Image source: The Web (public domain)

The Servo Power Supply


As mention in Chapter 1, I measured the output voltage of my battery under load as 6.4V DC. The servo
is rated from 4.8VDC to 6VDC. It may work fine with 6.4V, but the servo does have some sensitive
electronics within the casing, so I will use a diode connected to the servo supply to drop this voltage by
0.6V and bring the supply within specification.

Experiment 5: Calibrating the Sweep Sensor


We need to calibrate the sweep sensor so that it will operate correctly. We want the HC-SR04 to look
directly forward when it is set to 900 and then to the left when set to 1500 and finally to the right when
set to 300. The code will first set the servo to 900 and will wait for a button press. After it has come to
rest at 900, the servo armature with the HC-SR04 mount connected to it, can be fixed to the servo with
the screws supplied with the servo kit. Press the button switch, and the servo will move to 1500. Make
sure it is turned to the left. It may be that the sensor turns to the right depending on the orientation of
you motor. If so, 1500 will represent right and 300 will represent left. It does not really matter, but make
sure you adjust your final code.
First, connect the additional components on the breadboard as in Figure 5-8. There is no need to
connect the HC-SR04 just yet, but we will mount the sensor bracket.
Figure 5-8: Connecting the HC-SR04 and Servo
Image source: The author
Next, type in or copy the Sketch as in Listing 5-1.
/*
Servo
Copyright 2020 Gary Hallberg
Licensed under MIT https://github.com/ghallberg-nbtt/cautious-
spoon/blob/master/LICENSE
*/

#include <Servo.h>

const int SERVO = 7; //Servo on Pin 7


const int SWITCH = 2;
Servo myServo; //Create an object if the Servo class

void setup()
{
pinMode (SWITCH, INPUT_PULLUP);
//Create the Servo Object and pass pin number
myServo.attach(SERVO);
}

void loop()
{
myServo.write(90); //moves the servo to 90 deg
while (digitalRead (SWITCH) == HIGH) {
}
myServo.write(30); //moves the servo to right
delay (500);
myServo.write(150); //moves the servo to left
delay (500);
}

Listing 5-1: The Sketch to Calibrate the HC-SR04 Position


Listing source: The author with library contributions by Michael Margolis
The first command is the Sketch loads the library for the servo so we can access all its classes and
functions.
#include <Servo.h>

We need to create an object of the servo class and call it ‘myServo’. We also pass the I/O pin number
to which the control lead of the servo is connected.
myServo.attach(SERVO);

To move the servo, we simply append the ‘write’ command to the ‘myServo’ object and pass the angle
of rotation in degrees.
myServo.write(90);

I mounted my servo on a servo mounting plate. You will see that the servo is lower than in my test build.
I found from experimentation that the sensor was too high to detect some lower objects and so lowered
the whole unit this way. The HC-SR04 mounting plates are inexpensive, readily available, shop bought
components. My setup is shown in Figure 5-9.
Figure 5-9: The HC-SR04 and Servo Attachment
Image source: The author

Object Avoidance Algorithms


We do not have to use a sweep sensor to create an object avoiding rover. In Book 4 of this series (Range
Finding, Object Detection and Object Avoidance), we implemented a simple but effective object
avoidance algorithm using a statically mounted HC-SR04. However, with a sweep sensor we can
implement much more sophisticated and satisfying object avoidance functionality and this is what we
shall do next. The algorithm we will implement is illustrated in Figure 5-10.
Figure 5-10: Object Avoidance using a Sweep Sensor
Image source: The author
Here is how it works. The vehicle will move forward until an object is detected in its path. The sensor
will then rotate to the left or to the right and if no object is detected, it will turn in that direction and
continue to move forward. If an object is detected on the first side it looks, the sensor will rotate to look
in the opposite direction. If no object is detected on that side, the vehicle will turn to that side and
continue to move forward. If there are objects in front and to each side, the vehicle will reverse a set
distance and rotate left or right before moving forward if the path is clear.
There are a lot of actions in this algorithm, but it is easy to program. It is simply a case of following each
step through.

Experiment 6: Obstacle Avoiding Rover


Now is the time to connect the HC-Sr04 sensor as in Figure 5-11.
Figure 5-11: Connecting the HC-SR04 Sensor
Image source: The author
The first thing you may have noticed is that we are using analog input A2 as a digital input. After we
connect the Echo pin of the HC-SR04, we have in fact used all the normal digital I/O pins as pins 0 and
1 must be reserved for the Bluetooth module. You can, however, use the analog inputs as digital I/O
pins in the same way as other pins. You can use decimal numbers 14 to 19 to identify them or use A0 to
A5. Since the motor shield uses A0 and A1 for current sensing, we will use pin A2 as the trigger input.
You should now type in for copy the Sketch as in Listing 5-2. It follows on from the code developed in
Experiment 4.
/*
Object Avoidance
Copyright 2020 Gary Hallberg
Licensed under MIT https://github.com/ghallberg-nbtt/congenial-
tribble/blob/master/LICENSE
*/
#include <Servo.h>

const int MOTOR_A_DIRECTION = 12;


const int MOTOR_A_BRAKE = 9;
const int MOTOR_A_SPEED = 3;
const int MOTOR_B_DIRECTION = 13;
const int MOTOR_B_BRAKE = 8;
const int MOTOR_B_SPEED = 11;
const int SWITCH = 2;
const int LED = 4;
const int RIGHT_SENSOR = 5;
const int LEFT_SENSOR = 6;
const int SERVO = 7;
const int ECHO = 10;
const int TRIGGER = A2;
const int speed = 120;
const int range = 40;
// variables will change:
boolean previousButton = HIGH; // Previous button state
boolean currentButton = HIGH; // Current button state
int function = 1; //Holds the selected function
boolean printOnce = false; // We only want to print to SM on button press
float duration; //time of HC-SR04 pulse
float distance; // calculated distance
//objects
Servo myServo; //Create an object if the Servo class

void setup() {
//Set digital control pins as output
pinMode (MOTOR_A_DIRECTION, OUTPUT);
pinMode (MOTOR_A_BRAKE, OUTPUT);
pinMode (MOTOR_B_DIRECTION, OUTPUT);
pinMode (MOTOR_B_BRAKE, OUTPUT);
pinMode (SWITCH, INPUT_PULLUP);
pinMode (LED, OUTPUT);
pinMode (RIGHT_SENSOR, INPUT);
pinMode (LEFT_SENSOR, INPUT);
pinMode (TRIGGER, OUTPUT);
pinMode (ECHO, INPUT);
//Ensure motors are stopped
brake ("right");
brake ("left");
digitalWrite (LED, LOW); //Turn off LED
delay (2000); // Pause before starting
Serial.begin (9600);
//Create the Servo Object and pass pin number
myServo.attach(SERVO);
myServo.write(90); //look ahead
delay (500);

void loop() {
//read switch and debounce
currentButton = digitalRead(SWITCH); // Read the switch state
if (previousButton != currentButton) // If switch pressed
{
delay(5); // Wait 5ms
currentButton = digitalRead(SWITCH); // Read switch again
}

if (previousButton == HIGH && currentButton == LOW) // Detect a press


{
printOnce = false;
//Increment function selection
function++; // increment function by 1
if (function == 5) {
function = 1; //allow the function to cycle 1 thru 4
}
//Flash LED
switch (function) {
case 1:
//Standby
//1 LED flash
flashLED (function);
break;
case 2:
//Line follower
//2 LED flashes
flashLED (function);
break;
case 3:
//Object avoidance
//3 LED flashes
flashLED (function);
break;
case 4:
//Bluetooth control
//4 LED flashes
flashLED (function);
break;
}
}
previousButton = currentButton; // Reset button value
//Act on function selection
switch (function) {
case 1:
//Standby
brake ("right");
brake ("left");
if (!printOnce) {
Serial.println ("Function 1 - Standby");
}
printOnce = true;
break;
case 2:
//Line follower
if (!printOnce) {
Serial.println ("Function 2 - Line Follower");
}
printOnce = true;
followLine();
break;
case 3:
//Object avoidance
if (!printOnce) {
Serial.println ("Function 3 - Object Avoidance");
}
printOnce = true;
avoidObject ();
break;
case 4:
//Bluetooth control
if (!printOnce) {
Serial.println ("Function 4 - Bluetooth Control");
}
printOnce = true;
break;
}
}

void forward (String motor, int speed) {


if (motor == "right") {
digitalWrite(MOTOR_A_DIRECTION, LOW); //Forward direction of Ch A
digitalWrite(MOTOR_A_BRAKE, LOW); //Disengage the brake for Ch A
analogWrite(MOTOR_A_SPEED, speed); //Drive motor on Ch A at set speed
} else {
digitalWrite(MOTOR_B_DIRECTION, LOW); //Forward direction of Ch B
digitalWrite(MOTOR_B_BRAKE, LOW); //Disengage the brake for Ch B
analogWrite(MOTOR_B_SPEED, speed); //Drive motor on Ch B at set speed
}
}

void reverse (String motor, int speed) {


if (motor == "right") {
digitalWrite(MOTOR_A_DIRECTION, HIGH); //Reverse direction of Ch A
digitalWrite(MOTOR_A_BRAKE, LOW); //Disengage the brake for Ch A
analogWrite(MOTOR_A_SPEED, speed); //Drive motor on Ch A at set speed
} else {
digitalWrite(MOTOR_B_DIRECTION, HIGH); //Reverse direction of Ch B
digitalWrite(MOTOR_B_BRAKE, LOW); //Disengage the brake for Ch B
analogWrite(MOTOR_B_SPEED, speed); //Drive motor on Ch B at set speed
}
}

void brake (String motor) {


if (motor == "right") {
digitalWrite(MOTOR_A_BRAKE, HIGH); //Engage the brake for Ch A
} else {
digitalWrite(MOTOR_B_BRAKE, HIGH); //Engage the brake for Ch B
}
}

void flashLED (int numberOfFlashes) {


for (int i = 0; i < numberOfFlashes; i++) {
digitalWrite (LED, HIGH);
delay (250);
digitalWrite (LED, LOW);
delay (250);
}
}

void followLine() {
//read state of sensors
boolean lineDetectedRight = digitalRead (RIGHT_SENSOR);
boolean lineDetectedLeft = digitalRead (LEFT_SENSOR);
if (!lineDetectedRight && !lineDetectedLeft) {
//Drive forward
forward ("right", 60);
forward ("left", 60);
} else if (lineDetectedRight && !lineDetectedLeft) {
//Turn right
brake ("right");
forward ("left", 60);
} else if (!lineDetectedRight && lineDetectedLeft) {
//Turn left
forward ("right", 60);
brake ("left");
} else {
//Both sensors trigger - stop!
brake ("right");
brake ("left");
}
}

void avoidObject() {
//Take a reading to detect object
distance = measureDistance();
if (distance < range) {
brake ("left");
brake ("right");
delay (250); //stop for 1/4 second
int direction = random(2); //0 = right and 1 = left
if (direction == 0) {
myServo.write (30);
} else {
myServo.write (150);
}
delay (500);
//Take another object reading
distance = measureDistance();
myServo.write(90); //look ahead
delay (500);
if (distance < range) {
//look in opposite direction
if (direction == 0) {
myServo.write (150);
} else {
myServo.write (30);
}
delay (500);
//Take distance reading in opposite direction
distance = measureDistance();
myServo.write(90); //look ahead
delay (500);
if (distance < range) {
//Reverse
reverse ("right", speed);
reverse ("left", speed);
delay (2000);
if (direction == 0) {
brake ("right");
forward ("left", speed);
} else {
forward ("right", speed);
brake ("left");
}
delay (500);
} else {
//turn in that direction
if (direction == 0) {
//reverse a little
reverse ("right", speed);
reverse ("left", speed);
delay (500);
//Turn right
brake ("right");
forward ("left", speed);
delay (500);
} else {
//reverse a little
reverse ("right", speed);
reverse ("left", speed);
delay (500);
//Turn left
forward ("right", speed);
brake ("left");
delay (500);
}
}
} else {
//Turn in that direction
if (direction == 0) {
//reverse a little
reverse ("right", speed);
reverse ("left", speed);
delay (500);
//Turn right
brake ("right");
forward ("left", speed);
delay (500);
} else {
//reverse a little
reverse ("right", speed);
reverse ("left", speed);
delay (500);
//Turn left
forward ("right", speed);
brake ("left");
delay (500);
}
}
} else {
forward ("right", speed);
forward ("left", speed);
}
}

float measureDistance () {
digitalWrite(TRIGGER, LOW);
delayMicroseconds(2);
digitalWrite(TRIGGER, HIGH);
delayMicroseconds(10);
digitalWrite(TRIGGER, LOW);
duration = pulseIn(ECHO, HIGH);
float functionDistance = (duration / 58);
return functionDistance;
}

Listing 5-2: Object Avoidance Functionality


Listing source: The author
We only need to focus on the parts that are new to us.
We define constants for the HC-SR04 sensor and defining constants for the speed and object range help
us fine tune the performance.
const int ECHO = 10;
const int TRIGGER = A2;
const int speed = 120;
const int range = 30;

I chose a speed of 120 and an object range of 40cm. I also created a function in the ‘switch’ and ‘case’
for the object avoidance selection. This will hold all the object avoidance code although there is another
function to measure the distance to the object.
avoidObject ();

The flow of the code in the ‘avoidObject’ function follows the flow of the flowchart of Figure 5-10.
Take some time to follow it through and understand the process. You will also notice that I reverse the
rover a little before turning the vehicle otherwise it tends to hit the object it is trying to avoid!
I created a function to measure the distance and it returns the result to the floating-point variable
‘distance’.
distance = measureDistance();

Let us look closer at the ‘measureDistance’ function.


float measureDistance () {
digitalWrite(TRIGGER, LOW);
delayMicroseconds(2);
digitalWrite(TRIGGER, HIGH);
delayMicroseconds(10);
digitalWrite(TRIGGER, LOW);
duration = pulseIn(ECHO, HIGH);
float functionDistance = (duration / 58);
return functionDistance;
}

Rather than ‘void’ which means the function does not return a value, we declare this function as
‘float’ since the returned value will be a floating-point number. To initiate the HC-SR04, we first set
the ‘TRIGGER’ pin ‘LOW’ for 2 microseconds just to ensure it is in a ‘LOW’ state. We then set it ‘HIGH’. We
then need to wait for 10 microseconds before setting it ‘LOW’ again. We then use a handy function built
into the Arduino programming language (‘pulseIn’) to read in the duration of the pulse on the ‘ECHO’
pin. We can then use the given formula to calculate the distance in centimeters and return this value.
You can see a video demonstration of the rover here (https://youtu.be/6ktThzcmW_Y).

Summary
In this you learnt about object avoidance algorithms and how to use the HC-SR04 ultrasonic sensor to
give the rover object avoidance capabilities. You learnt about ‘Time of Flight’ principles and how the
HC-SR04 sensor uses them to calculate a distance to an object. You learnt about servo motors and how
they can be used to create a sweep sensor. You learnt how to use functions in Arduino code to measure
the width of a pulse and how libraries can be used to make your code development easier.
Chapter 6: Remote Control using Bluetooth

In this chapter we will look to control the rover remotely via a Bluetooth app. We could do this via a
smartphone, but not everybody has a smartphone. I figured that if you are developing Arduino
Sketches, then you will have access to some computing device, and we can use that if it supports
Bluetooth. We will develop our own program using the Processing IDE and the Processing
programming language. You will see that the Processing IDE is remarkably like the Arduino IDE as is
the programming language. But first, we need to understand a bit about Bluetooth.

Parts needed for this Chapter


• The rover assembly from chapter 5
• HC-05 Bluetooth module
• Jumper wires

What is Bluetooth?
Bluetooth is a short-range wireless technology that was designed to do away with the computer’s jungle
of wires. The technology is highly commoditized, meaning it is extremely cheap and you can find
Bluetooth functionality in all sorts of devices such as headsets, speakers, keyboards, and the like.
Bluetooth components can be used as master and slave pairs to provide 2-way serial connections in
place of cables and this is how we will use the technology. We should be able to achieve a maximum
range of 20m.

The HC-05 Bluetooth Module


We will use the HC-05 Bluetooth module with our rover. This is an extremely common component since
it can be used both as a master and slave. It is capable of 2-way (full duplex) communications. The
default bit rate is 9600bps and so draws a lot of parallels with our serial monitor on the Arduino IDE.
Be sure to purchase the version of the module with the breakout pins so that it can easily be connected
to the breadboard. Figure 6-1 shows a picture of the HC-05.
Figure 6-1: The HC-05 Bluetooth Module
Image source: The Web (public domain)
Let us take a closer look at the pinouts. The VCC supply is between +4VDC and +6VDC (typically
+5VDC). GND is ground. There are 2 serial data pins, TXD and RXD, these will connect to I/O pins 0
and 1 of the Arduino Uno that are set aside for serial communications. The STATE pin is connected to
on board LED, it can be used as a feedback indicator to show if Bluetooth is working properly. The EN
pin is used to toggle between Data mode (set ‘LOW’) and AT command mode (set ‘HIGH’). By default, it
is in Data mode. Here is a more detailed summary the HC-05 specification.
• Serial Bluetooth module for Arduino and other microcontrollers
• Operating Voltage: 4V to 6V (Typically +5V)
• Operating Current: 30mA
• Range: <100m
• Works with Serial communication (USART) and TTL compatible
• Follows IEEE 802.15.1 standardized protocol
• Uses Frequency-Hopping Spread spectrum (FHSS)
• Can operate in Master, Slave or Master/Slave mode
• Can be easily interfaced with Laptop or Mobile phones with Bluetooth
• Supported baud rate: 9600,19200,38400,57600,115200,230400,460800
• Default Bluetooth Name: “HC-05”
• Default Password: 1234 or 0000
• Default Communication: Slave
• Default Mode: Data Mode
• Data Mode Baud Rate: 9600, 8, N, 1
• Command Mode Baud Rate: 38400, 8, N, 1

The Processing IDE


Processing is an opensource project that was started in 2001. It was designed to help with and promote
software literacy within the visual arts and visual literacy within technology. There are tens of
thousands of students, artists, designers, researchers, and hobbyists who use Processing for learning
and prototyping. Its graphical nature means it is ideal to use to create a graphical based app to control
our rover.
The Processing IDE works for a computer like the Arduino IDE works for a micro-controller. The
Processing IDE is like the Arduino IDE in terms of structure. It has ‘setup’ and ‘draw’ functions like
the Arduino IDE has a ‘setup’ and ‘loop’ function. The Processing IDE can communicate with the
Arduino IDE through serial communications. This way, we can send data from the Arduino to the
Processing IDE and from the Processing IDE to the Arduino. Processing is also available for virtually
any platform and is a great choice if you are using a Windows, Linux, or an Apple computer.
To download Processing go to the following URL and select the download for your environment.
https://processing.org/download/.
The Processing IDE does not require installation. You simply need to extract the compressed file. After
starting the IDE you will be presented the screen as shown in Figure 6-2. No need to go through the
IDE interface as it is so like the Arduino IDE.
Figure 6-2: The Processing IDE
Image source: The Processing Foundation

Experiment 7: Designing and Programming the App


First, we need to design our GUI. This will be a simple layout as shown in Figure 6-3.

Figure 6-3: The GUI to Control the Rover


Image source: The author
There will be 4 buttons to control the direction, a stop button and 2 speed selectors. We will use a
predefined library for Processing called ‘controlP5’ written by Andreas Schlegel. This library gives us
many different types of graphical control with minimal programming effort. It is ideal for our needs as
the focus should be on the Bluetooth control aspect of the project. From the Processing IDE select
Sketch > Import Library > Add Library and then search for the ‘controlP5’ library and install
it. We will test the code by using a wired USB port connected to our rover. The complete listing is shown
in Listing 6-1. You need to type this in or copy to your Processing IDE.
/*
Rover control GUI
Copyright 2020 Gary Hallberg
Licensed under MIT https://github.com/ghallberg-nbtt/congenial-
tribble/blob/master/LICENSE
*/

import controlP5.*; //Import ControlP5 library


import processing.serial.*; //Serial port communications

ControlP5 cp5; //Create ControlP5 object


PFont font; //Object for the font
Serial port; //Object of the serial port

void setup() {
size(800, 500);
cp5 = new ControlP5(this);
font = createFont("arial", 20); //Use arial font for buttons 20pt
cp5.addButton("forward") //"forward" is the name of button
.setPosition(440, 60) //x and y coords of upper left corner of button
.setSize(120, 120) //(width, height)
.setFont(font)
;
cp5.addButton("right")
.setPosition(580, 200)
.setSize(120, 120)
.setFont(font)
;
cp5.addButton("reverse")
.setPosition(440, 340)
.setSize(120, 120)
.setFont(font)
;
cp5.addButton("left")
.setPosition(300, 200)
.setSize(120, 120)
.setFont(font)
;
cp5.addButton("stop")
.setPosition(440, 200)
.setSize(120, 120)
.setFont(font)
;
cp5.addButton("high") //High speed
.setPosition(100, 140)
.setSize(120, 80)
.setFont(font)
;
cp5.addButton("low") //Low speed
.setPosition(100, 280)
.setSize(120, 80)
.setFont(font)
;

background(77, 166, 255); // background color of window (r, g, b)

//title for our window


fill(0, 0, 0); //text color (r, g, b)
textFont(font);
text("ROVER CONTROL", 325, 30); // ("text", x coordinate, y coordinate)
fill(80, 80, 80); //text color (r, g, b)
text("direction", 460, 490); // ("text", x coordinate, y coordinate)
text("speed", 133, 395); // ("text", x coordinate, y coordinate)

printArray(Serial.list()); //prints all available serial ports


port = new Serial(this, "COM3", 9600); //com port of the Arduino
}

void draw() { //same as loop in Arduino


//No need for code here just yet
}

//Functions to act on button presses


//Called when matched to button name
void forward(){
port.write('f'); //Send 'f' to serial port
}

void reverse(){
port.write('b');
}

void right(){
port.write('r');
}
void left(){
port.write('l');
}

void stop(){
port.write('s');
}

void high(){
port.write('2');
}

void low(){
port.write('1');
}

Listing 6-1: The Processing GUI to Control the Rover


Listing source: The author with library contributions by Andreas Schlegel
First you need to import the ‘controlP5’ library and the library that supports serial communications.
The latter will be built into the IDE so no need to install it.
import controlP5.*; //Import ControlP5 library
import processing.serial.*; //Serial port communications

You then need to create objects for classes in these libraries and an object for the font we will use.
ControlP5 cp5; //Create ControlP5 object
PFont font; // Object for the font
Serial port; //Object of the serial port

The structure of Processing is like Arduino in that there is a ‘setup’ function and a ‘draw’ function that
is the same as the ‘loop’ function in Arduino. Our GUI is static and so we can place all the code in the
‘setup’ function as we only need to draw the GUI once.
We need to create a window and I chose a size of 800 pixels by 500 pixels.
size(800, 500);

The next line of code tells the compiler we are running the ‘cp5’ object we created in ‘this’ context.
This context means the GUI application we are creating.
cp5 = new ControlP5(this);

Contexts are a fundamental part of many programming environments and the concept is difficult to
understand and articulate, so it is one of those you just need to go with until such time as you have
enough experience to fully understand.
Next, we set a font for our titles and button names.
font = createFont("arial", 20);

Creating a button is straightforward. The code adds a button with the specified title, sets the position
of the top left-hand corner, sets the size of the button, and applies the font.
cp5.addButton("forward") //"forward" is the name of button
.setPosition(440, 60) //x and y coords of upper left corner of button
.setSize(120, 120) //(width, height)
.setFont(font)
;
Setting the background color of the window and adding some title text completes the GUI element.
background(77, 166, 255); // background color of window (r, g, b)
//title for our window
fill(0, 0, 0); //text color (r, g, b)
textFont(font);
text("ROVER CONTROL", 325, 30); // ("text", x coordinate, y coordinate)
fill(80, 80, 80); //text color (r, g, b)
text("direction", 460, 490); // ("text", x coordinate, y coordinate)
text("speed", 133, 395); // ("text", x coordinate, y coordinate)

Next, we need to look at how to set up the serial communications. The communications port we will be
using will be the number assigned by your Arduino board and can be retrieved from the Arduino IDE
as in Figure 6-4.
Figure 6-4: Finding the Arduino COM Port Number
Image source: The Arduino IDE
It is useful to list all the COM ports available. These will be displayed in a small monitor window at the
bottom of the Processing IDE when the program is run. My Arduino board is using COM3 and setup
for 9600bps.
printArray(Serial.list()); //prints all available serial ports
port = new Serial(this, "COM3", 9600); //com port of the Arduino

The beauty of using a library like ‘controlP5’ is that all the complex coding to change the mouse over
color, the on click color and click event is taken care of. All that the Sketch needs is a function created
that has the text of each button as its name. The code within that function will then be executed on each
click of the respective button. All we need to do is send an ASCII character over the serial port to the
Arduino. The function for the forward button is below and sends the character ‘f’.
void forward(){
port.write('f'); //Send 'f' to serial port
}

Your GUI should look like the one in Figure 6-5.


Figure 6-5: The Processing GUI whilst Running
Image source: The author
Next onto the Arduino Sketch. Here we have built the code to drive the rover via remote control into a
structure like the other functions. The full listing is shown in Listing 6-2.
/*
Object Avoidance
Copyright 2020 Gary Hallberg
Licensed under MIT https://github.com/ghallberg-nbtt/congenial-
tribble/blob/master/LICENSE
*/

#include <Servo.h>

const int MOTOR_A_DIRECTION = 12;


const int MOTOR_A_BRAKE = 9;
const int MOTOR_A_SPEED = 3;
const int MOTOR_B_DIRECTION = 13;
const int MOTOR_B_BRAKE = 8;
const int MOTOR_B_SPEED = 11;
const int SWITCH = 2;
const int LED = 4;
const int RIGHT_SENSOR = 5;
const int LEFT_SENSOR = 6;
const int SERVO = 7;
const int ECHO = 10;
const int TRIGGER = A2;
const int speed = 120;
const int range = 30;
// variables will change:
boolean previousButton = HIGH; // Previous button state
boolean currentButton = HIGH; // Current button state
int function = 1; //Holds the selected function
boolean printOnce = false; // We only want to print to SM on button press
float duration; //time of HC-SR04 pulse
float distance; // calculated distance
int speedBluetooth = 120;
//objects
Servo myServo; //Create an object if the Servo class

void setup() {
//Set digital control pins as output
pinMode (MOTOR_A_DIRECTION, OUTPUT);
pinMode (MOTOR_A_BRAKE, OUTPUT);
pinMode (MOTOR_B_DIRECTION, OUTPUT);
pinMode (MOTOR_B_BRAKE, OUTPUT);
pinMode (SWITCH, INPUT_PULLUP);
pinMode (LED, OUTPUT);
pinMode (RIGHT_SENSOR, INPUT);
pinMode (LEFT_SENSOR, INPUT);
pinMode (TRIGGER, OUTPUT);
pinMode (ECHO, INPUT);
//Ensure motors are stopped
brake ("right");
brake ("left");
digitalWrite (LED, LOW); //Turn off LED
delay (2000); // Pause before starting
Serial.begin (9600);
//Create the Servo Object and pass pin number
myServo.attach(SERVO);
myServo.write(90); //look ahead
delay (500);

void loop() {
//read switch and debounce
currentButton = digitalRead(SWITCH); // Read the switch state
if (previousButton != currentButton) // If switch pressed
{
delay(5); // Wait 5ms
currentButton = digitalRead(SWITCH); // Read switch again
}
if (previousButton == HIGH && currentButton == LOW) // Detect a button
press
{
printOnce = false;
//Increment function selection
function++; // increment function by 1
if (function == 5) {
function = 1; //allow the function to cycle 1 thru 4
}
//Flash LED
switch (function) {
case 1:
//Standby
//1 LED flash
flashLED (function);
break;
case 2:
//Line follower
//2 LED flashes
flashLED (function);
break;
case 3:
//Object avoidance
//3 LED flashes
flashLED (function);
break;
case 4:
//Bluetooth control
//4 LED flashes
flashLED (function);
break;
}
}
previousButton = currentButton; // Reset button value
//Act on function selection
switch (function) {
case 1:
//Standby
brake ("right");
brake ("left");
if (!printOnce) {
Serial.println ("Function 1 - Standby");
}
printOnce = true;
break;
case 2:
//Line follower
if (!printOnce) {
Serial.println ("Function 2 - Line Follower");
}
printOnce = true;
followLine();
break;
case 3:
//Object avoidance
if (!printOnce) {
Serial.println ("Function 3 - Object Avoidance");
}
printOnce = true;
avoidObject ();
break;
case 4:
//Bluetooth control
if (!printOnce) {
Serial.println ("Function 4 - Bluetooth Control");
brake ("right");
brake ("left");
}
printOnce = true;
remoteControl();
break;
}
}

void forward (String motor, int speed) {


if (motor == "right") {
digitalWrite(MOTOR_A_DIRECTION, LOW); //Forward direction of Ch A
digitalWrite(MOTOR_A_BRAKE, LOW); //Disengage the brake for Ch A
analogWrite(MOTOR_A_SPEED, speed); //Drive motor on Ch A at set speed
} else {
digitalWrite(MOTOR_B_DIRECTION, LOW); //Forward direction of Ch B
digitalWrite(MOTOR_B_BRAKE, LOW); //Disengage the brake for Ch B
analogWrite(MOTOR_B_SPEED, speed); //Drive motor on Ch B at set speed
}
}

void reverse (String motor, int speed) {


if (motor == "right") {
digitalWrite(MOTOR_A_DIRECTION, HIGH); //Reverse direction of Ch A
digitalWrite(MOTOR_A_BRAKE, LOW); //Disengage the brake for Ch A
analogWrite(MOTOR_A_SPEED, speed); //Drive motor on Ch A at set speed
} else {
digitalWrite(MOTOR_B_DIRECTION, HIGH); //Reverse direction of Ch B
digitalWrite(MOTOR_B_BRAKE, LOW); //Disengage the brake for Ch B
analogWrite(MOTOR_B_SPEED, speed); //Drive motor on Ch B at set speed
}
}

void brake (String motor) {


if (motor == "right") {
digitalWrite(MOTOR_A_BRAKE, HIGH); //Engage the brake for Ch A
} else {
digitalWrite(MOTOR_B_BRAKE, HIGH); //Engage the brake for Ch B
}
}

void flashLED (int numberOfFlashes) {


for (int i = 0; i < numberOfFlashes; i++) {
digitalWrite (LED, HIGH);
delay (250);
digitalWrite (LED, LOW);
delay (250);
}
}

void followLine() {
//read state of sensors
boolean lineDetectedRight = digitalRead (RIGHT_SENSOR);
boolean lineDetectedLeft = digitalRead (LEFT_SENSOR);
if (!lineDetectedRight && !lineDetectedLeft) {
//Drive forward
forward ("right", 60);
forward ("left", 60);
} else if (lineDetectedRight && !lineDetectedLeft) {
//Turn right
brake ("right");
forward ("left", 60);
} else if (!lineDetectedRight && lineDetectedLeft) {
//Turn left
forward ("right", 60);
brake ("left");
} else {
//Both sensors trigger - stop!
brake ("right");
brake ("left");
}
}

void avoidObject() {
//Take a reading to detect object
distance = measureDistance();
if (distance < range) {
brake ("left");
brake ("right");
delay (250); //stop for 1/4 second
int direction = random(2); //0 = right and 1 = left
if (direction == 0) {
myServo.write (30);
} else {
myServo.write (150);
}
delay (500);
//Take another object reading
distance = measureDistance();
myServo.write(90); //look ahead
delay (500);
if (distance < range) {
//look in opposite direction
if (direction == 0) {
myServo.write (150);
} else {
myServo.write (30);
}
delay (500);
//Take distance reading in opposite direction
distance = measureDistance();
myServo.write(90); //look ahead
delay (500);
if (distance < range) {
//Reverse
reverse ("right", speed);
reverse ("left", speed);
delay (2000);
if (direction == 0) {
brake ("right");
forward ("left", speed);
} else {
forward ("right", speed);
brake ("left");
}
delay (500);
} else {
//turn in that direction
if (direction == 0) {
//reverse a little
reverse ("right", speed);
reverse ("left", speed);
delay (500);
//Turn right
brake ("right");
forward ("left", speed);
delay (500);
} else {
//reverse a little
reverse ("right", speed);
reverse ("left", speed);
delay (500);
//Turn left
forward ("right", speed);
brake ("left");
delay (500);
}
}
} else {
//Turn in that direction
if (direction == 0) {
//reverse a little
reverse ("right", speed);
reverse ("left", speed);
delay (500);
//Turn right
brake ("right");
forward ("left", speed);
delay (500);
} else {
//reverse a little
reverse ("right", speed);
reverse ("left", speed);
delay (500);
//Turn left
forward ("right", speed);
brake ("left");
delay (500);
}
}
} else {
forward ("right", speed);
forward ("left", speed);
}
}

float measureDistance () {
digitalWrite(TRIGGER, LOW);
delayMicroseconds(2);
digitalWrite(TRIGGER, HIGH);
delayMicroseconds(10);
digitalWrite(TRIGGER, LOW);
duration = pulseIn(ECHO, HIGH);
float functionDistance = (duration / 58);
return functionDistance;
}

void remoteControl() {
if (Serial.available()) { //if data is available to read
char val = Serial.read();
if (val == 'f') { //if f received
forward ("right", speedBluetooth);
forward ("left", speedBluetooth);
}
if (val == 'b') { //if b received
reverse ("right", speedBluetooth);
reverse ("left", speedBluetooth);
}
if (val == 'r') { //if r received
brake ("right");
forward ("left", speedBluetooth);
}
if (val == 'l') { //if l received
forward ("right", speedBluetooth);
brake ("left");
}
if (val == 's') { //if s received
brake ("right");
brake ("left");
}
if (val == '1') { //if 1 received
speedBluetooth = 120;
}
if (val == '2') { //if 2 received
speedBluetooth = 220;
}
}
}

Listing 6-2: Code added to Drive the Rover via Remote Control
Listing source: The author
Although our Sketch is quite large now, there is little code added to control the rover and little to review.
I added a variable for the speed under remote control since this can be changed via the app we
developed.
int speedBluetooth = 120;
Within the ‘case’ statement for the Bluetooth control we reference the function for the remote control
of the rover. This is called ‘remoteControl()’. Also, you will see that I have added calls to the brake
function for each motor. If I did not do this the rover will be driving forward looking for an object to
avoid as we must toggle through that app function beforehand.
//Bluetooth control
if (!printOnce) {
Serial.println ("Function 4 - Bluetooth Control");
brake ("right");
brake ("left");
}
printOnce = true;
remoteControl();

Taking a look at the ‘remoteControl()’ function listed in Listing 6-2. We first start with a check to
see if data is available to read from the serial port buffer and only execute the block of code if there is.
if (Serial.available()) {
//execute this block if data available
}

All we then need to do is read the data and act based on the value of ‘char’ when there is a match in the
‘if’ statement.
char val = Serial.read();

All we need to do now is add the Bluetooth functionality in place of the wired serial port. You can see a
video demonstration of the experiment here (https://youtu.be/8MuJOBgqzZU).

Wiring the HC-05


Figure 6-6 shows how to wire the HC-05 to the Arduino board.
Figure 6-6: Connecting the HC-05 and Arduino
Image source: The author
You will see that I/O pins 0 and 1 have a special function. They are connected to the Arduino’s UART
(Universal Asynchronous Receiver-Transmitter) chip. This is the same chip used to pass serial data via
the USB port. Pin 0 also has ‘Rx’ as a notation and pin 1 has ‘Tx’. These pins are used to transfer serial
data in each direction. What this means is, if we can get our HC-05 to pair with our PC, and act as a
serial ‘COM’ port, then there is no further programming we need to do for our Rover. You will see that
we have added a potential divider to the ‘RXD’ pin of the HC-05. Although the HC-05 takes a 5VDC
supply, it uses 3.3VDC logic for the ‘TXD’ and ‘RXD’ pins. Since pin 1 of the Arduino will drive the ‘Rx’
of the HC-05 at 5VDC for logic ‘HIGH’, we should add a potential divider to bring this down to 3.3VDC.
The ‘TXD’ pin of the HC-05 will drive pin 0 of the Arduino at 3.3VDC, but this is high enough for the
Arduino to register this as logic ‘HIGH’, so no extra components needed. We do not need to connect the
‘EN’ and ‘STATE’ pins of the HC-05.

Experiment 8: Connecting to the Rover via Bluetooth


All we need to do now is connect the HC-05 to the rover breadboard and pair it to your PC. I will go
through the process using a Windows PC. The principle will be the same if you are using a Linux or
Apple based machine.
First connect the HC-05 as in Figure 6-7.
Figure 6-7: Connecting the HC-05 to the Rover
Image source: The author
Power the rover on and leave it in function 1 so that is holds still. The HC-05 will energize.
I have a built in Bluetooth with my laptop, but the process will be the same if you need to use a USB
based Bluetooth adapter.
First pair the device. In Windows 10 you will need to add a new Bluetooth device. Select Settings >
Bluetooth & other devices as in Figure 6-8.
Figure 6-8: Windows 10 Setting Screen
Image source: Microsoft Windows
Select Add Bluetooth or other device. You will be presented with the screen as in Figure 6-9. Select
the Bluetooth option.

Figure 6-9: Selecting a Bluetooth Device


Image source: Microsoft Windows
All being well, the HC-05 will be detected by your PC. You will need to enter the default passcode of
1234 to complete pairing as in Figure 6-10.
Figure 6-10: Pairing with the HC-05
Image source: Microsoft Windows
You will be presented with the screen as in Figure 6-11 on successful completion of the pairing process.

Figure 6-11: Successful Pairing


Image source: Microsoft Windows
The next process is to setup the HC-05 for serial communications. If you look at Figure 6-8, you will
see a menu item called ‘More Bluetooth options’. You need to select this. When the Bluetooth
Settings dialog box opens, select the COM Ports tab.
Figure 6-12: The Bluetooth Settings Dialog
Image source: Microsoft Windows
You should see entries in there against the HC-05. If not, you may need to select Add.. and place an
incoming port and outcoming port from the drop down options provided. You need to note the COM
port number of the Outgoing HC-05 port. For me this is COM10. The default baud rate will be 9600, 8,
N, 1. The same as the Arduino UNO. The baud rate does need to match at both ends. The value of your
outgoing port then needs to be placed into your Processing Sketch.
port = new Serial(this, "COM10", 9600); //com port of the Arduino

This is all that you need to do. Since there is no additional programming to do on the rover. All you
need to do now is select function 4 and run the Processing app. Have fun driving!
An image of my final rover build is shown in Figure 6-13.
Figure 6-13: The Final Rover Build
Image source: The author

Summary
In this chapter you learnt about Bluetooth communications and learnt how to use the HC-05 Bluetooth
module. You learnt how to download and install the Processing IDE and developed a simple GUI to
control the rover remotely. You learnt how to install Bluetooth devices on a Windows PC.
Epilogue

I have had a great time developing and building the rover for this book. I hope you have found the
content useful and engaging. You will be left with a platform you can use to develop more sophisticated
functionality. Also, this book promised to help you make a simple rover. I believe that what we have
produced is a step up from ‘out of the box’ products on the market today in terms of the software and
the scope of the functionality certainly is not out of the skill limits of the novice engineer. Like all large
projects, just break it down into smaller more manageable tasks in the way you did here, and you can
produce a sophisticated machine. You can be proud of yourself.
In the subsequent books in the series, we will use our knowledge gained to look at other focus areas and
create ever more challenging, creative and interesting projects. Whatever your goals are, I hope to have
inspired you and helped you some way to achieving those goals.
If you like this book and the series of books, please leave a review on the Amazon website. For the latest
news on other books in the series you can following my Facebook page, Twitter or follow me as an
author on Amazon.
About the Author

Gary Hallberg has over 34 years of experience in the telecommunications and data communications
industries. He started his career working with circuit switched voice and data technologies in PDH and
SDH environments and then moved on to work with early packet switched networks using Frame Relay
and ATM. His career shifted focus to IP routing when the Internet started to grow, designing large scale
service provider networks using leading edge equipment from vendors such as Juniper and Cisco. Gary
attained Cisco Certified Professional status during this time. Presently, he is working for a global
equipment vendor with a focus on Ethernet networks and is at the forefront of network evolution,
providing infrastructures for SD-WAN, Network Functions Virtualization, SDN and 5G backhaul. Gary
has a Bachelor of Engineering degree in electronic engineering and a Master of Philosophy degree in
robotics and manufacturing.

You might also like