Make An Arduino Controlled Robot PDF
Make An Arduino Controlled Robot PDF
Make An Arduino Controlled Robot PDF
Controlled Robot
Download from Wow! eBook <www.wowebook.com>
Michael Margolis
Make an Arduino-Controlled Robot
by Michael Margolis
Copyright © 2013 Michael Margolis. All rights reserved.
Printed in the United States of America.
Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.
O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are
also available for most titles (http://my.safaribooksonline.com). For more information, contact our corpo-
rate/institutional sales department: 800-998-9938 or corporate@oreilly.com.
Editor: Brian Jepson Production Editor: Rachel Steely
Interior Designers: Nellie McKesson and Edie
Freedman
While every precaution has been taken in the preparation of this book, the publisher and authors assume
no responsibility for errors or omissions, or for damages resulting from the use of the information contained
herein.
ISBN: 978-1-449-34437-5
[LSI]
Table of Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii
iii
Next Steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Table of Contents v
Adding Scanning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
vii
How This Book Is Organized
The book contains information that covers a broad range of robotics tasks. The
hardware and software is built up stage by stage, with each chapter using
concepts explained in earlier chapters. A simple “Hello Robot” sketch is intro
duced in Chapter 6, Testing the Robot’s Basic Functions and extended in subse
quent chapters. Each chapter introduces sketches that add new capabilities to
the robot. Experienced users can skip directly to the chapters of interest—full
source code for every sketch in this book is available online. However, users
who want to learn all about the techniques covered will benefit and hopefully
enjoy working with all the sketches presented in the book, as each sketch
enables the robot to perform increasingly complex tasks.
The sketches are built using functional modules. The modules are stored using
Arduino IDE tabs (see Chapter 5). Modules described in early chapters are
reused later and to avoid printing the same code over and over in the book,
only code that is new or changed is printed. Figure P-1 illustrates how the code
is enhanced from sketch to sketch. The horizontal bars represent the sketches,
the vertical bars represent functional modules that are included in the sketch
es. The initial ‘helloRobot’ sketch is transformed into the ‘myRobot’ sketch by
the moving the code for program definitions into a module named robotDe
fines.ino and reflectance sensors into a module named IrSensors.ino. These
module are included as tabs in the ‘myRobot’ sketch. Each subsequent sketch
is enhanced by adding code to an existing module or creating a new module
as a tab.
All code for every sketch is available in the download for this book and you can
load the sketch being discussed into your IDE if you want a complete view of
all the code.
Chapter 1, Introduction to Robot Building provides a brief introduction to robot
hardware and software.
Chapter 2, Building the Electronics describes how to prepare the electronics for
use with the robot.
Chapter 3, Building the Two-Wheeled Mobile Platform describes how to assem
ble the 2 Wheel Drive (2WD) mobile platform.
Chapter 4, Building the Four-Wheeled Mobile Platform describes how to assem
ble the 4 Wheel Drive (4WD) mobile platform.
Preface ix
Chapter 5, Tutorial: Getting Started with Arduino introduces the Arduino envi
ronment and provides help getting the development environment and hard
ware installed and working.
Chapter 6, Testing the Robot’s Basic Functions explains the first robotics sketch.
It is used to test the robot. The code covered in this chapter is the basis of all
other sketches in the book:
• HelloRobot.ino (Arduino sketch) — Brings the robot to life so you can test
your build.
• myRobot.ino — Same functionality as above but structured into modules
to make it easy to enhance.
Chapter 7, Controlling Speed and Direction explains how you make the robot
move:
Appendix A, Enhancing Your Robot provides tips and techniques for designing
and building complex projects.
Appendix B, Using Other Hardware with Your Robot describes some alternative
solutions for motor control.
Appendix C, Debugging Your Robot has hardware and software debugging tips.
This sections includes Arduino and Processing source code to enable real time
graphical display of robot parameters on a computer screen.
Preface xi
A good book for inspiration on more robotics projects is:
• Make: Arduino Bots and Gadgets by Tero Karvinen, Kimmo Karvinen (O’Reil
ly)
Here is the terse version that returns the same thing (note the negation oper
ator before the function call):
return !irSensorDetect(sensor);
Feel free to substitute your preferred style. Beginners should be reassured that
there is no benefit in performance or code size in using the terse form.
One or two more advanced programming concepts have been used where
this makes the code easier to enhance. For example, long lists of sequential
constants use the enum declaration.
The enum keyword creates an enumeration; a list of constant integer values. All
the enums in this book start from 0 and increase sequentially by one.
For example, the list of constants associated with movement directions could
be expressed as:
const int MOV_LEFT = 0
const int MOV_RIGHT = 1;
const int MOV_FORWARD = 2;
const int MOV_BACK = 3;
const int MOV_ROTATE = 4;
const int MOV_STOP = 5;
The following declares the same constants with the identical values:
enum {MOV_LEFT, MOV_RIGHT, MOV_FORWARD,
MOV_BACK, MOV_ROTATE, MOV_STOP};
Preface xiii
Constant width
Indicates command lines and options that should be typed verbatim;
names and keywords in programs, including method names, variable
names, and class names; and HTML element tags
Constant width bold
Indicates emphasis in program code lines
Constant width italic
Indicates text that should be replaced with user-supplied values
How to Contact Us
We have tested and verified the information in this book to the best of our
ability, but you may find that features have changed (or even that we have
made a few mistakes!). Please let us know about any errors you find, as well as
your suggestions for future editions, by writing to:
O’Reilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
800-998-9938 (in the United States or Canada)
707-829-0515 (international/local)
707-829-0104 (fax)
We have a web page for this book, where we list errata, example code, and any
additional information. You can access this page at:
http://shop.oreilly.com/product/0636920028024.do
To comment or ask technical questions about this book, send email to:
bookquestions@oreilly.com
For more information about our books, courses, conferences, and news, see
our website at http://www.oreilly.com.
Find us on Facebook: http://facebook.com/oreilly
Follow us on Twitter: http://twitter.com/oreillymedia
Watch us on YouTube: http://www.youtube.com/oreillymedia
Acknowledgments
Rob DeMartin, the business manager at Maker Media, was the driving force
behind the botkits, which inspired the writing of this book. Isaac Alexander
and Eric Weinhoffer at Maker Media ran with the concept to make it a product.
I thank them for testing the content of the book to ensure that the projects
and the hardware worked well together.
Preface xv
I am grateful to the Arduino community for contributing a wealth of free soft
ware, in particular, the IrRemote library from Ken Sherriff that is used in the
remote control chapter. I would also like to express my appreciation to Limor
Fried (Ladyada) for creating the hardware, software and online build notes for
the motor shield used in this book.
Thanks also to DFRobot, the innovative company that designed the robot
platforms and provided the exploded view drawings used in the build chap
ters.
Mat Fordy at Cool Components (coolcomponents.co.uk) organized the robot
ics workshop that provided a testing ground for the book’s projects. It was
helpful and rewarding to work with the participants, each with a different level
of experience, to build the robots and see their pleasure in bringing their cre
ations to life. Their feedback helped make the book content clear, practical and
fun.
If I have achieved my goal of making the rich variety of technical topics in this
book accessible to readers with limited electronics or programming experi
ence, then much of the credit goes to Brian Jepson. Brian, who was also my
editor for the Arduino Cookbook, was with me every step of the way. I thank
him for his guidance: from his support and passion in beginning the project,
to his editorial expertise and application of his masterful communications skills
right through to using his technical knowledge to test all the projects in the
book.
I would like to thank my entire family for listening to me explain the finer points
of robotics during a week- long vacation in the early stages of preparing this
book. Four generations of my family were patient and constructive at times
when they would have preferred to be boating on the lake or walking in the
woods.
Finally, this book would not be what it is without the contributions made by
my wife, Barbara Faden. Her feedback on early drafts of the manuscript helped
shape the content. I am especially grateful for her support and patience in the
wake of disruption created as I wrangled with these two little robots to meet
the book’s deadline.
This book takes you through the steps needed to build a robot capable of
autonomous movement and remote control. Build instructions are provided
for 2WD (two wheel drive) and 4WD (four wheel drive) platforms. The platforms
shown in Figure 1-1 and Figure 1-2 will make the construction a snap, but you
can build your own robot chassis if you prefer. The connection and use of the
control electronics and sensors are fully explained and the source code is in
cluded in the book and available for download online (see “How to Contact
Us” (page xv) for more information on downloading the sample code).
1
Introduction to Robot Building
Figure 1-3. Robot moves around but remains within the white area
Chapter 1 3
Why Build a Robot?
Figure 1-5. Two wheeled and four wheeled robots with distance scanners
There are many different kinds of robots, some can crawl, or walk, or slither.
The robots described in this book are the easiest and most popular; they use
two or four wheels driven by motors.
Chapter 1 5
Tools
Tools
These are the tools you need to assemble the robot chassis.
Phillips Screwdriver
A small Phillips screwdriver from your local hardware store.
Small long-nose or needle-nose pliers
For example, Radio Shack 4.5-inch mini long-nose pliers, part number
64-062 (see Figure 1-10) or Xcelite 4-inch mini long-nose pliers, model L4G.
Small wire cutters
For example, Radio Shack 5” cutters, part number 64-064 (Figure 1-11) or
Jameco 161411
Soldering iron
For example, Radio Shack 640-2070 (Figure 1-12) or Jameco 2094143 are
low cost irons suitable for beginners. But if you are serious about elec
tronics, a good temperature controlled iron is worth the investment, such
as Radio Shack 55027897 or Jameco 146595.
Solder 22 AWG (.6mm) or thinner
For example, Radio Shack 640-0013 or Jameco 73605.
Chapter 1 7
Building the Electronics 2
This chapter guides you through the electronic systems that will control your
robot. Both the two wheeled and four wheeled platforms use the same mod
ules, a pre-built Arduino board (Arduino Uno or Leonardo), and a motor con
troller kit. The motor controller featured in this book is the AFMotor shield from
Adafruit Industries. Although other motor controllers can be used (see Ap
pendix B) the AFMotor shield provides convenient connections for the signals
and power to all the sensors and devices covered in this book. It is also capable
of driving four motors, which is required for the four wheel drive chassis.
Although the attachment of the boards to the robot differs somewhat de
pending on the chassis, the building of the AFMotor circuit board kit is the
same for both. If you don’t have much experience with soldering, you should
practice soldering on some wires before tackling the circuit board (you can
find soldering tutorials here: http://www.ladyada.net/learn/soldering/
thm.html).
Hardware Required
See http://shop.oreilly.com/product/0636920028024.do for a detailed parts list.
9
Construction Techniques
Construction Techniques
This section provides an overview of the motor controller shield construction.
Soldering
Soldering is easy to do if you understand the basic principles and have a little
practice. The trick for making a good solder joint is to provide the right amount
of heat to the parts to be soldered and use the right solder. 22 AWG solder
(0.6mm or .025 inch) or thinner is a good choice for soldering printed circuit
boards. A 25-watt to 40-watt iron, ideally with temperature control, is best. The
components to be joined should be mechanically secure so they don’t move
while the solder is cooling—wires should be crimped around terminals (see
Figure 4-11 and Figure 4-12). To make the joint, the tip of the iron should have
good contact with all the components to be soldered. Feed a small amount of
solder where the iron is touching the parts to be joined. When the solder flows
around the joint, remove the solder first and then the iron. The connection
should be mechanically secure and the joint shiny.
The parts to the right of (as well as below) the board are packed with
the shield, but the three 6-pin headers on the left are not supplied with
the standard shield. These headers are used to connect the sensors.
These headers are included with the Maker Shed companion kits that
go along with this book. You can also purchase female headers from
Adafruit and other suppliers.
The two Maker Shed kits can be found at http://www.makershed.com/
Bots_and_Bits_for_Bots_s/46.htm. Look for either the Rovera 2W
(Arduino-Controlled 2 Wheel Robotics Platform) or Rovera 4W (Ardu
ino Controlled 4 Wheel Robotics Platform).
Solder the smallest components first (Figure 2-2). The three small capacitors
and two resistors are not polarized so you can insert them either way around.
Chapter 2 11
Construction Techniques
The resistor network (the long thin component with ten pins) is polarized—
the end with the white dot goes to the left of the PCB (nearest to C1) as shown
in Figure 2-3.
Figure 2-3. Solder the resistor network the marker (circled) indicates correct orientation
The large capacitors, ICs, and LED are all polarized. The color of the components
shown in the step-by-step assembly pictures on the Adafruit site (you can find
the link at the beginning of these build notes) may not match the components
or layout for the parts you received (particularly the capacitors) so carefully
check that you have placed the correct value component in the correct orien
tation. Figure 2-4 shows the layout for version 1.2 of the shield PCB. The kit
includes two IC sockets for the L293D chips. As mentioned in the assembly
instructions on the Adafruit site, these are optional but if you like to play safe
and want to use the sockets, solder them so the indent indicating pin 1 matches
the outline printed on the PCB.
Figure 2-5 shows the board with all of the standard shield components (push
button, headers, screw terminals) soldered. The final assembly step is to solder
the three 6-pin female headers near the analog input pins. These headers are
not included in the shield package or mentioned in Adafruit’s step-by-step
build instructions, but are included with the Maker Shed kits.
Chapter 2 13
Construction Techniques
Figure 2-6 shows all components including the sensor headers soldered. Trim
the component pins (except the header pins that connect the shield to the
Arduino) on the underside of the board so they are clear of the Arduino when
the shield is plugged onto the board. Locate one of the jumpers supplied with
the shield and plug this onto the pins marked power jumper—this connects
the motor power input and the Arduino VIN (power input) together so both
are fed from the batteries that you will be wiring after you have built the robot
chassis.
Figure 2-7 shows where all of the sensors and other external devices will be
connected. The three pin female headers are not needed for some of the
projects but you will find it convenient to solder these to the shield at this time.
Figure 2-8 shows two styles of connections. On the left, you’ll find the
stripboard-based wiring scheme as described in “Making a Line Sensor
Mount” (page 17). As you’ll see in later chapters, you can experiment with a
variety of mounting methods, including the stripboard-based one. The right
side of Figure 2-8 shows the wiring for separately connected sensors. As you
read through the later chapters and experiment with various mounting tech
niques, you’ll use one or the other wiring schemes. Because you’ll be using
sockets and ribbon connectors to hook up the sensors, you won’t be locked
into any particular connection scheme; you can mix and match.
Chapter 2 15
Construction Techniques
The left and right designation in the diagram refers to left and right
from the robot’s perspective, and the later chapters will explain where
to connect these.
Figure 2-8. Connection detail stripboard wiring is shown on the left, individual jumpers shown
on the right
Chapter 2 17
Construction Techniques
Figure 2-10. Stripboard layout for mounting QTR1A reflectance sensors for line following
To ensure that the mounting bolts don’t short the tracks, you can either cut
the tracks as shown in Figure 2-10 (you will be cutting along the third column
from the left, or the “C” column) or use insulated washers between the bolts
heads and the tracks. Figure 2-11 shows how the header sockets are connected,
and Figure 2-12 shows the completed stripboard, with the ribbon cable con
nected. A ten inch length of cable is more than ample. Figure 2-13 shows the
other end of the ribbon connected to shield pins.
Chapter 2 19
Construction Techniques
The method of mounting the stripboard depends on the robot chassis; see
Chapter 3, Building the Two-Wheeled Mobile Platform or Chapter 4, Building the
Four-Wheeled Mobile Platform. The three holes shown will suit either chassis
but you may prefer to wait until you have built the chassis and only drill the
holes you need.
Next Steps
The next stage in building the robot is to assemble the chassis. Chapter 3 covers
the two-wheeled robot and Chapter 4 is for the four-wheeled version.
This chapter provides advice on the construction of a Two Wheel Drive (2WD)
chassis with front caster, as shown in Figure 3-1. Construction is straightfor
ward; you can follow the detailed steps or improvise if you want to customize
your robot. The chapter also shows how you attach and connect sensors used
in the projects covered in later chapters.
If you prefer to build a two wheeled robot of your own design, you should read
the sections on attaching control electronics and sensors; this will prepare you to
use the code for the projects in the chapters to come. Information in this chapter
my also provide some ideas to help with the design of your own robot.
21
Hardware Required
Hardware Required
See http://shop.oreilly.com/product/0636920028024.do for a detailed parts list.
Mechanical Assembly
Lay Out the Chassis Parts
Figure 3-2 shows all of the parts contained in the 2WD chassis package. The
three black brackets to the left of the figure are not needed for any of the
projects in this book.
Figure 3-3 shows the contents of the bag containing the mounting hardware.
Locate the two bolts with the flat heads and put them aside for mounting the
battery case. Also identify the two thicker (M4) bolts that will be used to attach
the caster. The remaining short bolts in this pack are identical.
Chapter 3 23
Mechanical Assembly
Motor Assembly
Use two long bolts with lock washers and nuts, as shown in Figure 3-5, to attach
each motor to the chassis lower plate. Tighten the nuts snugly but take care
not to stress the plastic motor housing.
Lock washers are used to prevent a nut from accidentally coming lose
due to vibration. This is particularly important for attaching the motor
and switch. These washers have a split ring or serrations that apply
extra friction when tightened.
If you find that things still come lose, don’t overtighten the nuts; an
effective solution is retighten the nut and apply a dab of nail polish to
the point where the threads emerge from the nut.
Figure 3-4 shows the motors in place with the nut seen on the upper right
ready to be tightened.
Chapter 3 25
Mechanical Assembly
Chapter 3 27
Mechanical Assembly
Attach the sensor bracket to the underside of the lower chassis plate, as seen
in Figure 3-8 and Figure 3-9.
This robot is sometimes built with the sensor plate mounted at the
opposite end of the chassis (furthest from the caster). You can build
yours however you like, but the orientation shown here enables the
servo mounted distance scanner to be attached in the front of the robot.
Also, the sensor bracket in this location maximizes the distance be
tween the wheels and the line sensors and this improves line following
sensitivity.
Figure 3-9 shows the underside of the chassis after mounting the sensor brack
et. Note that the sensor bracket is attached to the bottom of the chassis plate.
Figure 3-8. Sensor bracket viewed with the robot right Figure 3-9. Sensor bracket viewed with the robot up
side up side down
The battery pack is bolted to the bottom base plate with two countersunk (flat
headed) Phillips bolts as shown in Figure 3-10 and Figure 3-11. You may want
to delay this step until after the battery leads have been soldered to make it
easier to position all the wires.
Cut two pieces of red/black wire, each about 7 1/2 inches long. Strip to expose
about 3/16 inch of bare wire at one end of the wires and attach to the motor
terminals. Strip 1/4 inch off the other end of the pairs of wires; these will be
connected to the motor shield. Connect a 0.1uF capacitor across each of the
motor terminals, as shown in Figure 3-12. The capacitors suppress electrical
spikes generated by the motor that could interfere with signals on the Arduino
board.
Chapter 3 29
Mechanical Assembly
The DC power jack is bolted to the top plate using the large (M8) lock washer
and nut. The switch is mounted using two (M6) nuts and a lock washer. Put
one nut on the switch leaving around 3/16” of thread above the nut. Then place
the lock washer on the thread and push this through the opening in the rear
plate and secure with the second M6 nut.
Orient the switch so the toggle moves towards the jack, as shown in Figure 3-13
and Figure 3-14 (Figure 3-15 shows the view from beneath).
Figure 3-14. Top panel showing location of switch and Figure 3-15. Top panel underside showing orientation
DC jack of switch and jack
The battery can be wired as shown in Figure 3-16 and Figure 3-17. The power
switch will disconnect the battery when the robot is not in use. The DC jack is
not used in this configuration (other than as a junction point for the black
ground wires). The switch is off when the toggle is closer to the DC jack as
shown (the toggle is a lever; when the exposed end is up as seen in the figure,
the contact at the bottom is connected and the contact wired to the shield is
open).
Chapter 3 31
Mechanical Assembly
You can build a simple trickle charger into the robot if you will be using re
chargeable NiMH batteries. The charger can be built using the circuit shown
in Figure 3-18 and Figure 3-19. See “Trickle Charging” (page 229) for information
about using the charger.
The easiest way to mount the Arduino board is with a strip of Velcro. A 2.5” x
1.5” strip is supplied with the Rovera 2W (Arduino-Controlled 2 Wheel Robotics
Platform) kit. To prevent the Arduino pins from accidentally shorting to the
chassis, apply insulating tape to the underside of the Arduino board. Gaffer
tape works well but you can use (non-conductive) duct tape or heavy duty
electrical tape. Attach the ‘hairy’ side of the Velcro to the taped Arduino board,
the hook side is fastened as shown in Figure 3-20.
Chapter 3 33
Mechanical Assembly
Figure 3-20. Velcro pad in position on the 2WD chassis. Inset shows Velcro attached to the
Arduino board.
Figure 3-21 shows the mounted boards. The Velcro will hold the boards in
position when the robot is moving about, but use one hand to steady the
Arduino when you unplug the shield and take care not to use too much down
ward pressure that could push the Arduino pins through the tape when plug
ging in the shield.
If you prefer a more rigid mount, you can use two of the 10mm brass standoffs
supplied with the chassis and two M3 bolts and nuts (seen on the right side of
the board as shown in Figure 3-22). Use a 10mm spacer and M2.5 in the hole
near the reset switch. (The hole near the DC jack at the lower left is not used.)
Figure 3-23 shows the location of the mounting points viewed from the un
derside of the panel.
Chapter 3 35
Mechanical Assembly
Figure 3-22. Mounting the Arduino board as viewed Figure 3-23. Underside showing location of the three
from the top of the chassis Arduino mounting points
Attach the top plate with four M3 bolts as shown in Figure 3-24.
Chapter 3 37
Mounting the IR sensors
for line following. The stripboard mount described in “Making a Line Sensor
Mount” (page 17) simplifies the attachment and wiring of the sensors for line
detection and this can also be used for edge detection, but bear in mind that
the robot will perform the edge detection task best with the sensors further
apart. If the sensors are close together, the robot can have difficulty determin
ing the best angle to turn when an edge is encountered.
The side with the sensor faces the ground and the header pins face upwards.
Mount each sensor using a 2-56 bolt and nut (M2 bolts and nuts can also be
used) with a 1/2” plastic spacer so the face of the sensor is 3/8” or closer to the
ground. Figure 3-27, Figure 3-28, Figure 3-29, and Figure 3-30 show suggested
mounting.
Chapter 3 39
Mounting the IR sensors
Figure 3-29. Front view showing location of the Edge Figure 3-30. Edge sensors wired and ready to run
Detection Sensors
If you use the stripboard mount for line sensors covered in Chapter 2, Building
the Electronics, the stripboard can be mounted above or below the sensor
bracket, enabling you to experiment with sensor distance to the ground—but
use insulated washers to ensure that the tracks with sensor connections are
not shorted to the bracket. Figure 3-32 and Figure 3-33 show how the strip
board can be mounted.
Chapter 3 41
Mounting the IR sensors
Figure 3-33. Reflectance Sensor location for line following, alternate view
See Figure 3-27 for information on connecting the stripboard wires to the mo
tor shield.
Next Steps
Chapter 5, Tutorial: Getting Started with Arduino explains how to set up and use
the development environment that will be used to upload code to the robot.
If you are already an Arduino expert, you can skip to Chapter 6, Testing the
Robot’s Basic Functions, but first, see “Installing Third-Party Libraries” (page 83)
for advice on the libraries used with the code for this book.
If you have the libraries installed and want run a simple test to verify that the
motors are working correctly, you can run the sketch shown in Example 3-1.
Chapter 3 43
Next Steps
int pwm;
void setup()
{
Serial.begin(9600);
blinkNumber(8); // open port while flashing. Needed for Leonardo only
This sketch runs the motors in opposite directions to cause the robot to rotate
clockwise for 5 seconds, then reverses direction to rotate counter-clockwise.
This will repeat until the power is switched off.
This chapter provides advice on the construction of the 4WD (4 Wheel Drive)
chassis shown in Figure 4-1. Construction is straightforward—you can follow
the detailed steps or improvise if you want to customize your robot. The chap
ter also shows how you attach and connect sensors used in the projects cov
ered in later chapters.
If you prefer to build a four wheeled robot of your own design, you should read
the sections on attaching control electronics and sensors if you want to use
the code for the projects in the chapters to come. Information in this chapter
my also provide some ideas to help with the design of your own robot.
45
Hardware Required
You will need a Phillips screwdriver, long-nose pliers, wire cutters, wire strip
pers, a soldering iron, and solder. If you don’t have these on hand, you can find
more information in Chapter 1, Introduction to Robot Building.
Hardware Required
See http://shop.oreilly.com/product/0636920028024.do for a detailed parts list.
Mechanical Assembly
Mechanical assembly of the 4WD chassis is straightforward and the only tools
needed are a Phillips screwdriver and pliers. Following the steps in order will
ensure that you use the correct hardware in each assembly. You will need a
soldering iron, wire cutters, and wire strippers to wire up the motor and power
leads.
Chapter 4 47
Mechanical Assembly
Motor Assembly
Use four long bolts to attach two motors to each of the side plates. The motor
shaft goes through the large hole and there is a small locating stud on the
motor that fits into the smaller hole. The lock washer (the one with a raised
edge) goes between the nut and flat washer. Ensure the motor is flat against
the plate and tighten the nuts firmly but take care not to use too much force
or you will stress the plastic motor housing. Figure 4-4 and Figure 4-5 shows
the assembly.
Chapter 4 49
Mechanical Assembly
Download from Wow! eBook <www.wowebook.com>
Chapter 4 51
Mechanical Assembly
The DC power jack is bolted to the rear plate using the large (M8) lock washer
and nut as shown in Figure 4-8. The switch is mounted using two nuts and a
lock washer (the locating washer is not used). Put one nut on the switch, leaving
about enough thread for the nut to be attached to the other side. Place the
lock washer on the thread and push this through the opening in the rear plate
and secure with the second M6 nut. Orient the switch so the toggle moves
from side to side, as shown in the figure. Figure 4-9 and Figure 4-10 show two
views of the assembly.
Figure 4-9. Rear panel switch and power jack assem Figure 4-10. The other side of the panel showing the
bly viewed from the front switch orientation and power jack
Chapter 4 53
Mechanical Assembly
Figure 4-12. Crimp the wires Figure 4-13. Solder the motor terminals
Chapter 4 55
Mechanical Assembly
Figure 4-16. Wiring for trickle charging with Arduino voltage monitoring
Figure 4-17. Wiring for trickle charging with Arduino voltage monitoring
Attach the bottom plate using four M3x6 bolts (Figure 4-19).
Chapter 4 57
Mechanical Assembly
The Velcro will hold the boards in position when the robot is moving about,
but use one hand to steady the Arduino when you unplug the shield and take
care not to use too much downward pressure that could push the Arduino pins
through the tape when plugging in the shield.
Chapter 4 59
Mechanical Assembly
Download from Wow! eBook <www.wowebook.com>
If you prefer a more rigid mount, you can use three 3/8” or 1/4 inch (5mm)
spacers with three 1/2 inch 2-56 bolts and nuts. Figure 4-22 and Figure 4-23
show the location of the mounting hardware.
Figure 4-22. Arduino board mounted using three Figure 4-23. Underside view showing mounting nuts
spacers
Figure 4-24 shows the motor wires and battery wires inserted through the
cutouts in the top plate ready for the connections shown in Figure 4-25 .
Figure 4-24. Wires ready to connect to shield Figure 4-25. Wires connected
Figure 4-26 shows how the motor and battery wires attach to the connectors
on the motor shield.
Chapter 4 61
Mechanical Assembly
Attach the sensor plate with two M3 bolts as shown in Figure 4-27; the top
plate is attached using four M3 bolts as seen in Figure 4-28.
The upper deck is bolted to four 50mm standoffs that are attached as shown
in Figure 4-29.
Chapter 4 63
Mechanical Assembly
Chapter 4 65
Mounting the IR sensors
Chapter 4 67
Next Steps
Figure 4-37. Reflectance sensor location for line follow Figure 4-38. Reflectance sensor location for line fol
ing, viewed from front lowing, alternate view
Next Steps
Chapter 5, Tutorial: Getting Started with Arduino explains how to set up and use
the development environment that will be used to upload code to the robot.
If you are already an Arduino expert, you can skip to Chapter 6, Testing the
Robot’s Basic Functions, but first, see “Installing Third-Party Libraries” (page 83)
for advice on the libraries used with the code for this book and the steps needed
to configure the RobotMotor library for the 4WD robot.
If you have the libraries installed and want run a simple test to verify that the
motors are working correctly, you can run the following sketch:
int pwm;
void setup()
{
Serial.begin(9600);
blinkNumber(8); // open port while flashing. Needed for Leonardo only
Motor_Right_Front.run(BACKWARD);
Motor_Right_Rear.run(BACKWARD);
This sketch runs the motors in opposite directions to cause the robot to rotate
clockwise for 5 seconds, then stops for 5 seconds. This will repeat until the
power is switched off.
Chapter 4 69
Next Steps
This test sketch does not use the RobotMotor library—if this test func
tions correctly but the test in Chapter 6, Testing the Robot’s Basic Func
tions does not work, the most likely cause is the configuration of the
motor library—make sure you copy the 4wd version of the library code
as described in “Installing Third-Party Libraries” (page 83).
The Arduino environment has been designed to be easy to use for beginners
who have no software or electronics experience. If you are new to Arduino, this
chapter will help you get started but you will need to consult the Arduino
online help and a good book on Arduino will be a big help (the author’s
"Arduino Cookbook” is highly recommended as reference.)
If you’re already familiar with Arduino, please feel free to skip the in
troductory material in this chapter. However, you will need to install
the libraries that are included in the download the code available from:
http://shop.oreilly.com/product/0636920028024.do. The section “In
stalling Third-Party Libraries” (page 83) has details on installing the re
quired libraries.
Arduino is best known for its hardware, but you also need software to program
that hardware. Both the hardware and the software are called “Arduino.” The
combination enables you to create projects that sense and control the physical
world. The software is free, open source, and cross-platform. The boards are
inexpensive to buy or you can build your own (the hardware designs are also
open source). In addition, there is an active and supportive Arduino commu
nity that is accessible worldwide through the Arduino forums and the wiki
(known as the Arduino Playground). The forums and the wiki offer project
development examples and solutions to problems that can provide inspiration
and assistance as you pursue your own projects.
The information in this chapter will get you started by explaining how to set
up the development environment and how to compile and run an example
sketch.
71
Hardware Required
The Blink sketch, which comes with Arduino, is used as an example sketch in
this chapter. If you have already assembled the robot and downloaded the
source code for this book, feel free to use the HelloRobot sketch described in
Chapter 6, Testing the Robot’s Basic Functions.
If you don’t have the Arduino software and driver installed on your
machine, wait until “Connecting the Arduino Board” (page 78) to plug
the Arduino into your computer.
Hardware Required
• Computer with Arduino 1.0.1 or later installed
• Leonardo (or Uno) Arduino board
• Motor Shield (see Chapter 2, Building the Electronics)
• USB cable
Arduino Software
Software programs, called sketches, are created on a computer using the Ar
duino integrated development environment (IDE). The IDE enables you to write
and edit code and convert this code into instructions that Arduino hardware
understands. The IDE also transfers those instructions to the Arduino board (a
process called uploading).
Arduino Hardware
The Arduino board is where the code you write is executed. The board can only
control and respond to electricity, so specific components are attached to it to
enable it to interact with the real world. These components can be sensors,
which convert some aspect of the physical world to electricity so that the board
can sense it, or actuators, which get electricity from the board and convert it
into something that changes the world. Examples of sensors include switches,
accelerometers, and ultrasound distance sensors. Actuators are things like
lights and LEDs, speakers, motors, and displays.
There are a variety of official boards that you can run your Arduino sketches
on and a wide range of Arduino-compatible boards produced by members of
the community.
The most popular boards contain a USB connector that is used to provide
power and connectivity for uploading your software onto the board. Figure 5-1
shows the board used for the robots in this book, the Arduino Leonardo.
You can get boards that are smaller and boards with more connections. The
Leonardo is used with these robotics projects because it is inexpensive but you
can use other boards such as the Uno if you prefer.
If you want to use an Uno board (or earlier Arduino boards), you may
need to use a s slightly higher voltage (an additional battery) to power
the robot, see Appendix D).
Add-on boards that plug into Arduino to extend hardware resources are called
shields. The robots covered in this book use a shield that controls the direction
and speed of the motors, see Figure 5-2:
Chapter 5 73
Installing the Integrated Development Environment (IDE)
Online guides for getting started with Arduino are available at http://ardui
no.cc/en/Guide/Windows for Windows, http://arduino.cc/en/Guide/MacOSX for
Mac OS X, and http://www.arduino.cc/playground/Learning/Linux for Linux.
Installing Arduino on OS X
The Arduino download for the Mac is a disk image (.dmg); double-click the file
when the download is complete. The image will mount (it will appear like a
memory stick on the desktop). Inside the disk image is the Arduino application.
Copy this to somewhere convenient—the Applications folder is a sensible
place. Double-click the application once you have copied it over (it is not a
good idea to run it from the disk image). The splash screen will appear, followed
by the main program window (Figure 5-4).
Chapter 5 75
Installing the Integrated Development Environment (IDE)
Driver Installation
To enable the Arduino development environment to communicate with the
board, the operating system needs to use the appropriate drivers for your
board.
On Windows, use the USB cable to connect your PC and the Arduino board
and wait for the Found New Hardware Wizard to appear. If you are using a
Leonardo or Uno board let the wizard attempt to find and install drivers.
If you are using an earlier board (any board that uses FTDI drivers) with Win
dows Vista or Windows 7 and are online, you can let the wizard search for drivers
and they will install automatically. On Windows XP (or if you don’t have internet
access), you should specify the location of the drivers. Use the file selector to
navigate to the FTDI USB Drivers directory, located in the directory where you
unzipped the Arduino files. When this driver has installed, the Found New
Hardware Wizard will appear again, saying a new serial port has been found.
Follow the same process as before.
On the Mac, the latest Arduino boards can be used without additional drivers.
When you first plug the board in a notification will pop up saying a new network
port has been found; you can dismiss this. If you are using earlier boards (boards
that need FTDI drivers), you will need to install driver software. There is a pack
age named FTDIUSBSerialDriver, with a range of numbers after it, inside the
Arduino installation disk image. Double-click this and the installer will take you
through the process. You will need to know an administrator password to
complete the process.
On Linux, most distributions have the driver already installed, but follow the
Linux link given in “Arduino Hardware” (page 72) for specific information for
your distribution.
Chapter 5 77
Connecting the Arduino Board
If you have a factory fresh board, an orange LED (labeled “Pin 13 LED” in
Figure 5-5) should flash on and off when the board is powered up (boards come
from the factory preloaded with software to flash the LED as a simple check
that the board is working).
If the power LED does not illuminate when the board is connected to
your computer, the board is probably not receiving power.
The Sketch Editor area is where you view and edit code for a sketch. It supports
common text editing keys such as Ctrl-F (⌘-F on a Mac) for find, Ctrl-Z (⌘-Z on
a Mac) for undo, Ctrl-C (⌘-C on a Mac) to copy highlighted text, and Ctrl-V (⌘-
V on a Mac) to paste highlighted text.
Figure 5-7 shows how to load the Blink sketch (the sketch that comes preloaded
on a new Arduino board).
After you’ve started the IDE, go to the File→Examples menu and select 1.Ba
sics→Blink, as shown in Figure 5-7. The code for blinking the built-in LED will
be displayed in the Sketch Editor window.
Chapter 5 79
Using the IDE
Before the code can be sent to the board, it needs to be converted into in
structions that can be read and executed by the Arduino controller chip; this
is called compiling. To do this, click the compile button (the top-left button
with a tick inside), or select Sketch→Verify/Compile (Ctrl-R, ⌘-R on a Mac).
You should see a message that reads “Compiling sketch...” and a progress bar
in the message area below the text editing window. After a second or two, a
message that reads “Done Compiling” will appear. The black console area will
contain the following additional message:
The exact message may differ depending on your board and Arduino version;
it is telling you the size of the sketch and the maximum size that your board
can accept.
The final message telling you the size of the sketch indicates how much pro
gram space is needed to store the controller instructions on the board. If the
size of the compiled sketch is greater than the available memory on the board,
the following error message is displayed:
If this happens, you need to make your sketch smaller to be able to put it on
the board, or get a board with higher capacity. You will not have this problem
with the Blink example sketch.
If there are errors in the code, the compiler will print one or more error mes
sages in the console window. These messages can help identify the error.
As you develop and modify a sketch, you should also consider using the
File→Save As menu option and using a different name or version number reg
ularly so that as you implement each bit, you can go back to an older version
if you need to.
/dev/tty.usbmodemXXXXXXX
/dev/cu.usbmodemXXXXXXX
Chapter 5 81
Using Tabs
/dev/tty.usbserial-XXXXXXX
/dev/cu.usbserial-XXXXXXX
Each board will have different values for XXXXXXX. Select either entry.
Click on the upload button (in Figure 5-6, it’s the second button from the left),
or choose File→Upload to I/O board (Ctrl-U, ⌘-U on a Mac).
The software will compile the code, as in “Using the IDE” (page 78). After the
software is compiled, it is uploaded to the board. If you look at your board, you
will see the LED stop flashing, and two lights (labeled as Serial LEDs in
Figure 5-5) just below the previously flashing LED should flicker for a couple
of seconds as the code uploads. The original light should then start flashing
again as the code runs.
The IDE will display an error message if the upload is not successful. Problems
are usually due to the wrong board or serial port being selected or the board
not being plugged in. The currently selected board and serial port are dis
played in the status bar at the bottom of the Arduino window
If you have trouble identifying the correct port on Windows, try unplugging
the board and then selecting Tools→Serial Port to see which COM port is no
longer on the display list. Another approach is to select the ports, one by one,
until you see the lights on the board flicker to indicate that the code is
uploading.
Using Tabs
Tabs provide a convenient way to organize code when your sketch starts to
grow. It enables you to keep functionally related code together and simplifies
sharing this code across more than one sketch.
The arrow in the upper right of Figure 5-8 points to the button which invokes
a drop-down window of tab related functions. This window displays the names
of the tabs and offers a list of commands:
• New Tab creates a new tab that (you will be prompted to name the tab)
• Rename enables you to change the name of the currently selected tab
• Delete deletes the current tab (you are asked if you are sure you want to
do that)
Each tab is a separate file and when you copy these files to other sketches you
add the tab to that sketch.
Because there are many functional modules used in this book and these are
shared across most of the sketches, tabs are used extensively. Figure 5-8 shows
the myRobot sketch, discussed in the next chapter, that uses tabs for infrared
sensor code (irSensors) and for program constants and definitions (robotDe
fines.h).
Chapter 5 83
Installing Third-Party Libraries
If the libraries provide example sketches, you can view these from the IDE
menu; click File→Examples, and the library’s examples will be under the li
brary’s name in a section between the general examples and the Arduino dis
tributed libraries example listing.
If the library examples do not appear in the Examples menu or you get a mes
sage saying “Library not found” when you try to use the library, check that the
libraries folder is in the correct place with the name spelled correctly. A library
folder named <LibraryName> (where <LibraryName> is the name for the li
brary) must contain a file named <LibraryName>.h with the same spelling and
capitalization. Check that additional files needed by the library are in the folder.
In this chapter, you will upload a test sketch to the robot that will verify that
your robot is working correctly.
Hardware Required
• The assembled robot chassis.
• Motors connected to shield (see Figure 3-25 for 2WD or Figure 4-26 for
4WD).
• Example code and libraries installed, see “Installing Third-Party Libraries”
(page 83).
• 5 AA cells inserted into the battery holder (USB does not provide sufficient
power to drive the motors).
• Reflectance sensors mounted and connected (left sensor to analog input
0, right to analog 1). You can use the stripboard wiring described in “Mak
ing a Line Sensor Mount” (page 17). But to run the edge detecting project
described in Chapter 9, you need more space between the sensors.
Figure 6-1 shows the assembled two wheel robot; Figure 6-2 shows the as
sembled four wheel robot. Figure 6-3 shows the sensor and motor connections.
85
Software Prerequisites
Figure 6-1. Two wheeled robot with reflectance sensors Figure 6-2. Four wheeled robot with reflectance sen
sors
Software Prerequisites
Although the sketch code used in this chapter is printed in the pages that
follow, you will need some libraries that are included in example code (see
“How to Contact Us” (page xv) for the URL). The sketch folders can be copied
to your Arduino sketchbook folder (if you are not familiar with the Arduino
environment, read through Chapter 5). The download files in the library folder
must be copied to your Arduino libraries folder (see “Installing Third-Party Li
braries” (page 83)).
Install the AFMotor library contained in the download zip file. This library is
modified from the one on the Adafruit site to work with the Leonardo board;
the standard Adafruit library can be used with the Uno board.
Install the RobotMotor library contained in the example code download. This
library comes configured for the two wheeled robot; if you have the four
wheeled robot will need to update the library for this robot as described in the
Note below.
If your robot uses four wheel drive, you must configure the RobotMo
tor library code by modifying the RobotMotor.h file to tell the compiler
that the library should be built for the 4WD chassis. See “Installing
Third-Party Libraries” (page 83) for details on how to do this.
A third library, named IrRemote, is also included in the download. This library
won’t be needed until Chapter 11, but copying it into your libraries folder now
will save you having to do this later.
Chapter 6 87
Load and Run helloRobot.ino
toward the DC jack) and connect your Arduino to your computer with a USB
cable. Next, upload the sketch (see Chapter 5 if you need help loading the
sketch).
// obstacles constants
const int OBST_NONE = 0; // no obstacle detected
const int OBST_LEFT_EDGE = 1; // left edge detected
const int OBST_RIGHT_EDGE = 2; // right edge detected
const int OBST_FRONT_EDGE = 3; // edge detect at both left and right sensors
// Setup runs at startup and is used configure pins and init system variables
void setup()
{
Serial.begin(9600);
blinkNumber(8); // open port while flashing. Needed for Leonardo only
motorBegin(MOTOR_LEFT);
motorBegin(MOTOR_RIGHT);
void loop()
{
// call a function when reflection blocked on left side
if(lookForObstacle(OBST_LEFT_EDGE) == true) {
calibrateRotationRate(DIR_LEFT,360); // calibrate CCW rotation
}
// as above for right sensor
if(lookForObstacle(OBST_RIGHT_EDGE) == true) {
calibrateRotationRate(DIR_RIGHT, 360); // calibrate CW rotation
}
}
/**********************
code to look for obstacles
**********************/
Chapter 6 89
Load and Run helloRobot.ino
/*************************************
functions to rotate the robot
*************************************/
// return the time in milliseconds to turn the given angle at the given speed
long rotationAngleToTime( int angle, int speed)
{
int fullRotationTime; // time to rotate 360 degrees at given speed
angle = abs(angle);
delay(1000);
blinkNumber(speed/10);
}
else
Serial.println("Invalid sensor");
delay(time);
motorStop(MOTOR_LEFT);
motorStop(MOTOR_RIGHT);
delay(2000); // two second delay between speeds
}
}
/****************************
ir reflectance sensor code
****************************/
const byte NBR_SENSORS = 3; // this version only has left and right sensors
const byte IR_SENSOR[NBR_SENSORS] = {0, 1, 2}; // analog pins for sensors
void irSensorBegin()
{
for(int sensor = 0; sensor < NBR_SENSORS; sensor++)
irSensorCalibrate(sensor);
}
Chapter 6 91
Load and Run helloRobot.ino
The sketch tests the calibration of the robot’s speed of movement. The front
sensors are used to initiate a motor test—the motors rotate the robot 360
degrees in the direction of the sensor that was triggered. If the robot is func
tioning correctly, it will execute a complete revolution at seven speeds ranging
from slowest to fastest.
To run the test, place the robot on a reflective white surface such as a large
sheet of paper. When the robot’s up and running, Arduino’s pin 13 LED will
flash once.
Another way to test is to put the robot on something that will raise the
wheels off the ground by an inch or so. This will enable the motors to
turn without the robot skittering around.
This sketch displays debugging information to the serial console. If you’d like
to view it, you’ll need to keep the USB cable plugged into your computer and
your robot; be careful, since the robot will be moving. If you’re using an Arduino
Leonardo, wait until the robot’s LED flashes to indicate it’s ready before open
ing the Arduino Serial Monitor (the Serial Monitor is the rightmost icon on the
Arduino toolbar). When the sketch starts, you should see the following in the
Arduino Serial Monitor:
Waiting for a sensor to detect blocked reflection
Swipe something dark (a small piece of matte black paper the size of a business
card works well) near one of the sensors (panel 2 seen in Figure 6-7 ). The Serial
monitor should now display the output similar to that shown in Example 6-2.
The number of lines and the values displayed will vary with different robots
but you should see multiple lines showing the direction of rotation, speed and
time in milliseconds.
Motors on the left side should spin in reverse, motors on the right should spin
forward for the indicated time in milliseconds (if the robot was on the ground,
it would rotate to the left (counter-clockwise). If you don’t see the expected
results, see “Troubleshooting” (page 98) for help.
Completing this test will verify that everything (the robot motors, power
source, Arduino and motor shield) is wired up and functioning correctly. Dou
ble check that you have completed all the building steps. Take particular care
that the battery wires to the motor shield are attached to the correct polarity.
Chapter 6 93
Load and Run helloRobot.ino
The line shown in Example 6-4 includes the library written for this book
(RobotMotor.h).
This library provides a consistent interface for motor functions in order to iso
late the higher level logic from hardware specifics. This means that you can
use the same sketch code with (almost) any motor hardware simply by chang
ing the RobotMotor library code to suit the hardware. The motor code is ex
plained in the next chapter and you can find example code to support a dif
ferent motor controller in Appendix B, Using Other Hardware with Your Robot.
The block that begins with: /***** Global Defines ****/ contains declara
tions for constants that identify: sensors, directions and obstacles. These con
stants enable you to refer to elements in your sketch using meaningful names
instead of numbers, for example this:
calibrateRotationRate(DIR_LEFT,360);
instead of this:
motorForward(0, 360);
The setup section calls functions to initialize the motor and sensor modules
(more on these later). The loop function uses the lookForObstacle function to
determine if a reflection is detected. It waits until no reflection is detected on
Chapter 6 95
About the Sketch
either sensor; the robot is not on the ground (or on a non-reflective surface).
The lookForObstacle function is checked to determine if the left or right sensor
detects a reflection, and if so, calls the calibrateRotationRate function to
rotate the robot for a short period.
The lookForObstacle function is told which obstacle to check for (the obsta
cles are identified using the defines described above). The case statement (see
http://arduino.cc/en/Reference/SwitchCase) is used to call irEdgeDetect func
tion that returns true if an object is detected on that sensor. If no object is
detected, the function returns OBST_NONE, shown in Example 6-5. See “Infrared
Reflectance Sensors” (page 134) for a detailed explanation of irEdgeDetect and
related functions.
}
isDetected[sensor] = result;
return result;
}
This function will return true if the reflection level is reduced. This is deter
mined through a call to analogRead to get a raw sensor reading that is com
pared to a detected/not detected threshold. Values greater than or equal to
the threshold are considered a loss of reflection (the voltage from the sensor
increases when the reflected light decreases , see “Infrared Reflectance Sen
sors” (page 134) for details on the reflectance sensors). The results from this test
is stored in an array named isDetected. The array can be used to recall the
sensor state of the most recent call to irSensorDetect and is used here to
suppress printing of the test result if a previous test already indicated that an
Download from Wow! eBook <www.wowebook.com>
The motor code commands the motor controller board to drive the motor
forwards, backwards or stop.
For example, the following will spin the right motor forward at a speed given
by the speed parameter (the parameter is the percentage of the maximum
speed):
motorForward(MOTOR_RIGHT, speed);
Chapter 6 97
Troubleshooting
Troubleshooting
If you are having trouble getting HelloRobot working then the first thing to do
is to put the robot down, walk away from your computer screen and have a
refreshing drink. Come back and look at things with fresh eyes and check to
see if you have things wired up and connected correctly. If it looks like the
connections are okay, then the next step is to make a list of the major symp
toms:
Compile errors
Software Errors
• The Serial Monitor is not displaying the text shown at the end of “Load and
Run helloRobot.ino” (page 88)—read through Chapter 5, Tutorial: Getting
Started with Arduino and check that you have the drivers for your board
correctly installed.
• The Serial Monitor displays the initial text but then displays errors or other
unexpected text—see Appendix C, Debugging Your Robot.
Hardware symptoms
• No LEDs on the Arduino board are lit (you may need to remove the motor
shield to check this). - This usually means that either no power is being
supplied to the board. If the power switch is on, check that the batteries
have sufficient voltage and are located correctly. Check the wiring from
the battery and switch to the shield.
• Motors don’t turn—Check that the batteries are fitted correctly (USB does
not provide enough power to drive the motors). Check the motor wiring.
You can test each motor by disconnecting the motor wires going to the
motor terminals on the shield and connecting them directly to the battery
terminals. If the motors still do not turn but the shield LED is lit, then double
check the shield soldering.
• Two of the four motors don’t turn on the 4WD robot—Have you configured
the library for 4WD?—see “Software Prerequisites” (page 86).
• Motors run but the robot does not rotate 360 degrees—the robot rotation
does not need to be exact; anything within 20 or 30 degrees is good
enough. See Chapter 7 if you do want to adjust the rotation rate.
Chapter 6 99
Making the Sketch Easy to Enhance
You can download the myRobot sketch from the book’s website but you
may want to go through these steps yourself to familiarize yourself with
the procedure for creating and using tabs in the IDE.
1. Load the HelloRobot sketch and use the IDE file menu to save as ‘myRobot’.
2. Create a tab by clicking the tab dropdown and selecting ‘New Tab’ (see
Figure 5-8). Name the tab ‘IrSensors’.
3. Click the myRobot tab, scroll down to the end of the sketch and cut all code
from the end up to the ir reflectance sensor code (Example 6-8) com
ment and paste it into the IrSensors tab.
const byte NBR_SENSORS = 3; // this version only has left and right sensors
const byte IR_SENSOR[NBR_SENSORS] = {0, 1, 2}; // analog pins for sensors
void irSensorBegin()
{
for(int sensor = 0; sensor < NBR_SENSORS; sensor++)
irSensorCalibrate(sensor);
}
{
boolean result = false; // default value
int value = analogRead(IR_SENSOR[sensor]); // get IR light level
if( value <= irSensorReflect[sensor]) {
result = true; // object detected (lower value means more reflection)
if( isDetected[sensor] == false) { // only print on initial detection
Serial.print(locationString[sensor]);
Serial.println(" object detected");
}
}
isDetected[sensor] = result;
return result;
}
The myRobotOk example sketch provided in the book download code shows
the code after the code is moved into the tabs.
Global Definitions
Definitions that need to be accessed across multiple process will automatically make all of the functions
modules are called ‘global’ definitions. These are gen in each tab accessible throughout the sketch, con
erally stored in files called ‘header files’ (or ‘headers'). stant definitions should be explicitly included at the
These files typically have a file extension of .h and top of the main tab as follows:
the file containing these global definitions is here // include the global defines
called robotDefines.h. Although the Arduino build #include "robotDefines.h"
The final step in restructuring the sketch is to move the constant definitions
at the top of the sketch into a separate tab. These constants are used by a
number of different modules and collecting these together makes it easier to
ensure that the values are accessible by all the modules:
Chapter 6 101
Making the Sketch Easy to Enhance
2. From the top of the myRobot tab, move the defines starting from:
/**** Global Defines ****/
// obstacles constants
const int OBST_NONE = 0; // no obstacle detected
const int OBST_LEFT_EDGE = 1; // left edge detected
const int OBST_RIGHT_EDGE = 2; // right edge detected
const int OBST_FRONT_EDGE = 3; // edge detect at both left and right sensors
This chapter covers the principles of robot motor control that apply to both
two wheeled and four wheeled platforms. The motor controller hardware is
explained, as is the code used to make this functionality accessible to the
sketches. The second half of this chapter (“Software Architecture for Robot
Mobility” (page 119)) describes software modules that frees the sketch logic from
a dependency on any specific motor hardware. All sketches use the library
named RobotMotor that provides a consistent interface to the hardware spe
cific motor system. An optional software module named Move provides high
level functions to move the robot that simplifies the code in the more complex
sketches that follow in chapters to come.
Hardware Required
• This chapter uses the AFMotor shield described in Chapter 2.
103
Types of Motors
Types of Motors
Brushed DC Motors, such as the ones used in the two wheeled and four
wheeled platforms (see Figure 7-2) are the most common type used with Ar
duino robots. These have two leads connected to brushes (contacts) that con
trol the magnetic field of the coils that drive the motor core (armature). Motor
direction can be reversed by reversing the polarity of the power source. These
motors typically rotate too fast to directly drive the robot wheels or tracks, so
gear reduction is used to reduce speed and increase torque.
Other kinds of motors can be used to power robots; here are some you may
come across:
Continuous rotation servo
These motors are used on smaller robots. They have the advantage that
the motor controller, motor, and gearbox are all mounted in the same
housing, so they are easy to attach to a robot and can be driven directly
from Arduino pins. However they usually have less torque than typical
stand-alone brushed motors.
Brushless motors
These have increased torque and efficiency compared to brushed motors
but they are more expensive and complex to control. However, prices are
dropping and they are a good choice for a larger robot.
Chapter 7 105
Motor Controllers
Stepper motors
These motors are used on large robots when precise control is required.
These motors typically require 12 or 24 volts so they are not often used on
small battery operated robots. However they may become more popular
due to the recent availability of low cost 5 volt steppers.
Motor Controllers
The two wheel and four wheel platforms use small DC motors that are con
trolled using an H-Bridge. The H-Bridge featured in this book is part of the
AFMotor shield from Adafruit Industries. This can drive up to four motors in
dependently, although only two are used with the two wheeled robot. This
shield requires a library for interfacing sketch code with the hardware; this
library is included with the code download for this book (see “How to Contact
Us” (page xv)).
The name H-bridge derives from the characteristic shape that you can
see in these figures.
To enable the sketches to work with other H-Bridge hardware, a library named
RobotMotor is provided with the example code that provides generic control
functions that the library translates into the specific commands for the AFMo
tor shield or another shield if you have use different hardware. see “Software
Architecture for Robot Mobility” (page 119)
This library is modified from the one on the Adafruit site to work with
the Leonardo board. The standard Adafruit library can be used with
the Uno board). See “Installing Third-Party Libraries” (page 83) if you
need directions for installing a library. If you followed along with
Chapter 6, you will already have the library installed.
The following diagrams explain how an H-bridge works and the RobotMotor
functions used to control the motors:
Chapter 7 107
Motor Controllers
Figure 7-6. HBridge with Motor Brake Not all H-bridges, including the
Adafruit library, support this
mode. The RobotMotor library will
call motorStop when the motor
Brake function is called.
Chapter 7 109
Controlling Motor Speed
All the code in this book uses a percent value to refer to speed. This value is
the percentage of power given to the motors (technically, its called the duty
cycle). Percent speed is used instead of the raw PWM value to isolate the sketch
logic from the low level motor code. Different motor hardware use various
techniques for controlling motor rotation speed. For example, continuous ro
tation servos can use servo angle (where 90 is stop and 0 actually rotates the
motor at full reverse speed), and stepper motors don’t use PWM to control
speed. Because the high level logic always uses percent and this is mapped to
the range needed by the hardware in the motor interface code, the same high
level code can be used with other hardware simply by swapping the appro
priate motor interface module.
motors[motor].setSpeed(pwm) ;
}
Bear in mind that the speed percentage is actually controlling motor power
and this is usually not directly proportional to speed, particularly at low power.
The amount of power required to get the robot moving is dependent on the
motor, gearbox, battery voltage, robot weight and the surface the robot is on.
The method to calibrate the robot will be described shortly, but first, here is
an explanation of how the software handles robot speed control.
The code fragment shown in Example 7-2 contains the constants that are used
to calculate the appropriate delays for different speeds to rotate the robot.
rotationTime stores the duration for a 360 degree rotation for all practical
speeds. Speeds less than MIN_SPEED (40%) do not provide sufficient power to
overcome friction in the drive system.
Example 72. Constants for the delays needed to rotate the robot, from RobotMotor.cpp
const int MIN_SPEED = 40; // first table entry is 40% speed
const int SPEED_TABLE_INTERVAL = 10; // each table entry is 10% faster speed
const int NBR_SPEEDS = 1 + (100 - MIN_SPEED)/ SPEED_TABLE_INTERVAL;
int speedTable[NBR_SPEEDS] = {40, 50, 60, 70, 80, 90, 100}; // speeds
int rotationTime[NBR_SPEEDS] = {5500, 3300, 2400, 2000, 1750, 1550, 1150}; // time
The table holds durations in milliseconds for speeds in intervals of 10%. The
values were derived from experimentation with the two wheeled robot using
a sketch named myRobotCalibrateRotation sketch and noting the angles for
each of the speeds as shown in Figure 7-8.
Figure 7-8. Angle that the robot rotates for one second burst at each of the supported speeds
By calculating the angle as a fraction of 360 degrees, the time to rotate the
robot one complete revolution can be determined for each speed ( the calcu
lation for the value in milliseconds is: 1000*(360/angle).
Chapter 7 111
Controlling Motor Speed
Figure 7-9 shows the actual times for the 2WD robot.
The relationship between rotation angle and speed percentage is not linear,
so interpolation is used to calculate the duration to produce a full rotation for
any speed (as long as it is as fast or faster than the minimum speed).
Example 7-3 shows the code that uses the table with times based on the data
shown in Figure 7-9.
The RobotMotor library has the code to determine how much time the robot
requires to rotate 360 degrees. This will differ between the two and four
wheeled chassis and vary as the motor speed varies. Example 7-3 shows the
values used in the RobotMotor.cpp code for the 2WD chassis.
int speedTable[NBR_SPEEDS] = {40, 50, 60, 70, 80, 90, 100}; // speeds
int rotationTime[NBR_SPEEDS] = {5500, 3300, 2400, 2000, 1750, 1550, 1150}; // time
Note that there are fewer entries in the tables for the 4WD robot because this
chassis requires a higher speed to get going. “Calibrating Rotation and Track
ing” (page 116) explains how to adjust the tables to suit your robot.
The table entries assume speed intervals of 10% so the value for MIN_SPEED
should be multiple of 10. There must be one rotation time per speed so if you
increase MIN_SPEED by 10 for example, you will also need to remove the first
element in both speedTable and rotationTime.
The code in RobotMotor.cpp that uses the data in the rotationTime table is the
same for both chassis (see Example 7-5).
Chapter 7 113
Controlling Motor Speed
Modifying a Library
You know how to modify an Arduino sketch—just 2. Locate the libraries directory inside, and then
edit it in the Arduino IDE. But modifying a library is a open the directory named RobotMotor.
bit more involved. You need to go into the sketch
folder, open up the library directory, and find the file. 3. Right-click (or Control-click on the Mac) the
Then you need to open it in a text editor. Here’s how RobotMotor.h file, and open it with a plain text
to modify the RobotMotor.h file to use the 4WD chas editor. On Windows, you should use Notepad.
sis. On the Mac, you can use TextEdit. On Linux,
use your favorite plain text editor.
First, find the sketchbook location. Go to Arduino’s
preferences (File→Preferences on Windows or Linux, 4. Change #define CHASSIS_2WD to #define
Arduino→Preferences on Mac). Under Sketchbook CHASSIS_4WD and save the file.
Location, you’ll find the name of the directory that
contains your sketches and libraries. Next: Although you need to quit and restart the Arduino
IDE when you install a new library, you don’t need to
1. Open the sketchbook folder in the Finder do so each time you modify a library.
(Mac) or Explorer (Windows).
angle = abs(angle);
This code determines the index into the speedTable array that is closest to (but
not greater than) the desired speed. This index is stored in the variable t0. The
interpolated time will be between this value and the next index (t1), with the
rotation time calculated using the ratio of the rotationTime value between t0
and t1 in the same proportion as the desired speed in the speedTable. It may
be easier to understand how this works by consulting Figure 7-10.
Chapter 7 115
Controlling Motor Speed
For example, for a speed of 65%, which is halfway between the values for 60%
and 70%, the time associated with 65% speed will be 2200, which is half way
between 2400 (the 60% speed value) and 2000 (the 70% speed value). A speed
of 62.5% is 1/4 of the range between the table entries (60 and 70), so the time
will be 1/4 of the range between the speeds for that range (2400 and 2000,
which is 2300 milliseconds). The map function is used to calculate this propor
tional value:
fullRotationTime = map(speed,speedTable[index],speedTable[index+1],t0,t1);
To calculate the time to rotate an angle other than 360 degrees, the map func
tion is used again:
long result = map(angle, 0,360, 0, fullRotationTime);
// Setup runs at startup and is used configure pins and init system variables
void setup()
{
motorBegin(MOTOR_LEFT);
motorBegin(MOTOR_RIGHT);
calibrateSpeed();
}
void loop()
{
}
void calibrateSpeed()
{
for(int speed = MIN_SPEED; speed <= 100; speed += 10)
{
// rotate robot left for 1 second
motorReverse(MOTOR_LEFT, speed);
motorForward(MOTOR_RIGHT, speed);
delay(1000); // delay 1 second
motorStop(MOTOR_LEFT);
motorStop(MOTOR_RIGHT);
Running this sketch will rotate the robot left (CCW) for one second, stop for
one second, then rotate the robot right (CW) for a second. If you mark the angle
of the robot after each CCW rotation, you can calculate how much longer or
shorter it would take the robot to turn 360 degrees for each speed. If your robot
does not rotate at all at the slower speeds, note the lowest speed that the robot
does move and set MIN_SPEED in RobotMotor.cpp to this value.
The RobotMotor library also supports the ability to adjust the relative power
to each motor in order to prevent the robot drifting off a straight course due
to differences in performance between the left and right motor(s). If your robot
does not track a straight line when moving forward or backward, you can
modify the motor library (see next section) to correct this.
The RobotMotor.cpp library file contains a constant that can be adjusted to
correct drift:
const int differential = 0; // % faster left motor turns compared to right
If your robot drifts, adjust the constant differential to compensate. Set the
value using trial and error, positive values nudge the robot to the right, nega
tive values to the left. The correct value will be the difference in speed between
the motors in percent. The drift will vary somewhat with motor speed so best
to set this when testing with the robot running at a speed midway between
the minimum and maximum speeds.
Chapter 7 117
Controlling Motor Speed
Here is a modified version of the previous sketch that will drive the robot in a
straight line when the differential constant is adjusted to correct drift. You can
make differential a negative number if your right motor turns faster than
your left (the robot drifts to the left).
const int TEST_SPEED = MIN_SPEED + 10; // Typical speed to run the robot
const int differential = 0; // % faster left motor turns compared to right
// Setup runs at startup and is used configure pins and init system variables
void setup()
{
motorBegin(MOTOR_LEFT);
motorBegin(MOTOR_RIGHT);
calibrateDrift();
}
void loop()
{
}
void calibrateDrift()
{
motorForward(MOTOR_LEFT, TEST_SPEED - differential);
motorForward(MOTOR_RIGHT, TEST_SPEED);
delay(2000); // delay 2 second
motorStop(MOTOR_LEFT);
motorStop(MOTOR_RIGHT);
}
If the robot drifts the right when running this sketch, try setting differen
tial to 2. If this overcorrects (the robot now drifts to the left), decrease the
differential value. If you need more correction, increase the value. If the robot
was drifting to the left, use negative values of differential to compensate. You
should be able to get the robot running more or less straight after a little trial
and error. Don’t worry about minor deviations which are caused by small dif
ferences in the efficiency of the motors at varying battery levels.
After you have settled in a value for differential you must change this in the
RobotMotor.cpp file. Open this file with a text editor and (see “Modifying a
Library” (page 114)) and find the declaration towards the beginning of the file:
const int differential = 0; // % faster left motor turns compared to right
Replace 0 with the value determined from the calibration sketch and save the
file.
Chapter 7 119
Software Architecture for Robot Mobility
Example 7-9 shows the source code for the RobotMotor library’s .cpp file. The
header file RobotMotor.h (Example 7-8) defines the constants for the left and
right motors and declares the functions for speed and direction.
Chapter 7 121
Software Architecture for Robot Mobility
int speedTable[NBR_SPEEDS] = {40, 50, 60, 70, 80, 90, 100}; // speeds
int rotationTime[NBR_SPEEDS] = {5500, 3300, 2400, 2000, 1750, 1550, 1150}; // time
AF_DCMotor motors[] = {
AF_DCMotor(1, MOTOR12_1KHZ), // left is Motor #1
AF_DCMotor(2, MOTOR12_1KHZ) // right is Motor #2 };
// constants for 4 wheeled robot
#elif defined CHASSIS_4WD
const int MIN_SPEED = 60; // first table entry is 60% speed
const int SPEED_TABLE_INTERVAL = 10; // each table entry is 10% faster speed
const int NBR_SPEEDS = 1 + (100 - MIN_SPEED)/ SPEED_TABLE_INTERVAL;
motors[motor].setSpeed(pwm) ;
#if defined CHASSIS_4WD
motors[motor+2].setSpeed(pwm) ;
#endif
}
void motorForward(int motor, int speed)
{
motorSetSpeed(motor, speed);
motors[motor].run(FORWARD);
#if defined CHASSIS_4WD
motors[motor+2].run(FORWARD);
#endif
}
void motorReverse(int motor, int speed)
{
motorSetSpeed(motor, speed);
motors[motor].run(BACKWARD);
#if defined CHASSIS_4WD
motors[motor+2].run(BACKWARD);
#endif
}
void motorStop(int motor)
{
// todo set speed to 0 ???
motors[motor].run(RELEASE); // stopped
#if defined CHASSIS_4WD
motors[motor+2].run(RELEASE);
#endif
}
void motorBrake(int motor)
{
motors[motor].run(BRAKE); // stopped
#if defined CHASSIS_4WD
motors[motor+2].run(BRAKE);
#endif
}
The RobotMotor.cpp file contains code for both the two wheel and four wheel
chassis. Conditional compilation is used to build the library for the appropriate
version. #if defined CHASSIS_2WD and #if defined CHASSIS_4WD are checks
to see which chassis has been defined in the RobotMotor.h file. code between
#if defined CHASSIS_2WD and #endif will only be compiled if CHASSIS_2WD is
defined in RobotMotor.h. See “Installing Third-Party Libraries” (page 83) for
more details on changing the define for the four wheel chassis.
This library can be modified to support different hardware. For example, see
Appendix B for the code to use the Ardumoto shield (but note that Ardumoto
only supports two motors so is not suitable for the four wheeled robot).
Chapter 7 123
Functions to Encapsulate Robot Movements
Enhance” (page 99) but uses the rotation functions in the Move tab to drive
the robot. Using the higher level functions to drive the robot not only simplifies
your code, it isolates the sketch logic from the hardware specific motor code.
The sketches in all of the following chapters control robot movement through
the functions in the Move tab.
void moveBegin()
{
motorBegin(MOTOR_LEFT);
motorBegin(MOTOR_RIGHT);
moveStop();
}
void moveLeft()
{
motorForward(MOTOR_LEFT, 0);
motorForward(MOTOR_RIGHT, moveSpeed);
changeMoveState(MOV_LEFT);
}
void moveRight()
{
motorForward(MOTOR_LEFT, moveSpeed);
motorForward(MOTOR_RIGHT, 0);
changeMoveState(MOV_RIGHT);
}
void moveStop()
{
motorStop(MOTOR_LEFT);
motorStop(MOTOR_RIGHT);
changeMoveState(MOV_STOP);
}
void moveBrake()
{
motorBrake(MOTOR_LEFT);
motorBrake(MOTOR_RIGHT);
changeMoveState(MOV_STOP);
}
void moveBackward()
{
motorReverse(MOTOR_LEFT, moveSpeed);
motorReverse(MOTOR_RIGHT, moveSpeed);
changeMoveState(MOV_BACK);
}
void moveForward()
{
motorForward(MOTOR_LEFT, moveSpeed);
motorForward(MOTOR_RIGHT, moveSpeed);
changeMoveState(MOV_FORWARD);
}
The code provides functions that combine the individual motor commands
described in “Motor Controllers” (page 106). For example, the moveForward
function calls the individual functions to rotate the left and right motors in the
Chapter 7 125
Functions to Encapsulate Robot Movements
direction that moves the robot forward. The speed to move is set by the move
SetSpeed function. moveSetSpeed commands the motors to run at the desired
speed and stores the speed value so the robot can resume running at the last
set speed following an evasive action needed to avoid obstacles.
int moveGetState()
{
return moveState;
}
If you are unfamiliar with enum (enumerated lists), see “Code Style
(About the Code)” (page xii) in Preface or an online C or C++ reference.
To assist debugging, each state has an associated text label that can be printed
to the serial monitor to show what the robot should be doing.
const char* states[] = {"Left", "Right", "Forward",
"Back", "Rotate", "Stop"};
The move state defines are located at the end of the robotDefines.h tab.
Chapter 7 127
Functions to Encapsulate Robot Movements
// return the time in milliseconds to turn the given angle at the given speed
long rotationAngleToTime( int angle, int speed)
{
int fullRotationTime; // time to rotate 360 degrees at given speed
angle = abs(angle);
Serial.print(locationString[direction]);
Serial.print(": rotate "); Serial.print(angle);
Serial.print(" degrees at speed "); Serial.print(speed);
Serial.print(" for "); Serial.print(time);
Serial.println("ms");
delay(time);
motorStop(MOTOR_LEFT);
motorStop(MOTOR_RIGHT);
delay(2000); // two second delay between speeds
}
}
Chapter 7 129
Functions to Encapsulate Robot Movements
The moveRotate function will rotate a robot by the given angle. Negative angles
turn counter clockwise, positive angles turn clockwise. Rotation is achieved by
running the motors in opposite directions (see “How Robots Move” (page 5)).
//moves in the given direction at the current speed for the given duration in milliseconds
void timedMove(int direction, int duration)
{
Serial.print("Timed move ");
if(direction == MOV_FORWARD) {
Serial.println("forward");
moveForward();
}
else if(direction == MOV_BACK) {
Serial.println("back");
moveBackward();
}
else
Serial.println("?");
movingDelay(duration);
moveStop();
}
}
}
}
}
Chapter 7 131
8
Tutorial: Introduction to
Sensors
Sensor information can be used by your robot to navigate and interact with
its environment. Sensors report on the world around them; measuring light,
distance, sound, movement, direction, temperature, pressure, or location. This
chapter describes how common sensors used with two wheeled and four
wheeled platforms work.
The first half of this chapter covers the primary sensors used in the chapters
that follow: IR reflective sensors and SONAR distance sensors. These are used
to determine if an object is near the robot. Reflective sensors detect nearby
objects and are used for line following and edge detection (determining if the
robot is near the edge of the surface it is moving on, such as the edge of a
table). Distance sensors are used to determine the distance to objects up to
ten feet away from the robot. The second half of the chapter covers other types
of sensors you can add to enable the robot to respond to distance, sound,
movement, or other stimuli. You should also have a look at Appendix D, Power
Sources which describes a very useful aspect to sense, the robot’s battery volt
age.
Hardware Discussed
QTR-1A reflectance sensors
Two are used for edge detection, but a third is required for line following.
Additional sensors are available from many internet shops that stock robot
parts, or direct from the manufacturer: http://www.pololu.com/catalog/
product/958/
SONAR Distance Sensor
One is used to measure the distance to obstacles (Maker Shed product
code MKPX5).
133
Software
Software
The chapter contains background information on sensors that will be added
to the robot in later chapters. The reflectance sensor code is from the sketches
introduced in Chapter 6, Testing the Robot’s Basic Functions. The Ping (Sonar
distance sensor) hardware and software is covered in Chapter 10, Autonomous
Movement.
Sensor constants determine which sensor to use: SENSE_IR_LEFT for the left
sensor, SENSE_IR_RIGHT for the right (these constants are defined in robotDe
fines.h (see “Making the Sketch Easy to Enhance” (page 99)). The irSensor
Detect function uses the sensor constant to retrieve the analog pin number
stored in the IR_SENSOR array. If the analogRead value is less than a predeter
mined threshold, the function returns true indicating that a reflection has
been detected. These functions use arrays instead of simple variables to store
pins and thresholds because arrays make it easy to extend the code to support
any number of sensors. To add a sensor, increase the NBR_SENSORS constant
and add the sensors pin number to the list of pins in the IR_SENSOR array.
Chapter 8 135
Infrared Reflectance Sensors
ing downwards, no reflection from the surface is detected because a dark ob
ject is blocking the reflection or the nearest surface—probably the floor—is
many inches away! This effect is used in the examples from Chapter 6 to detect
when you’ve placed a dark object under the sensor.
}
isDetected[sensor] = result;
return result;
}
The sensors need to be calibrated to take ambient light into account. Reflec
tance sensors respond to sunlight and artificial light so a threshold is measured
with no object near the sensor. Levels above this threshold mean the light level
is above ambient, which indicates that a nearby object is reflecting the IR light
from the sensor. Ambient light calibration is done using the code shown in
Example 8-3.
This loads the ambient light level into the variable ambient, calculates levels
for reflectance detection (stored in the irSensorReflect array) and levels for
edge detection (stored in the iresensorEdge array). The constant irReflect
Threshold is the percentage difference in light to detect a reflecting obstacle.
The constant iredgeThreshold is the percent difference to detect an edge. The
default values for these thresholds are 10% for reflection and 90% for edge
detection.
Here is an example assuming the ambient value from analogRead was 1000
with irReflectThreshold equal to 10 :
(1000 * 90) / 100 =
90000 / 100 =
900
In this example, if the ambient reading was 1000, the irSensorReflect’s thresh
old reading for object detection is 900, which is 10% below the ambient read
ing.
Chapter 8 137
Sonar Distance Sensors
The speed of sound is 340 meters per second, which means it takes 29 micro
seconds for sound to travel 1 centimeter (the reciprocal of 340 metres per
second). To derive the distance in cm, the duration is divided by 29. The dura
tion is the time for the sum of outgoing and reflected pulses so the distance
to the object is microseconds / 29 / 2.
The pulse duration is measured using the Arduino pulseIn function. This
returns the a pulse duration in microseconds, see http://arduino.cc/en/Refer
ence/pulseIn.
Example 8-4 shows the code that uses the Ping sensor to return the distance
in inches. You’ll see this code in action in Chapter 10.
pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH, 20000); // if a pulse does not arrive
// in 20 ms then the ping sensor
// is not connected
if(duration >=20000)
return 0;
// The ping travels out and back, so to find the distance of the
// object we take half of the distance travelled.
return microseconds / 29 / 2;
}
Chapter 8 139
Maxbotix EZ1 Sonar Distance Sensor
The version using pulse width (ezDistancePW) will wait for one second before
giving up if no return pulse is detected (for example, if the sensor is discon
nected). You can optionally set the maximum time to wait for pulseIn; the
following example sets the timeout to the duration needed for a pulse to travel
the maximum distance detectable by the sensor:
int value = pulseIn(pin, HIGH, MAX_DISTANCE * 147L * 2);
// pulseIn with timeout
You can use the analog input version (ezDistanceAN), if you have a spare analog
input pin, but if you only have digital pins free, then use the pulse width code
(ezDistancePW). The analog version takes only as long as needed to measure
the voltage so does not need a timeout.
You can find more information on this sensor on the manufacturers web page:
http://www.maxbotix.com/Ultrasonic_Sensors/MB1010.htm .
int cm = mvToDistance(mV);
return cm;
}
Chapter 8 141
Proximity Sensor
You can find lots more information on this sensor here: http://www.societyo
frobots.com/sensors_sharpirrange.shtml .
Proximity Sensor
A PIR (Passive Infrared) sensor can be used to activate your robot when it de
tects the presence of a nearby person, or even a dog or cat. The sensor acts
like a switch that sends a HIGH signal to an Arduino pin when motion is detected
(they work by detecting changes in the heat radiated from people or pets).
Figure 8-5 shows the sensor connected to analog pin 5, but you can use any
spare pin, such as A4 instead of A5.
The following loop code will spin the robot when movement is detected. If you
want your robot to do this, replace the loop function in the myRobot sketch
from “Making the Sketch Easy to Enhance” (page 99) with the code shown in
Example 8-7.
Sound Sensor
You can use a sound sensor to start or stop your robot in response to sound,
for example a hand clap or whistle. You will need a microphone with an am
plifier, for example, the BOB-09964 breakout board from SparkFun. Figure 8-6
shows the board connected to analog pin 4.
Chapter 8 143
Sound Sensor
The code that follows is the main tab from the myRobotSound sketch available
in the download for this book. Noise level above a threshold will drive the robot
forward. The robot stops when the level drops below the threshold. If you need
to change the sensitivity, experiment with higher or lower values for the
threshold. Example 8-8 shows the code for the main tab.
***********************************************************/
const int middleValue = 512; //the middle of the range of analog values
const int numberOfSamples = 128; //how many readings will be taken each time
const int threshold=400; //at what level the robot will move
// Setup runs at startup and is used configure pins and init system variables
void setup()
{
Serial.begin(9600);
blinkNumber(8); // open port while flashing. Needed for Leonardo only
motorBegin(MOTOR_LEFT);
motorBegin(MOTOR_RIGHT);
}
void loop()
{
int level = getSoundLevel();
if (level > threshold) //is level more than the threshold ?
{
motorForward(MOTOR_LEFT, speed);
motorForward(MOTOR_RIGHT, speed);
}else
{
motorStop(MOTOR_LEFT);
motorStop(MOTOR_RIGHT);
}
}
int getSoundLevel()
{
long sumOfSquares = 0;
for (int i=0; i<numberOfSamples; i++) { //take many readings and average them
sample = analogRead(analogInPin); //take a reading
signal = (sample - middleValue); //work out its offset from the center
Chapter 8 145
Arduino Cookbook
return runningAverage;
}
See the Arduino Cookbook if you want a detailed description of how this code
works.
Arduino Cookbook
Download from Wow! eBook <www.wowebook.com>
For descriptions of how to use lots of additional sensors with Arduino, see:
Arduino Cookbook by Michael Margolis (O’Reilly).
This chapter covers techniques that enable your robot to use sensors to gain
awareness of its environment. Using reflectance sensors, the robot will gain
the ability to follow lines or to avoid falling off the edge of the surface it is on.
Information from the sensors is abstracted so that the robot logic has a single
consistent interface and can easily be enhanced to support other sensors. The
physical mounting of the sensors varies with different platforms: see Chapter 4,
Building the Four-Wheeled Mobile Platform if you have the 4WD chassis, Chap
ter 3, Building the Two-Wheeled Mobile Platform if you have the 2WD chassis.
Hardware Required
• Two reflectance sensors are used for edge detection and a third is needed
for line following. Although you can use the stripboard mount (for the
three line following sensors) discussed in Chapter 2 to experiment with
edge detection, the robot will perform the edge detection task best with
the sensors further apart (the stripboard approach is best for line follow
ing). If the sensors are close together, the robot can have difficulty deter
mining the best angle to turn when an edge is encountered.
147
Sketches Used in This Chapter
void lookBegin()
{
irSensorBegin(); // initialize sensors
}
// function to check if robot can continue moving when taking evasive action
// returns true if robot is not blocked when moving to avoid obstacles
// this 'placeholder' version always returns true
boolean checkMovement()
{
return true;
}
Chapter 9 149
Edge Detection
called with relevant sensor and this will return true if an object is detected on
that sensor. If no object is detected, the function returns OBST_NONE. The look
functionality can be expanded by adding code to the case statement and call
ing appropriate sensor functions, as you will see later in this chapter.
But first, let’s use the existing functionality to give the robot the ability to follow
lines and detect edges.
Edge Detection
Edge detection is one of the easier behaviors to understand and program. The
robot moves until it encounters an edge; it should then change direction to
avoid moving over the edge. Edges are detected by using reflectance sensors
(see: Chapter 8, Tutorial: Introduction to Sensors). Typically, the edge is an area
that does not reflect, for example the edge of a table.
In the sketch that follows, the robot will remain within a reflective surface (for
example, a large white sheet of paper) that is bounded by a black line. Black
electrical tape (3/4 inch or wider) works well but a black line of similar width
drawn with magic marker or paint can also work as the ‘edge’. To avoid dam
aging your robot, an actual table is not recommended for early experiments
until you are sure you have everything working correctly.
Figure 9-2 shows how the robot responds to moving over an edge. In panel1,
the sensors do not detect an edge so the robot moves forward. In panel 2, the
left sensor moves off the reflective surface so the robot stops and rotates 120
degrees. In panel 3, the robot completes its rotation; panel 4, shows the robot
moving forward again.
Chapter 9 151
Edge Detection
/// Setup runs at startup and is used configure pins and init system variables
void setup()
{
Serial.begin(9600);
blinkNumber(8); // open port while flashing. Needed for Leonardo only
void loop()
{
/// code for roaming around and avoiding obstacles
if( lookForObstacle(OBST_FRONT_EDGE) == true)
{
Serial.println("both sensors detected edge");
timedMove(MOV_BACK, 300);
moveRotate(120);
while(lookForObstacle(OBST_FRONT_EDGE) == true )
moveStop(); // stop motors if still over cliff
}
else if(lookForObstacle(OBST_LEFT_EDGE) == true)
{
Serial.println("left sensor detected edge");
timedMove(MOV_BACK, 100);
moveRotate(30);
}
else if(lookForObstacle(OBST_RIGHT_EDGE) == true)
{
Serial.println("right sensor detected edge");
timedMove(MOV_BACK, 100);
moveRotate(-30);
}
else
{
moveSetSpeed(MIN_SPEED);
moveForward();
}
}
The code for this sketch is derived from the myRobotMove sketch discussed
in Chapter 7, Controlling Speed and Direction. You can download the example
code, locate myRobotEdge, open the sketch and upload it to the robot. Or you
can derive the sketch yourself:
1. Open the myRobotMove sketch in the example code and do a Save As and
name it myRobotEdge.
2. Create the Look tab.
3. Locate and move the two functions at the end of the main tab starting
from the comment “code to look for obstacles” into the Look tab. This code
is listed in the section: “The Look Code” (page 149).
4. Replace the main sketch code with the code listed here: Example 9-2.
5. Compile and upload the code
Place the robot within the bounded surface and switch the power on (the robot
calibrates the sensors after it is switched on so all the sensors should be over
the reflective area). After a short delay the robot will move forward until it
detects a non-reflective edge.
The loop code checks if an edge is detected directly ahead with both sensors
(OBST_FRONT_EDGE), or on the left (OBST_LEFT_EDGE) or right (OBST_RIGHT_EDGE).
If the edge was ahead, the robot backs away for 0.3 seconds, rotates 120 de
grees and then moves forward again. If the edge was to the side, the robot
turns 30 degrees away from that side and then moves forward. Feel free to
experiment with the angles to get a behaviour that suits the area you have
defined for containing your robot.
Chapter 9 153
Line Following
Line Following
Line following is a classic task for a robot. The robot uses sensors to determine
its position in relation to a line and follows this line by moving to keep its
sensors centered above the line. Figure 9-3 shows a robot moving around a
track marked with a black line on a white surface.
Download from Wow! eBook <www.wowebook.com>
In Panel 1, the robot is approaching a corner but is still centered over the line
- the motors are both running at the same speed (indicated by the equal length
arrows), and the robot moves straight ahead. The robot has reached the left
hand curve in panel 2—the right motor speed is increased, the left slowed to
turn the robot to the right. Panel 3 shows the robot completing the turn. In
Panel 4, the robot is about to reach a curve to the left where it will continue to
adjust motor speeds to keep the sensors over the line.
The illustrations that follow show what happens in more detail. Figure 9-4
shows the location of the sensor with respect to the line when the robot is
centered. The left and right sensors are above the reflective surface. Lots of
light will reflect back to the sensor and the analogRead values are low. The
center sensor is above the black line so has little reflected light, causing the
reading to be high. The difference in readings between left and right indicates
drift and is close to zero so both motors will be driven at the same speed—the
robot moves straight ahead. You can read about how to display sketch data in
real time in “Seeing Sketch Data” (page 160).
Figure 9-5 shows the robot to the left of the line because the line is curving to
the right. The left sensor detects maximum reflection (the analogRead value is
low). As the center sensor moves towards the edge of the line, the reflection
increases (decreasing the analogRead value). The right sensor moves towards
the line so its reading increases. The drift (the difference between the left and
right) is positive so the left motor speeds up and the right motor slows down
—the robot turns to the right.
Chapter 9 155
Line Following
Figure 9-6 shows the robot to the right of the line because the line is curving
to the left. The right sensor detects maximum reflection (the analogRead value
is low). The reading from the center sensor increases as it moves towards the
edge of the line. The left sensor moves towards the line so its reading increases.
The drift is negative so the left motor slows down and the right motor speeds
up—the robot turns to the left.
For the robot to successfully follow a curvy line, the movement must be re
sponsive enough to make sharp turns but not so responsive that it zigs and
zags its way along even straight lines. Tuning the software to get this just right
requires experimentation and patience. The code that follows uses the differ
ence value between the left and right sensors to adjust the differential motor
speed. The preceding figures display the relative signal levels from the sensors
and the difference value is indicated as ‘Drift’. Sensitivity is controlled by map
ping the drift value to the actual motor differential speed.
Example 9-3 shows the line sense code that calculates the drift value (you’ll
see it again in a moment when you see the complete listing for the sketch):
The drift and desired speed are passed to the lineFollow function to drive the
robot. To adjust the motor’s sensitivity, drift is divided by a ‘damping’ factor -
the higher the factor, the less sensitive to drift. Decrease the damping if you
need to make the robot more sensitive, for example, if it is not turning fast
enough to follow sharp bends. Increase the damping if the robot is unneces
sarily zig-zagging on straight lines. The drift value is subtracted from the speed
for the left motor and added to the speed of the right motor to provide a
differential speed proportional to drift. The Arduino constrain function is used
to ensure the values remain within the valid range for speed (0 to 100 %).
Depending on the radius of your bends, you may not be able to completely
eliminate the zig-zags.
int lineFollow(int drift, int speed)
{
int leftSpeed = constrain(speed - (drift / damping), 0, 100);
int rightSpeed = constrain(speed + (drift / damping), 0, 100);
motorForward(MOTOR_LEFT, leftSpeed);
motorForward(MOTOR_RIGHT, rightSpeed);
}
Example 94. Complete listing for code in the myRobotLine main tab
/******************************************************************************
myRobotLine.ino
Chapter 9 157
Line Following
int speed = MIN_SPEED; // speed in percent when moving along a straight line
/// Setup runs at startup and is used configure pins and init system variables
void setup()
{
Serial.begin(9600);
blinkNumber(8); // open port while flashing. Needed for Leonardo only
void loop()
{
int drift = lineSense();
lineFollow(drift, speed);
}
/****************************
Line Sensor code
****************************/
void lineSenseBegin()
{
motorForward(MOTOR_LEFT, leftSpeed);
motorForward(MOTOR_RIGHT, rightSpeed);
}
The code for this sketch is derived from the myRobotEdge sketch discussed
earlier in this chapter. You can download the example code, locate myRobot
Line, open the sketch and upload it to the robot. Or you can derive the sketch
yourself:
1. Open the myRobotEdge sketch in the example code and do a Save As and
name it myRobotLine.
2. Locate the defines for locations of sensors in the robotDefines tab and add
the center sensor following the defines for the left and right sensors: const
int SENSE_IR_CENTER = 2;.
3. Replace the main sketch code with the code listed here: Example 9-4.
4. Compile and upload the code
Place the robot on the surface with the center sensor above the line and switch
the power on. After a short delay the robot will move forward and track the
line. The robots ability to follow the line depends on many factors:
Chapter 9 159
Seeing Sketch Data
• Robot over sensitive - if the robot follows the line but zig-zags excessively ,
increase the damping value in the line sensor code in the main tab. Try
larger values until you find a range that works. Note that you may need a
different damping value if you change the speed.
• Robot not sensitive enough - if the robot drifts off the line, decrease the
damping value in the line sensor code in the main tab. Try smaller values
until you find a range that works. Note that you may need a different
damping value if you change the speed.
Here is how the line following sketch should be modified to display the sensor
value:
Add the DataDisplay tab to the sketch (the myRobotLineDisplay sketch in the
example code download (“How to Contact Us” (page xv)) has this tab added
as well as all the other code changes that follow).
In the main sketch, add constants identifying the list of items to be displayed,
labels for each item, and the minimum and maximum values. Example 9-5
shows the constants used to produce the display in Figure 9-7.
char* labels[] =
{"", "Left Line", "Center Line", "Right Line","Drift", "Left Speed", "Right Speed"};
int minRange[] =
{0, 0, 0, 0, -1023, 0, 0};
int maxRange[] =
{0, 1023, 1023, 1023, 1023, 100, 100};
Download from Wow! eBook <www.wowebook.com>
You can then call the sendData function to send the values you want to display.
Example 9-7 shows the lineSense() function updated to send sensor data.
Chapter 9 161
Seeing Sketch Data
return drift;
}
motorForward(MOTOR_LEFT, leftSpeed);
motorForward(MOTOR_RIGHT, rightSpeed);
}
Hardware Required
• Ping distance sensor from Parallax; see “Sonar Distance Sensors” (page
137) in Chapter 8, Tutorial: Introduction to Sensors.
• Servo required for myRobotScan; see “Sonar Distance Sensors” (page 137)
in Chapter 8, Tutorial: Introduction to Sensors.
Connect the Ping sensor and servo the right way around; the black wires
(ground) go nearest the pin marked -, the white (or lighter color) signal wire
goes nearest the pin marked S (Figure 10-1).
Figure 10-1. Ping sensor and servo plug into pins on the motor shield
163
Sketches Used in This Chapter
The Distance tab’s code is not listed in this chapter, but it is included in
the example code (see “How to Contact Us” (page xv) for information
on downloading the example code).
Chapter 10 165
Mounting a Ping Distance Sensor
Figure 10-4. Parallax Ping Servo Bracket Figure 10-5. Parallax servo bracket parts
If you prefer to make a bracket, it’s easy to do, as the next section explains.
Figure 10-7 shows the a small block of wood cut and drilled.
Figure 10-7. Ping sensor with homemade wood mount, Figure 10-8. Rear view of mount; note nuts used as
not fully assembled spacers between PCB and mount
Figure 10-9 and Figure 10-10 show the mount attached to the robot.
Chapter 10 167
Mounting a Ping Distance Sensor
Figure 10-10. Feel free to make your mount in a different size or shape
Chapter 10 169
Letting the Robot Wander
Figure 10-13. Ping Sensor fixed in place to look for obstacles ahead
// Setup runs at startup and is used configure pins and init system variables
void setup()
{
Serial.begin(9600);
blinkNumber(8); // open port while flashing. Needed for Leonardo only
lookBegin();
moveBegin();
Chapter 10 171
Letting the Robot Wander
void loop()
{
moveForward();
roam(); // look around
}
This simply initializes the ‘Look’ module (“The Look Code” (page 149)) and
‘Move’ module (“Core Movement Code” (page 124)) and then calls a function
named roam that does all the hard work of looking for obstacles and moving
to avoid them. The roam function is added into code in the Look tab;
Example 10-2 replaces the entirety of the Look tab code that you saw in earlier
examples.
const int MIN_DISTANCE = 8; // robot stops when object is nearer (in inches)
const int CLEAR_DISTANCE = 24; // distance in inches considered attractive to move
const int MAX_DISTANCE = 150; // the maximum range of the distance sensor
void lookBegin()
{
irSensorBegin(); // initialize sensors
}
switch(obstacle) {
case OBST_FRONT_EDGE: return irEdgeDetect(DIR_LEFT) && irEdgeDetect(DIR_RIGHT);
case OBST_LEFT_EDGE: return irEdgeDetect(DIR_LEFT);
case OBST_RIGHT_EDGE: return irEdgeDetect(DIR_RIGHT);
case OBST_FRONT: return lookAt(lookAngles[DIR_CENTER]) <= MIN_DISTANCE;
}
return false;
}
Chapter 10 173
Letting the Robot Wander
void roam()
{
int distance = lookAt(lookAngles[DIR_CENTER]);
if(distance == 0)
{
moveStop();
Serial.println("No front sensor");
return; // no sensor
}
else if(distance <= MIN_DISTANCE)
{
moveStop();
//Serial.print("Scanning:");
int leftDistance = lookAt(lookAngles[DIR_LEFT]);
if(leftDistance > CLEAR_DISTANCE) {
// Serial.print(" moving left: ");
moveRotate(-90);
}
Download from Wow! eBook <www.wowebook.com>
else {
delay(500);
int rightDistance = lookAt(lookAngles[DIR_RIGHT]);
if(rightDistance > CLEAR_DISTANCE) {
// Serial.println(" moving right: ");
moveRotate(90);
}
else {
// Serial.print(" no clearence : ");
distance = max( leftDistance, rightDistance);
if(distance < CLEAR_DISTANCE/2) {
timedMove(MOV_BACK, 1000); // back up for one second
moveRotate(-180); // turn around
}
else {
if(leftDistance > rightDistance)
moveRotate(-90);
else
moveRotate(90);
}
}
}
}
}
}
else if(lookForObstacle(OBST_LEFT_EDGE) == true)
{
Serial.println("left sensor detected edge");
timedMove(MOV_BACK, 100);
moveRotate(30);
}
else if(lookForObstacle(OBST_RIGHT_EDGE) == true)
{
Serial.println("right sensor detected edge");
timedMove(MOV_BACK, 100);
moveRotate(-30);
}
}
The roam function uses information reported by the distance sensor to detect
obstacles. The distance sensor code is described in “Sonar Distance Sensors”
(page 137), the sketches in this chapter contain the code in a new tab named
Distance.
The checkMovement function introduced in the previous chapter is enhanced
here to check for and return false if there are obstacles in front when the robot
is moving forward. checkMovement is called when the robot is taking evasive
action during a timed move. You can add additional checks into this function
if needed. For example, if you add sensors to detect an edge to the rear of the
robot and added your own code that returned true when this sensor detected
an edge, the logic shown in Example 10-3 would prevent the robot from going
over an edge when backing up to avoid an obstacle in front.
Chapter 10 175
Letting the Robot Wander
}
}
return isClear;
}
Chapter 10 177
Adding Scanning
Adding Scanning
In the previous sketch, the robot needs to turn in order to look left and right.
Mounting the distance sensor on a servo adds the ability to rotate the sensor
so the robot can ‘turn its head’ to look around as shown in Figure 10-14.
The sketch logic is the same, but the Look module has code added to command
a servo to rotate left and right for brief periods. This allows the sensor to look
to see if it can detect an obstacle (see Figure 10-15). If your distance sensor is
not centered, you can add a line in setup() that will center the servo.
Example 10-6 shows the complete setup function with the servo centering line
added.
lookBegin();
moveBegin();
The call to softServoWrite centers the servo and waits for two seconds. If your
sensor is not centered, follow these steps:
The servo angle is controlled by adjusting the pulse width on the Arduino pin
connected to the servo. 1.5ms pulses will center the servo, and increasing or
decreasing the pulse width will turn the servo one direction or the other.
The exact relationship between pulse width and servo angle varies
across different servo products. If your servo turns right when it should
turn left, swap the right and left servo angles in the servoAngles array:
// servo angles left, right, center
const int servoAngles[] = { 150, 30, 90};
Chapter 10 179
Adding Scanning
Arduino has a Servo library that can control up to 12 servos, however this is
not used in this sketch for two reasons. The Servo library enables you to send
an angle to the servo and carry on executing sketch code while the servo is
being moved in the background, but your code must wait until the servo is
facing the desired direction before requesting a reading from the distance
sensor. However, the main reason not to use the Servo library is because it
requires exclusive use of one of the Arduino chip’s hardware timers (timer 1)
and timers are in short supply on a standard Arduino chip (see Appendix F,
Arduino Pin and Timer Usage ).
The code to control the servo goes in a tab named Softservo (see
Example 10-7).
int servoPin;
The softServoAttach function stores the pin number that the servo is attached
to. The softServoWrite function converts the desired angle into a pulse width
and creates the pulse using digitalWrite with a pulse width determined by
a call to delayMicroseconds. The pulses are sent repeatedly for the duration of
the given servoDelay which is a period sufficient for the servo to turn to the
desired direction.
The Look code is similar to the code described at the beginning of this chapter,
but here the lookAt function calls softServoWrite to rotate the servo instead
of rotating the entire robot. Example 10-8 shows the Look tab used in the
myRobotScan sketch.
// servo defines
const int sweepServoPin = 9; // pin connected to servo
const int servoDelay = 500; // time in ms for servo to move
const int MIN_DISTANCE = 8; // robot stops when object is nearer (in inches)
const int CLEAR_DISTANCE = 24; // distance in inches considered attracive to move
const int MAX_DISTANCE = 150; // the maximum range of the distance sensor
void lookBegin()
{
irSensorBegin(); // initialize sensors
softServoAttach(sweepServoPin); /// attaches the servo pin to the servo object
}
Chapter 10 181
Adding Scanning
Chapter 10 183
Adding Scanning
The lookForObstacle and roam functions are modified from the non-scanning
version to use the appropriate servo angles for looking left, right, and center.
The servo angles are stored in the array servoAngle (swap the left and right
values if your servo turns in the wrong direction). The lookAt function now
rotates the servo to the desired angle instead of moving the entire robot.
Hardware Required
• The TV remote control sketch requires an infrared decoder module.
TSOP4838 (or the equivalent PNA4602) modules (Figure 11-1) have power
and signal pins oriented to enable them to plug directly into the socket
on the motor shield.
You will also need an infrared remote control—almost any controller from
a TV or DVD player will do.
185
Sketches Used in This Chapter
Example 111. Remote tab code for simple serial remote control
// robot remote commands
// This version is for serial commands
// Command constants
void remoteService()
{
if(Serial.available() )
{
int cmd = Serial.read();
processCommand(cmd);
}
}
Chapter 11 187
Design of the Remote Control Code
{
if(newState != commandState)
{
Serial.print("Changing Cmd state from "); Serial.print( states[commandState]);
Serial.print(" to "); Serial.println(states[newState]);
commandState = newState;
}
}
This code adds a function named remoteService that is called from the main
sketch to check if any remote commands have been received. The remoteSer
vice function will be exanded later in this chapter to support other remote
control inputs.
You may have noticed that there are two functions named processCommand.
The one that takes a single parameter tests if a second parameter is required
(as in the case of the PIVOT command) and if so gets this using the Serial Stream
parseInt function.
Example 11-2 shows the main sketch code from the example, myRobotSerial
Remote that responds to the serial commands.
// Setup runs at startup and is used configure pins and init system variables
void setup()
{
Serial.begin(9600);
while(!Serial); // only needed for leonardo
moveBegin();
moveSetSpeed(MIN_SPEED + 10) ; // Run at 10% above minimum speed
}
void loop()
{
remoteService(); // wait for serial commands
}
// function to check if robot can continue moving when taking evasive action
// returns true if robot is not blocked when moving to avoid obstacles
// this 'placeholder' version always returns true
boolean checkMovement()
{
return true;
}
If you have a wireless device that passes serial data such as a Bluetooth module,
you can wirelessly control the robot by connecting the serial output of the
adapter to the Arduino serial input and wiring up the power leads. If you are
using a Leonardo, note that the TX/RX pins (digital 1 and 0) are accessed
through Serial1 rather than Serial, so modify your code accordingly (you’ll
need to replace all instances of Serial with Serial1 in all the tabs of your sketch).
Chapter 11 189
Controlling the Robot with a TV Type IR Remote
If the receiver module does not plug securely into the socket, use a long-nose
pliers to twist the ends of each of the three leads 90 degrees, as shown in
Figure 11-5.
Figure 11-5. IR Receiver Module with leads twisted to for better fit into socket
Chapter 11 191
Controlling the Robot with a TV Type IR Remote
A numeric value is provided for each remote keypress detected. The specific
key values decoded will depend on the remote controller you use.
Example 11-3 shows code for the Remote tab with support for the IR receiver.
IRrecv irrecv(irReceivePin);
decode_results results;
// Command constants
void remoteService()
{
if (irrecv.decode(&results))
{
if (results.decode_type != UNKNOWN)
{
//Serial.println(results.value); // uncomment to see raw result
convertIrToCommand(results.value);
}
irrecv.resume(); // Receive the next value
}
// additional support for serial commands
if(Serial.available() )
{
int cmd = Serial.read();
processCommand(cmd);
}
}
Chapter 11 193
Controlling the Robot with a TV Type IR Remote
To use this code with your remote, you need to replace the IR commands with
the ones your remote controller sends. Figure 11-6 shows a typical controller
with a suggested key assignment but you can choose any keys you want.
You can use the sketch shown in Example 11-4 to display the actual IR codes
that are sent. After you upload and run the sketch, it will prompt you to press
a key for each command to be learned. These are: forward, reverse, left, right,
pivot counterclockwise, pivot clockwise, and stop. The decoded value will be
displayed for each recognized keypress. After all the keys are learned, the codes
are written to the Serial Monitor in a format that you can copy into the Re
mote tab below the comment line that reads: //IR remote keycodes:replace
this with codes for your remote.
Chapter 11 195
Controlling the Robot with a TV Type IR Remote
*/
long irKeyCodes[ KEYCOUNT]; // this will store raw codes for all keys
char * remoteKeyNames[KEYCOUNT] =
{"Forward", "Back", "Left", "Right", "PivotCW", "PivotCCW", "Halt" };
// not used: Slower, Faster
void setup()
{
Serial.begin(9600);
while(!Serial); // only needed for leonardo
void loop()
{
long key = getIrKeycode(TIMEOUT);
if( key!= NO_KEY)
{
int index = findKey(key);
if( index != NO_KEY)
{
Serial.println(remoteKeyNames[index]);
}
}
}
flushKeys();
Chapter 11 197
Controlling the Robot with a TV Type IR Remote
irrecv.resume();
results.value = -1;
}
void printConstants()
{
int i = 0;
Serial.println("//IR remote keycodes:");
Serial.print("const long IR_MOVE_FORWARD = "); Serial.print(irKeyCodes[i++]);
Serial.println(";");
Serial.print("const long IR_MOVE_BACK = "); Serial.print(irKeyCodes[i++]);
Serial.println(";");
Serial.print("const long IR_MOVE_LEFT = "); Serial.print(irKeyCodes[i++]);
Serial.println(";");
Serial.print("const long IR_MOVE_RIGHT = "); Serial.print(irKeyCodes[i++]);
Serial.println(";");
Serial.print("const long IR_PIVOT_CW = "); Serial.print(irKeyCodes[i++]);
Serial.println(";");
Serial.print("const long IR_PIVOT_CCW = "); Serial.print(irKeyCodes[i++]);
Serial.println(";");
Serial.print("const long IR_HALT = "); Serial.print(irKeyCodes[i++]);
Serial.println(";");
The IR remote example sketch shown in Example 11-5 is similar to the example
earlier in this chapter with some additional lines for the IR remote.
******************************************************************************/
// Setup runs at startup and is used configure pins and init system variables
void setup()
{
Serial.begin(9600);
blinkNumber(8); // open port while flashing. Needed for Leonardo only
lookBegin();
moveBegin();
remoteBegin(irReceivePin); /// added Remote tab
Download from Wow! eBook <www.wowebook.com>
void loop()
{
remoteService();
}
Chapter 11 199
Enhancing Your Robot A
Experienced software engineers use a variety of techniques to help manage
complex projects. This section provides a list of useful tips for designing and
building complex robotics projects.
Planning
Think Before You Code
It helps to think about your project and be clear on what you want it to achieve
before you start coding. Tinkering around without a plan is a good way to learn
and to have fun, but it can make a large project too cumbersome to manage.
201
Implementing a Complex Project
Simplify
Spending time simplifying code will be repaid in reduced debug time. Complex
code can be difficult to debug or enhance, particularly when you come back
to it after a while. Looking at each completed function with an eye to seeing
if there is a simpler way of achieving the functionality can result in cleaner code
that is easier to maintain.
Experiment
If what you have tried isn’t working, try something new. Software problems
may actually be a hardware issue (and vice versa).
Be Tenacious
Interesting projects usually come with difficult problems—overcoming these
is part of the reward for a job well done.
Have Fun
Isn’t that why you started this project in the first place?
Appendix A 203
B
Using Other Hardware with
Your Robot
You may want to add more capability to your robot or perhaps substitute dif
ferent hardware than the items covered in the text. This chapter describes how
to use some common alternative components.
205
Alternative Motor Controllers
These functions convert requests to set the motor speed into servo angles that
are written to the continuous rotation servos. The conversion is performed
using the Arduino map function.
This code uses the Servo library. If you want to build the infrared remote
control project with continuous rotation servos, you will need to ensure
that the IRremote library is configured to use a timer other than Timer
1) because the Servo library requires Timer 1. See “Modifying a Library
to Change Timer Allocation” (page 236) for timer usage and details on
how to configure timers for the IRremote library.
#include <Arduino.h>
#include "RobotMotor.h"
int speedTable[NBR_SPEEDS] = {40, 50, 60, 70, 80, 90, 100}; // speeds
int rotationTime[NBR_SPEEDS] = {5500, 3300, 2400, 2000, 1750, 1550, 1150}; // time
#include <Arduino.h>
#include <Servo.h>
#include "RobotMotor.h"
Appendix B 207
Alternative Motor Controllers
const int MAX_ANGLE = 60; // number of degrees that motor driven at max speed
const int servoPins[2] = {7,8}; // digital pins connected to servos:(left,right)
int motorStopAngle[2] = {90,90}; // inc or dec so motor stops when motorStop is called
int motorSpeed[2] = {0,0}; // left and right motor speeds stored here (0-100%)
int speedTable[NBR_SPEEDS] = {40, 50, 60, 70, 80, 90, 100}; // speeds
int rotationTime[NBR_SPEEDS] = {5500, 3300, 2400, 2000, 1750, 1550, 1150}; // time
Appendix B 209
Debugging Your Robot C
Complex projects inevitably throw up obstacles in the form of bugs. As these
arise, you can congratulate yourself for choosing such a challenging project
and bear in mind the satisfaction you will feel when all the problems have been
overcome. Here is some software that should help you find and fix problems
you may encounter.
211
Identify the Symptoms and Localize the problem
Figure C-1 depicts the analogRead values from left, center and right and sen
sors used in line detection. The grey numbers in parentheses on the right in
dicate the possible range of values, the following number is the numeric value
sent from Arduino.
The figure shows the values when the robot is straying slightly to the right of
a dark line it is trying to follow (the values increase when the sensor is over the
line). The Position value goes positive when straying right and negative when
left. The Position value is used in the line following sketch to adjust the robot
direction so it stays on the line. The Distance value is in inches and is obtained
from the ping distance sensor.
The Processing sketch expects data in the following format; fields are separated
by commas:
For example, sending "Data,2,680\n" will display a bar with a value of 680 on
the second line.
You can send labels for each line by sending a string such as: "Label,2,Center
Line\n", which will tag the second row with the label “Center Line”.
The data range can be sent using a string such as "Range,5,0,144\n" which
will set the range of the fifth line from 0 to 144.
The easiest way to send this data is to add the DataDisplay tab to your sketch
and call the functions to format and send the data:
• sendData(row, value); sends the specified value for display on the given
row
• sendLabel(row, label); sends the specified label for display on the given
row
• sendRange(row, minimum, maximum); sends the minimum and maximum
values for the specified row
The sketch named myRobotDebug contains the DataDisplay tab and provides
an example of how to send data to the Processing. Example C-1 shows the
main sketch code.
***********************************************************/
#include "robotDefines.h" // global defines
// Setup runs at startup and is used configure pins and init system variables
void setup()
{
Serial.begin(9600);
while(!Serial); // only needed for leonardo
void loop()
{
lineSense();
int distance = pingGetDistance(pingPin);
sendData(DATA_DISTANCE, distance); // send distance
Appendix C 213
Identify the Symptoms and Localize the problem
/****************************
Line Sensor code
****************************/
// defines for locations of sensors
const int SENSE_LINE_LEFT = 0;
const int SENSE_LINE_RIGHT = 1;
const int SENSE_LINE_CENTER = 2;
return drift;
}
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH, 20000); // if a pulse does not arrive in 20 ms then
// the ping sensor is not connected
if(duration >=20000)
return 0;
The Arduino code that sends the data is in the tab named DataDisplay
(Example C-2), you can copy the code into any sketch you want to debug, or
you can simply add the tab to the sketch.
Appendix C 215
Identify the Symptoms and Localize the problem
*/
int windowWidth;
int windowHeight;
int graphHeight;
int rectCenter;
int rectLeft;
int rectRight;
int topMargin;
int bottomMargin;
int leftMargin = 50;
int rightMargin = 80;
int textHeight;
float lastMsgTime;
float displayRefreshInterval = 20; // min time between screen draws
void setup() {
String os=System.getProperty("os.name");
println(os);
initComms();
fontA = createFont("Arial.normal", fontSize);
textFont(fontA);
textHeight = (int)textAscent();
for (int i = 0; i <= maxNumberOfRows; i++)
labelList.add(Integer.toString(i));
adjustSize();
drawGrid();
}
void adjustSize()
{
topMargin = 3 * textHeight;
bottomMargin = 0;
if (displayWidth > 800) {
windowWidth = 800;
windowHeight = topMargin + bottomMargin + yPos(maxNumberOfRows);
size(windowWidth, windowHeight);
}
else {
windowWidth = displayWidth;
windowHeight = displayHeight;
}
//leftMargin = getleftMarginLen() ;
graphHeight = windowHeight - topMargin - bottomMargin;
rectCenter = leftMargin + graphWidth / 2;
rectLeft = leftMargin;
rectRight = leftMargin + graphWidth;
}
void drawGrid() {
fill(0);
String Title = "Arduino Data" + commsPortString() ;
Appendix C 217
Identify the Symptoms and Localize the problem
void processMessages() {
while(true) {
String message = commsGetMessage();
if (message.length() > 0)
{
int row = 0;
}
else
break; // finish processing when the message length is 0
}
}
void checkRefresh()
{
if ( lastMsgTime < 1)
lastMsgTime = millis(); // update the time if it was reset by the last display refresh
}
void draw() {
processMessages();
if ( millis() - lastMsgTime > displayRefreshInterval)
{
background(255);
drawGrid();
for ( int i=1; i <= maxNumberOfRows; i++)
{
drawBar(i);
}
lastMsgTime = 0;
}
}
/******************************
code for Serial port
*****************************/
import processing.serial.*;
void initComms(){
String portName = Serial.list()[portIndex];
println(Serial.list());
println(" Connecting to -> " + portName) ;
myPort = new Serial(this, portName, 9600);
String commsPortString() {
return " (" + Serial.list()[portIndex] + ")" ;
}
String message;
String commsGetMessage() {
if (myPort.available() > 0) {
try {
message = myPort.readStringUntil(10);
if (message != null) {
Appendix C 219
Identify the Symptoms and Localize the problem
// print(message);
return message;
}
}
catch (Exception e) {
e.printStackTrace(); // Display whatever error we received
}
}
return "";
}
This sketch talks to Arduino using the serial port and you need to ensure that
the Processing sketch is using the same port that is connected to your robot.
The port Arduino uses is displayed on in the Arduino IDE. You set the Processing
port by changing the value of the variable portIndex. When starting the Pro
cessing sketch, you will see a list of the ports on your computer. portIndex is
the position of the Arduino port in this list, but note that the index starts from
0, so the default value of 1 for portIndex is for the second port in the list.
A robot tethered via USB is not very convenient when you want to see what
the robot is doing while moving. Adding a wireless serial device such as Blue
tooth or XBee can be a big help when debugging or tuning your robot. If you
are using a Leonardo, note that the TX/RX pins (digital 1 and 0) are accessed
through Serial1 rather than Serial, so modify your code accordingly (you’ll
need to replace all instances of Serial with Serial1 in all the tabs of your sketch).
A standard board like the Uno uses the same Serial object as USB and al
though you don’t need to modify the example code, you will need to discon
nect the wireless device from the pins when uploading code. This is because
the wireless device uses the same pins (digital 1 and 0) as USB.
221
Monitoring Battery Voltage
To support a wide range of battery choices (including 8.4 volt LiPo batteries),
resistor values of 18k ohms for R1 and 2.2k ohms for R2 provide a voltage range
of up to 10 volts.
2200/(18000 + 2200)
= 0.109
Therefore the voltage on the terminal will be the battery voltage times
0.109. For example, 10 volts at the battery will be dropped to just under
the 1.1 volt range of the internal reference.
The resistors can be attached to the battery terminals as shown in Figure D-2,
but a more permanent solution is to solder the resistors to the shield as shown
in Figure D-3 and Figure D-4.
Figure D-3. Voltage Divider Resistors soldered to Vin and Gnd pins
Appendix D 223
Monitoring Battery Voltage
Figure D-4. Voltage Divider Resistors soldered to Vin and Gnd pins
The code to read and interpret the voltage is in the Battery tab (Example D-1).
This code reads the output of the voltage divider using analogRead and con
verts this into the battery voltage expressed in millivolts. This is compared to
preset thresholds levels so an LED can be flashed to indicate low and critical
battery levels. The code can also detect if the optional charger plug is con
nected to stop robot movement while being recharged.
/******************************************************************
* LED starts flashing when volage drops below warning level
* mark space ratio increses from 10% to 50% as voltage decreses from warning to critical
* robot shuts down when battery below critical and led flashes SOS
*
* LED mark space ratio changes from 10% to 90% as voltage increases to full
*****************************************************************/
{
batteryBegin(monitorPin, ledPin);
chargerDetectPin = chargerPin;
pinMode(chargerDetectPin, INPUT_PULLUP); // connect pull-up resistor
}
Appendix D 225
Monitoring Battery Voltage
delay(5000);
}
}
else if (mv < batteryWarning)
{
int percent = map(mv, batteryCritical, batteryWarning, 10, 50);
flash(percent, blinkPin);
}
}
delay(1000);
Serial.println();
}
int value = 0;
for(int i=0; i < 8; i++) {
value = value + analogRead(pin);
}
value = value / 8; // get the average of 8 readings
int mv = map(value, 0,1023, 0, INTERNAL_REFERENCE_MV / DIVISOR );
return mv;
}
flash(60, pin);
for(int i=0; i< 3; i++)
flash(20, pin);
}
There are two versions of the batteryBegin function. Use the one with three
parameters if you have wired up the trickle charger circuit. The three param
eters passed to the function are: the pin that the voltage divider is connected
to, the LED pin, and the pin that detects the charger plug. Here is the function:
batteryBegin(alogBatteryPin, ledPin, chargerDetectPin)
If you have not wired the robot to use a charger, then call batteryBegin with
two parameters: the pin that the voltage divider is connected to and the LED
pin:
batteryBegin(alogBatteryPin, ledPin)
The checking is done in the batteryCheck function. This gets the battery level
in millivolts by calling batteryMv and compares this to the warning and critical
thresholds. The LED is flashed when the level drops below the warning level
with a flash ratio (blink on time to off time) that changes as the voltage drops.
If the voltage drops below the critical level, the robot movement is stopped,
and the LED flashes a distress signal (SOS in morse code) every 5 seconds. When
this happens, the batteries must be replaced or recharged before the robot
will reactivate.
The myrobotBatteryMonitor example sketch (Example D-2) in the download
shows how to use the battery monitor function.
Appendix D 227
Monitoring Battery Voltage
// Setup runs at startup and is used configure pins and init system variables
void setup()
{
Serial.begin(9600);
blinkNumber(8); // open port while flashing. Needed for Leonardo only
lookBegin();
moveBegin();
//batteryBegin(alogBatteryPin, ledPin);
batteryBegin(alogBatteryPin, ledPin, chargerDetectedPin);
pinMode(ledPin, OUTPUT);
Serial.println("Ready");
}
void loop()
{
// roam();
batteryCheck();
}
Trickle Charging
The build chapters in the beginning of the book described a simple trickle
charger that you can use to recharge NiMH batteries. This section describes
how to use the charger as well as some important points to ensure that you
don’t damage your batteries.
Trickle charging is a method of recharging NiMH batteries that provides a slow
but steady charging current which should fully recharge 5 AA cells in around
14 to 16 hours. The charger has been designed for cells with a rated capacity
of 2000 to 2500 mAh (milliampere hours). Cells with a higher rating can be
used but they will require a longer charging period.
The batteries start charging when a DC power supply is plugged into the
charging socket and the power switch is turned on. The charging circuit is
designed for use with a 12 volt supply with a 2.1mm plug (positive on the
center connector). Cells with the suggested rating should handle the trickle
charge current for long periods, however it is good practice to keep your charge
session to 24 hours or less, particularly if your DC supply could be delivering a
little more than the recommended 12 volts.
Appendix D 229
Programming Constructs E
The code in this book takes advantage of a number of Arduino functions that
are summarized in this appendix. See the online Arduino reference for each
function if you want more detail.
Digital I/O
pinMode(pin, mode);
Configures a digital pin to read (input) or write (output) a digital value; see
http://arduino.cc/en/Reference/PinMode
digitalRead(pin);
Reads a digital value (HIGH or LOW) on a pin set for input; see http://ardu
ino.cc/en/Reference/DigitalRead
digitalWrite(pin, value);
Writes the digital value (HIGH or LOW) to a pin set for output; see http://
arduino.cc/en/Reference/DigitalWrite
pulseIn(pin, pulseType, timeout);
Returns the pulse width in microseconds of a changing digital signal on
the given pin. pulseType (either HIGH or LOW) determines if duration is
for a high or low pulse. timout is an optional value indicating how long to
wait for a pulse (the default is one second); see http://arduino.cc/en/Refer
ence/PulseIn
231
Analog I/O
Analog I/O
analogRead(pin);
Reads a value from the specified analog pin. The value ranges from 0 to
1023 for voltages that range from 0 to the reference voltage (5 volts by
default, but can be changed by using analogReference; see http://ardui
no.cc/en/Reference/AnalogRead
analogReference(type);
Configures the reference voltage used for analog input. This is used in the
battery monitor code discussed in Appendix D, Power Sources; see http://
arduino.cc/en/Reference/AnalogReference
Math functions
Download from Wow! eBook <www.wowebook.com>
min(x,y);
Returns the smaller of two numbers; see http://arduino.cc/en/
Reference/Min
max(x,y);
Returns the larger of two numbers; see http://arduino.cc/en/Reference/Max
constrain(x,lower,upper);
Constrains the value of x to be between the lower and upper range; see
http://arduino.cc/en/Reference/Constrain
map(x,fromLow,fromHigh,destLow,destHigh);
Scales a value from one range to another range. The result will have the
same proportion within the destination range as in the source range. The
following code scales the analogRead value to a percentage of the full
scale reading:
int val = analogRead(0);
int percent = map(val, 0,1023, 0,100)
See http://arduino.cc/en/Reference/Map
see: http://arduino.cc/en/Reference/Array
#include "header.h"
This makes functions and variables declared in the specified file available
to your sketch. See http://arduino.cc/en/Reference/Include
Appendix E 233
F
Arduino Pin and Timer
Usage
The tables in this section show the pin and timer resources used by the projects
in this book. You can use the same pin assignments for the Leonardo boards
or the standard ATmega328 boards such as the Uno. However, there are subtle
low level differences between these boards, so if you are adding capabilities
that use additional pins or resources beyond those described in this book, then
check the documentation on pin and resource usage for your board.
235
Handling Resource Conflicts
the Arduino Servo library, one or both of these libraries will malfunction. The
solution is to either reassign one of the libraries to use a different timer, or to
find an alternative way to perform one of the functions without a timer. Both
of these approaches will be discussed in this appendix.
The first code fragment determines the timer to be used with a Leonardo board
(the Arduino build process will use the code in this fragment if the chip is an
ATmega32U4). The uncommented line contains: #define IR_USE_TIMER4_HS
which results in the library using Timer 4. However, Timer 4 is also used to
control one of the motors in the 4WD robot. If you have the 4WD robot and
want to use the infrared remote control library, you need to find a free timer
to use. You can’t easily change the motor library because the pin for Timer 4 is
hard wired to the motor controller chip. But you can change the remote timer
by commenting out the line for Timer 4 and uncommenting a line that enables
a free timer. The Leonardo has 5 timers but as shown in Table F-2, only Timer
1 is available. The code to disable Timer 4 and enable Timer 1 is as follows:
// Leonardo or Teensy 2.0
#elif defined(__AVR_ATmega32U4__)
#define IR_USE_TIMER1 // tx = pin 14
// #define IR_USE_TIMER3 // tx = pin 9
// #define IR_USE_TIMER4_HS // tx = pin 10
Using your text editor to make and save that change in irRemoteInt.h will
eliminate the conflict by using Timer 1 instead of Timer 4.
If your 4WD robot uses an Arduino Uno, then the change is to remove the //
comment characters before the IR_USE_TIMER1 line and add the comment
characters before IR_USE_TIMER2
Appendix F 237
Pin and Timer Tables