In this chapter you will
A servo (short for servomechanism) is an electric motor with a built-in sensor. It can be commanded to rotate to a specific angular position. By attaching the shaft of the servo to other machines, like wheels, gears, and levers, you can precisely control items in the external world. For example, you might use a servo to control the steering of a remote control car by connecting the servo to a horn, a small arm or bar that the servo rotates. An example of a horn is one of the hands on an analog clock. Figure 14-1 shows a servo and three types of horns.
When you’re selecting a servo, consider several parameters:
The servo shown in Figure 14-1 is a generic SG90-type servo. It is inexpensive and can rotate up to 180 degrees, as shown in Figure 14-2.
It’s easy to connect a servo to an Arduino because it needs only three wires. If you’re using the SG90, the darkest wire connects to GND, the center wire connects to 5 V, and the lightest wire (the pulse or data wire) connects to a digital pin. If you’re using a different servo, check its data sheet for the correct wiring.
Now let’s put our servo to work. In this sketch, the servo will turn through its rotational range. Connect the servo to your Arduino as described, with the pulse wire connected to digital pin 4, and then enter and upload the sketch in Listing 14-1.
// Listing 14-1
#include <Servo.h>
Servo myservo;
void setup()
{
myservo.attach(4);
}
void loop()
{
myservo.write(180);
delay(1000);
myservo.write(90);
delay(1000);
myservo.write(0);
delay(1000);
}
Listing 14-1: Servo demonstration sketch
In this sketch, we use the Servo library, which needs to be installed. Follow the instructions outlined in Chapter 7. In the Library Manager, find and then install the “Servo by Michael Margolis, Arduino” library. Create an instance of the servo with the following:
#include <Servo.h>
Servo myservo;
Then, in void setup()
, we tell the Arduino which digital pin the servo control is using:
myservo.attach(4); // control pin on digital 4
Now we simply move the servo with the following:
myservo.write(x);
Here, x is an integer between 0 and 180 representing the angular position to which the servo will be moved. When running the sketch in Listing 14-1, the servo will rotate across its maximum range, stopping at the extremes (0 degrees and 180 degrees) and at the midpoint (90 degrees). When looking at your servo, note that the 180-degree position is on the left and 0 degrees is on the right.
In addition to pushing or pulling objects, servos can be used to communicate data in a similar way as an analog gauge. For example, you could use a servo as an analog thermometer, as you’ll see in Project 37.
Using our servo and the TMP36 temperature sensor from earlier chapters, we’ll build an analog thermometer. We’ll measure the temperature and then convert this measurement to an angle between 0 and 180 degrees to indicate a temperature between 0 and 30 degrees Celsius. The servo will rotate to the angle that matches the current temperature.
The required hardware is minimal:
The circuit is also very simple, as shown in Figure 14-3.
The sketch will determine the temperature using the same method used in Project 8 in Chapter 4. Then it will convert the temperature into an angular rotation value for the servo.
Enter and upload the following sketch:
// Project 37 - Building an Analog Thermometer
float voltage = 0;
float sensor = 0;
float currentC = 0;
int angle = 0;
#include <Servo.h>
Servo myservo;
void setup()
{
myservo.attach(4);
}
1 int calculateservo(float temperature)
{
float resulta;
int resultb;
resulta = -6 * temperature;
resulta = resulta + 180;
resultb = int(resulta);
return resultb;
}
void loop()
{
// read current temperature
sensor = analogRead(0);
voltage = (sensor*5000)/1024;
voltage = voltage-500;
currentC = voltage/10;
// display current temperature on servo
1 angle = calculateservo(currentC);
// convert temperature to a servo position
if (angle>=0 && angle <=30)
{
myservo.write(angle); // set servo to temperature
delay(1000);
}
}
Most of this sketch should be clear to you at this point, but the function calculateservo()
at 1 is new. This function converts the temperature into the matching angle for the servo to use according to the following formula:
angle = (–6 × temperature) + 180
You might find it useful to make a backing sheet to show the range of temperatures that the servo will display, with a small arrow to create a realistic effect. An example is shown in Figure 14-4. You can download a printable version of the backing sheet from the book’s website: https://nostarch.com/arduino-workshop-2nd-edition/.
The next step in our motor-controlling journey is to work with small electric motors. Small motors are used for many applications, from small fans to toy cars to model railroads.
As with servos, you need to consider several parameters when you’re choosing an electric motor:
Our example will use a small, inexpensive electric motor with a speed of 8,540 RPM when running on 3 V, similar to the one shown in Figure 14-5.
To control our motor we’ll use a transistor, introduced in Chapter 3. Because our motor uses up to 0.7 A of current (more than can be passed by the BC548 transistor), we’ll use a transistor called a Darlington for this project.
A Darlington transistor is nothing more than two transistors connected together. It can handle high currents and voltages. The TIP120 Darlington can pass up to 5 A of current at 60 V, which is more than enough to control our small motor. The TIP120 uses a similar schematic symbol as the BC548, as shown in Figure 14-6, but the TIP120 transistor is physically larger than the BC548.
The TIP120 uses the TO-220 housing style, as shown in Figure 14-7.
When you’re looking at the TIP120 from the labeled side, the pins from left to right are base (B), collector (C), and emitter (E). The metal heat sink tab is also connected to the collector.
In this project, we’ll control the motor by adjusting the speed.
The following hardware is required:
You must use a separate power source for motors, because the Arduino cannot supply enough current for the motor in all possible situations. If the motor becomes stuck, then it will draw up to its stall current, which could be more than 1 A. That’s more than the Arduino can supply, and if it attempts to supply that much current the Arduino could be permanently damaged.
A separate battery holder is a simple solution. For a 3 V supply, a two-cell AA battery holder with flying leads will suffice, such as the one shown in Figure 14-8.
Assemble the circuit as shown in the schematic in Figure 14-9.
In this project, we’ll adjust the speed of the motor from stopped (zero) to the maximum and then reduce it back to zero. Enter and upload the following sketch:
// Project 38 - Controlling the Motor
void setup()
{
pinMode(5, OUTPUT);
}
void loop()
{
1 for (int a=0; a<256; a++)
{
analogWrite(5, a);
2 delay(100);
}
3 delay(5000);
4 for (int a=255; a>=0; a--)
{
analogWrite(5,a);
delay(100);
}
delay(5000);
}
We control the speed of the motor using pulse-width modulation (as demonstrated in Project 3 in Chapter 3). Recall that we can do this only with digital pins 3, 5, 6, 9, 10, and 11. Using this method, current is applied to the motor in short bursts: the longer the burst, the faster the speed, as the motor is on more than it is off during a set period of time. So at 1, the motor speed starts at zero and increases slowly; you can control the acceleration by changing the delay
value at 2. At 3, the motor is running as fast as possible and holds that speed for 5 seconds. Then, from 4, the process reverses, and the motor slows to a stop.
The diode is used in the same way it was with the relay control circuit described in Figure 3-19 on page 42 to protect the circuit. When the current is switched off from the motor, stray current exists for a brief time inside the motor’s coil and has to go somewhere. The diode allows the stray current to loop around through the coil until it dissipates as a tiny amount of heat.
Stepper motors are different from regular DC motors, in that they divide a full rotation of the motor into a fixed number of steps. They do this by using two coil windings that are independently controlled. So instead of controlling a rotation with varying voltage as with a regular DC motor, you instead turn on or off the coils in a stepper motor in a certain pattern to rotate the shaft in either direction a set number of times. This control makes steppers ideal for jobs that need precise motor positioning. They are quite commonly found in devices from computer printers to advanced manufacturing devices.
We will demonstrate stepper motor operation using the model 28BYJ-48, as shown in Figure 14-10. This type of stepper motor can be controlled to rotate to one of 4,096 positions; that is, one full rotation is divided into 4,096 steps.
The board next to the motor is used as an interface between your Arduino and the stepper motor, making connection easy and fast. It is usually supplied along with the stepper motor. A close-up is shown in Figure 14-11.
Now to connect your stepper motor to the Arduino. Make the connections as shown in Table 14-1.
Table 14-1: Connections Between the Stepper Motor Controller Board and the Arduino
Control board pin label | Arduino pin |
IN1 | D8 |
IN2 | D9 |
IN3 | D10 |
IN4 | D11 |
5–12 V+ | 5 V |
5–12 V− | GND |
You can briefly run the stepper motor using power from your Arduino if nothing else is drawing power from the Arduino. However, this is not recommended. Instead, use an external 5 V power supply such as a plug pack or other convenient source. As the controller board doesn’t have a DC socket, you can use an external socket with terminal blocks to make easy, solderless connections, as shown in Figure 14-12.
You can then connect jumper wires from the + and – connectors on the terminal blocks to those on the stepper motor controller board. To simplify controlling the stepper motor in our Arduino sketches, you can use a neat Arduino library called CheapStepper. You can download it from https://github.com/tyhenry/CheapStepper/archive/master.zip and install it using the method described in Chapter 7.
Once you have successfully installed the library and connected your stepper motor as described earlier, enter and upload Listing 14-2.
// Listing 14-2
1 #include <CheapStepper.h>
2 CheapStepper stepper (8, 9, 10, 11);
3 boolean clockwise = true;
boolean cclockwise = false;
4 void setup()
{
stepper.setRpm(20);
Serial.begin(9600);
}
void loop()
{
Serial.println("stepper.moveTo (Clockwise, 0)");
5 stepper.moveTo (clockwise, 0);
delay(1000);
Serial.println("stepper.moveTo (Clockwise, 1024)");
5 stepper.moveTo (clockwise, 1024);
delay(1000);
Serial.println("stepper.moveTo (Clockwise, 2048)");
stepper.moveTo (clockwise, 2048);
delay(1000);
Serial.println("stepper.moveTo (Clockwise, 3072)");
stepper.moveTo (clockwise, 3072);
delay(1000);
Serial.println("stepper.moveTo (CClockwise, 512)");
5 stepper.moveTo (cclockwise, 512);
delay(1000);
Serial.println("stepper.moveTo (CClockwise, 1536)");
5 stepper.moveTo (cclockwise, 1536);
delay(1000);
Serial.println("stepper.moveTo (CClockwise, 2560)");
stepper.moveTo (cclockwise, 2560);
delay(1000);
Serial.println("stepper.moveTo (CClockwise, 3584)");
stepper.moveTo (cclockwise, 3584);
delay(1000);
}
Listing 14-2: Testing the stepper motor
Operation of the stepper motor is quite simple. We first include the library at 1 and create an instance of the motor at 2. (If you wish to change the digital pins used for the controller board, update them here.) The control function uses true
and false
for clockwise and counterclockwise rotation, respectively, so we assign these to Boolean variables at 3 to make things clearer. Finally, the motor can be instructed to rotate to one of the 4,096 positions using the function:
Stepper.moveTo(direction, location);
where the direction is either clockwise
or cclockwise
and the location is a value between 0 and 4,095. This is done starting at 5 and repeatedly through the end of the sketch.
Furthermore, in void setup()
at 4, we set the motor’s rotational speed to 20 RPM using:
stepper.setRpm(20);
This is the recommended speed for our stepper motor. Other motors will vary, so you should check with the supplier for these details.
A few moments after you upload the sketch, your stepper motor will start rotating to various positions, and you can see the commands echoed in the Serial Monitor, as shown in Figure 14-13.
Although controlling the speed of one DC motor can be useful, let’s move into more interesting territory by controlling four DC motors at once and affecting their speed and direction. Our goal is to construct a four-wheeled vehicle-style robot that we’ll continue to work on in the next few chapters. Here I’ll describe the construction and basic control of our robot.
Our robot has four motors that each control one wheel, allowing it to travel at various speeds as well as rotate in place. You will be able to control the speed and direction of travel, and you will also learn how to add parts to enable collision avoidance and remote control. Once you have completed the projects in this book, you will have a solid foundation for creating your own versions of this robot and bringing your ideas to life.
You’ll need the following hardware :
The foundation of any robot is a solid chassis containing the motors, drivetrain, and power supply. An Arduino-powered robot also needs to have room to mount the Arduino and various external parts.
You can choose from many chassis models available on the market. To keep things simple, we’re using an inexpensive robot chassis that includes four small DC motors that operate at around 6 V DC and matching wheels, as shown in Figure 14-14.
The task of physically assembling the robot chassis will vary between models, and you may need a few basic tools such as screwdrivers and pliers. If you’re not sure about your final design but wish to get your robot moving, a favored technique is to hold the electronics to the chassis with sticky products such as Blu Tack.
The motors included with the robot chassis typically operate at around 6 V DC, so we’ll use a four-cell AA battery holder to power our robot, as shown in Figure 14-15.
Some AA cell battery holders will not have the wiring needed to connect to our project and instead will have connections for a 9 V battery snap (as our unit in Figure 14-15 does). In this case, you’ll need a battery snap like the one in Figure 14-16.
The final requirement is to create the circuitry to control the four motors in the chassis. Although we could use the circuitry shown in Figure 14-9 for each of the motors, this wouldn’t allow us to control the direction of the motors and could be somewhat inconvenient to wire up ourselves. Instead, we’ll use a motor shield. A motor shield contains the circuitry we need to handle the higher current drawn by the motors and accepts commands from the Arduino to control both the speed and direction of the motors. For our robot, we’ll use an L293D Motor Drive Shield for Arduino, as shown in Figure 14-17.
Making the required connections to the motor shield is simple: connect the wires from the battery holder to the terminal block at the bottom left of the shield, as shown in Figure 14-18. The black wire (negative) must be on the right side and the red wire on the left.
Next you need to connect each DC motor to the motor shield. We’ll refer to the two DC motors at the front of the chassis as motor 2 (left) and motor 1 (right) and the two DC motors at the rear as motor 3 (left) and motor 4 (right). Each motor will have a red and a black wire, so connect them to the matching terminal blocks on the left-hand and right-hand side of the motor shield, as shown in Figure 14-19.
When connecting the wires from the DC motors, note that the black wires are on the outside ends of the terminal blocks and the red wires are on the internal ends. Furthermore, each terminal block is labeled with our matching motor number (M1, M2, M3, and M4) for easy reference.
If your motor’s wires are not color coded, you may have to swap them after the first run to determine which way is forward or backward.
After you’ve connected the power and motor wires to the shield and the shield to your Arduino, the robot should look something like the one in Figure 14-20.
Now to get the robot moving. To simplify its operation, we first need to download and install the Arduino library for the motor drive shield. Follow the instructions outlined in Chapter 7. In the Library Manager, find and then install the “Adafruit Motor Shield library by Adafruit.”
After a moment, the Adafruit Motor Shield library v1 will appear. Click Install and wait for the library to be installed. You can then close the Library Manager window.
Now we’ll create some functions to operate our robot. Because two motors are involved, we’ll need four movements:
Thus, we’ll need four functions in our sketch to match our four movements: goForward()
, goBackward()
, rotateLeft()
, and rotateRight()
. Each accepts a value in milliseconds, which is the length of time required to operate the movement, and a PWM value between 0 and 255. For example, to move forward for 2 seconds at full speed, we’d use goForward(255,2000)
.
Enter and save the following sketch (but don’t upload it just yet):
// Project 39 - Building and Controlling a Robot Vehicle
#include <AFMotor.h>
1 AF_DCMotor motor1(1); // set up instances of each motor
AF_DCMotor motor2(2);
AF_DCMotor motor3(3);
AF_DCMotor motor4(4);
2 void goForward(int speed, int duration)
{
motor1.setSpeed(speed);
motor2.setSpeed(speed);
motor3.setSpeed(speed);
motor4.setSpeed(speed);
motor1.run(FORWARD);
motor2.run(FORWARD);
motor3.run(FORWARD);
motor4.run(FORWARD);
delay(duration);
motor1.run(RELEASE);
motor2.run(RELEASE);
motor3.run(RELEASE);
motor4.run(RELEASE);
}
2 void goBackward(int speed, int duration)
{
motor1.setSpeed(speed);
motor2.setSpeed(speed);
motor3.setSpeed(speed);
motor4.setSpeed(speed);
motor1.run(BACKWARD);
motor2.run(BACKWARD);
motor3.run(BACKWARD);
motor4.run(BACKWARD);
delay(duration);
motor1.run(RELEASE);
motor2.run(RELEASE);
motor3.run(RELEASE);
motor4.run(RELEASE);
}
2 void rotateLeft(int speed, int duration)
{
motor1.setSpeed(speed);
motor2.setSpeed(speed);
motor3.setSpeed(speed);
motor4.setSpeed(speed);
motor1.run(FORWARD);
motor2.run(BACKWARD);
motor3.run(BACKWARD);
motor4.run(FORWARD);
delay(duration);
motor1.run(RELEASE);
motor2.run(RELEASE);
motor3.run(RELEASE);
motor4.run(RELEASE);
}
2 void rotateRight(int speed, int duration)
{
motor1.setSpeed(speed);
motor2.setSpeed(speed);
motor3.setSpeed(speed);
motor4.setSpeed(speed);
motor1.run(BACKWARD);
motor2.run(FORWARD);
motor3.run(FORWARD);
motor4.run(BACKWARD);
delay(duration);
motor1.run(RELEASE);
motor2.run(RELEASE);
motor3.run(RELEASE);
motor4.run(RELEASE);
}
void setup()
{
delay(5000);
}
void loop()
{
goForward(127,5000);
delay(1000);
rotateLeft(127,2000);
delay(1000);
goBackward(127,5000);
delay(1000);
rotateRight(127,5000);
delay(5000);
}
Controlling the robot is easy thanks to the four custom functions in the sketch. Each custom function makes use of the library functions used to control a motor. Before you can use these functions, you need to create an instance for each motor, as shown at 1.
The direction of travel for each motor is set using:
Motor.run(direction)
The value of direction is either FORWARD
, REVERSE
, or RELEASE
, to set the motor’s rotational direction forward or backward or cut power to the motor, respectively.
To set the speed of the motor, we use:
Motor.setSpeed(speed)
The value of speed is between 0
and 255
; it is the range of PWM used to control the rotational speed of the motor.
Therefore, in each of our four custom functions at 2, we use the combination of the motor speed and directional controls to control all four motors at once. Each of the custom functions accepts two parameters: speed
(our PWM value) and duration
(the amount of time to run the motor).
Upload the sketch, remove the USB cable, and connect the battery cable to the Arduino power socket. Then place the robot on carpet or a clean surface and let it drive about. Experiment with the movement functions in the sketch to control your robot; this will help you become familiar with the time delays and how they relate to distance traveled.
Some motor drive shields for Arduino may not have stacking header sockets to enable you to put another shield on top, and they might not allow easy connection of wires from sensors, etc. In this case, you should use a terminal shield for Arduino, an example of which is shown in Figure 14-21.
Terminal shields allow for easy wiring of hardware or sensors to the Arduino’s input and output pins without any soldering, and they can also be used to build your own circuitry for more permanent uses later.
Now that our robot can move, we can start to add sensors. These will tell the robot when it has bumped into something, or they will measure the distance between the robot and an object in its path so that it can avoid a crash. We’ll use three methods of collision avoidance: microswitches, infrared, and ultrasonic.
A microswitch can act like the simple push button we used in Chapter 4, but the microswitch component is physically larger and includes a large metal bar that serves as the actuator (see Figure 14-22).
When using a microswitch, you connect one wire to the bottom contact and the other to the contact labeled NO (normally open) to ensure that current flows only when the bar is pressed. We’ll mount the microswitch on the front of our robot so that when the robot hits an object, the bar will be pressed, causing current to flow and making the robot reverse direction or take another action.
The microswitch hardware is wired like a single push button, as shown in Figure 14-23.
We connect the microswitch to an interrupt port (digital pin 2). Although you might think we should have a function called by the interrupt to make the robot reverse for a few moments, that’s not possible, because the delay()
function doesn’t operate inside functions called by interrupts. We must think a little differently in this case.
Instead, the function goForward()
will turn on the motors if two conditions are met for the variables crash
and the Boolean move
. If crash
is true
, the motors will reverse at a slower speed for 2 seconds to back away from a collision situation.
We can’t use delay()
because of the interrupt, so we measure the amount of time that the motors run by reading millis()
at the start and comparing that against the current value of millis()
. When the difference is greater than or equal to the required duration, move
is set to false
and the motors stop.
Enter and upload the following sketch:
// Project 40 – Detecting Robot Vehicle Collisions with a Microswitch
#include <AFMotor.h>
AF_DCMotor motor1(1); // set up instances of each motor
AF_DCMotor motor2(2);
AF_DCMotor motor3(3);
AF_DCMotor motor4(4);
boolean crash = false;
void goBackward(int speed, int duration)
{
motor1.setSpeed(speed);
motor2.setSpeed(speed);
motor3.setSpeed(speed);
motor4.setSpeed(speed);
motor1.run(BACKWARD);
motor2.run(BACKWARD);
motor3.run(BACKWARD);
motor4.run(BACKWARD);
delay(duration);
motor1.run(RELEASE);
motor2.run(RELEASE);
motor3.run(RELEASE);
motor4.run(RELEASE);
}
1 void backOut()
{
crash = true;
}
void goForward(int duration, int speed)
{
long a, b;
boolean move = true;
2 a = millis();
do
{
if (crash == false)
{
motor1.setSpeed(speed);
motor2.setSpeed(speed);
motor3.setSpeed(speed);
motor4.setSpeed(speed);
motor1.run(FORWARD);
motor2.run(FORWARD);
motor3.run(FORWARD);
motor4.run(FORWARD);
}
if (crash == true)
{
3 goBackward(200, 2000);
crash = false;
}
4 b = millis() - a;
if (b >= duration)
{
move = false;
}
}
while (move != false);
// stop motors
motor1.run(RELEASE);
motor2.run(RELEASE);
motor3.run(RELEASE);
motor4.run(RELEASE);
}
void setup()
{
attachInterrupt(0, backOut, RISING);
delay(5000);
}
void loop()
{
goForward(5000, 127);
delay(2000);
}
This sketch uses an advanced method of moving forward, in that two variables are used to monitor movement while the robot is in motion. The first is the Boolean variable crash
. If the robot bumps into something and activates the microswitch, then an interrupt is called, which runs the function backOut()
at 1. It is here that the variable crash
is changed from false
to true
. The second variable that is monitored is the Boolean variable move
. In the function goForward()
, we use millis()
at 2 to calculate constantly whether the robot has finished moving for the required period of time (set by the parameter duration
).
At 4, the function calculates whether the elapsed time is less than the required time, and if so, the variable move
is set to true
. Therefore, the robot is allowed to move forward only if it has not crashed and not run out of time. If a crash has been detected, the function goBackward()
at 3 is called, at which point the robot will reverse slowly for 2 seconds and then resume as normal.
Our next method of collision avoidance uses an infrared (IR) distance sensor. This sensor bounces an infrared light signal off a surface in front of it and returns a voltage that is relative to the distance between the sensor and the surface. Infrared sensors are useful for collision detection because they are inexpensive, but they’re not ideal for exact distance measuring. We’ll use the Sharp GP2Y0A21YK0F analog sensor, shown in Figure 14-24, for our project.
To wire the sensor, connect the red and black wires on the sensor to 5 V and GND, respectively, with the white wire connecting to an analog input pin on your Arduino. We’ll use analogRead()
to measure the voltage returned from the sensor. The graph in Figure 14-25 shows the relationship between the distance measured and the output voltage.
Because the relationship between distance and output is not easily represented with an equation, we’ll categorize the readings into 5 cm stages. To demonstrate this, we’ll use a simple example. Connect your infrared sensor’s white lead to analog pin 0, the red lead to 5 V, and the black lead to GND. Then enter and upload the sketch shown in Listing 14-3.
// Listing 14-3
float sensor = 0;
int cm = 0;
void setup()
{
Serial.begin(9600);
}
void loop()
{
1 sensor = analogRead(0);
2 if (sensor<=90)
{
Serial.println("Infinite distance!");
} else if (sensor<100) // 80 cm
{
cm = 80;
} else if (sensor<110) // 70 cm
{
cm = 70;
} else if (sensor<118) // 60 cm
{
cm = 60;
} else if (sensor<147) // 50 cm
{
cm = 50;
} else if (sensor<188) // 40 cm
{
cm = 40;
} else if (sensor<230) // 30 cm
{
cm = 30;
} else if (sensor<302) // 25 cm
{
cm = 25;
} else if (sensor<360) // 20 cm
{
cm = 20;
} else if (sensor<505) // 15 cm
{
cm = 15;
} else if (sensor<510) // 10 cm
{
cm = 10;
} else if (sensor>=510) // too close!
{
Serial.println("Too close!");
}
Serial.print("Distance: ");
Serial.print(cm);
Serial.println(" cm");
delay(250);
}
Listing 14-3: IR sensor demonstration sketch
The sketch reads the voltage from the IR sensor at 1 and then uses a series of if
statements at 2 to choose which approximate distance is being returned. We determine the distance from the voltage returned by the sensor using two parameters. The first is the voltage-to-distance relationship, as displayed in Figure 14-25. Then, using the knowledge (from Project 6 in Chapter 4) that analogRead()
returns a value between 0 and 1,023 relative to a voltage between 0 V and around 5 V, we can calculate the approximate distance returned by the sensor.
After uploading the sketch, open the Serial Monitor and experiment by moving your hand or a piece of paper at various distances from the sensor. The Serial Monitor should return the approximate distance, as shown in Figure 14-26.
Now let’s use the IR sensor with our robot vehicle instead of the microswitch. We’ll use a slightly modified version of Project 40. Instead of using an interrupt, we’ll create the function checkDistance()
, which changes the variable crash
to true
if the distance measured by the IR sensor is around 20 cm or less. We’ll use this in the goForward()
forward motion do-while
loop.
Connect the IR sensor to your robot and then enter and upload this sketch:
// Project 41 - Detecting Robot Vehicle Collisions with an IR Distance Sensor
#include <AFMotor.h>
AF_DCMotor motor1(1); // set up instances of each motor
AF_DCMotor motor2(2);
AF_DCMotor motor3(3);
AF_DCMotor motor4(4);
boolean crash = false;
void goBackward(int speed, int duration)
{
motor1.setSpeed(speed);
motor2.setSpeed(speed);
motor3.setSpeed(speed);
motor4.setSpeed(speed);
motor1.run(BACKWARD);
motor2.run(BACKWARD);
motor3.run(BACKWARD);
motor4.run(BACKWARD);
delay(duration);
motor1.run(RELEASE);
motor2.run(RELEASE);
motor3.run(RELEASE);
motor4.run(RELEASE);
}
void checkDistance()
{
1 if (analogRead(0) > 460)
{
crash = true;
}
}
void goForward(int duration, int speed)
{
long a, b;
boolean move = true;
a = millis();
do
{
checkDistance();
if (crash == false)
{
motor1.setSpeed(speed);
motor2.setSpeed(speed);
motor3.setSpeed(speed);
motor4.setSpeed(speed);
motor1.run(FORWARD);
motor2.run(FORWARD);
motor3.run(FORWARD);
motor4.run(FORWARD);
}
if (crash == true)
{
goBackward(200, 2000);
crash = false;
}
b = millis() - a;
if (b >= duration)
{
move = false;
}
}
while (move != false);
// stop motors
motor1.run(RELEASE);
motor2.run(RELEASE);
motor3.run(RELEASE);
motor4.run(RELEASE);
}
void setup()
{
delay(5000);
}
void loop()
{
goForward(5000, 255);
delay(2000);
}
This sketch operates using the same methods used in Project 40, except this version constantly takes distance measurements at 1 and sets the crash
variable to true
if the distance between the IR sensor and an object is less than about 20 cm.
After running the robot and using this sensor, you should see the benefits of using a non-contact collision sensor. It’s simple to add more sensors to the same robot, such as sensors at the front and rear or at each corner. You should be able to add code to check each sensor in turn and make a decision based on the returned distance value.
Our final method of collision avoidance uses an ultrasonic distance sensor. This sensor bounces a sound wave of an ultra-high frequency (that cannot be heard by the human ear) off a surface and measures the amount of time it takes for the sound to return to the sensor. We’ll use the common HC-SR04-type ultrasonic distance sensor, shown in Figure 14-27, for this project, because it’s inexpensive and accurate to around 2 cm.
An ultrasonic sensor’s accuracy and range mean it can measure distances between about 2 and 450 cm. However, because the sound wave needs to be reflected back to the sensor, the sensor must be angled less than 15 degrees away from the direction of travel.
To connect the sensor, attach the Vcc (5 V) and GND leads to their connectors on the motor drive shield, attach the Trig pin to digital pin D2, and attach the Echo pin to digital pin D13. We use D2 and D13 as they are not used by the motor drive shield. However, if you’re just testing or experimenting with the sensor without the robot, you can connect the wires directly to your Arduino board.
To simplify operation of the sensor, download the Arduino library from https://github.com/Martinsos/arduino-lib-hc-sr04/archive/master.zip and install it as explained in Chapter 7. Once the library is installed, you can run the test sketch in Listing 14-4 to see how the sensor works.
// Listing 14-4
#include <HCSR04.h>
1 UltraSonicDistanceSensor HCSR04(2, 13); // trig - D2, echo - D13
2 float distance;
void setup ()
{
Serial.begin(9600);
}
void loop ()
{
3 distance = HCSR04.measureDistanceCm();
Serial.print("Distance: ");
Serial.print(distance);
Serial.println(" cm");
delay(500);
}
Listing 14-4: Ultrasonic sensor demonstration sketch
Retrieving the distance from the sensor is quite simple thanks to the library. At 1, we create an instance and declare which digital pins are connected to the sensor. Then at 2, we have a floating-point variable used to store the distance returned from the sensor’s library function. Finally, the distance is generated at 3 for display in the Serial Monitor.
After uploading the sketch, open the Serial Monitor and move an object toward and away from the sensor. The distance to the object should be returned in centimeters. See how it works in Figure 14-28.
Now that you understand how the sensor works, let’s use it with our robot.
In the following sketch, we check for distances between the robot and an object of 5 cm or less, which will give the robot a reason to back up. Enter and upload the following sketch to see for yourself:
// Project 42 - Detecting Collisions with an Ultrasonic Distance Sensor
#include <AFMotor.h>
#include <HCSR04.h>
// set up instances of each motor
AF_DCMotor motor1(1);
AF_DCMotor motor2(2);
AF_DCMotor motor3(3);
AF_DCMotor motor4(4);
// set up ultrasonic sensor
UltraSonicDistanceSensor HCSR04(2, 13); // trig - D2, echo - D13
boolean crash=false;
void checkDistance()
{
float distance;
distance = HCSR04.measureDistanceCm();
1 if (distance < 5) // crash distance is 5 cm or less
{
crash = true;
}
}
void goBackward(int speed, int duration)
{
motor1.setSpeed(speed);
motor2.setSpeed(speed);
motor3.setSpeed(speed);
motor4.setSpeed(speed);
motor1.run(BACKWARD);
motor2.run(BACKWARD);
motor3.run(BACKWARD);
motor4.run(BACKWARD);
delay(duration);
motor1.run(RELEASE);
motor2.run(RELEASE);
motor3.run(RELEASE);
motor4.run(RELEASE);
}
void goForward(int duration, int speed)
{
long a, b;
boolean move = true;
a = millis();
do
{
checkDistance();
if (crash == false)
{
motor1.setSpeed(speed);
motor2.setSpeed(speed);
motor3.setSpeed(speed);
motor4.setSpeed(speed);
motor1.run(FORWARD);
motor2.run(FORWARD);
motor3.run(FORWARD);
motor4.run(FORWARD);
}
if (crash == true)
{
goBackward(200, 2000);
crash = false;
}
b = millis() - a;
if (b >= duration)
{
move = false;
}
}
while (move != false);
// stop motors
motor1.run(RELEASE);
motor2.run(RELEASE);
motor3.run(RELEASE);
motor4.run(RELEASE);
}
void setup()
{
delay(5000);
}
void loop()
{
goForward(1000, 255);
}
The operation of this sketch should be quite familiar by now. Once again, we constantly measure the distance at 1 and then change the variable crash
to true
if the distance between the ultrasonic sensor and an object in its path is less than 5 cm. Watching the robot magically avoid colliding with things or having a battle of wits with a pet can be quite amazing.
In this chapter, you learned how to introduce your Arduino-based projects to the world of movement. Using simple motors, or pairs of motors, with a motor shield, you can create projects that can move on their own and even avoid obstacles. We used three types of sensors to demonstrate a range of accuracies and sensor costs, so you can now make decisions based on your requirements and project budget.
By now, I hope you are experiencing and enjoying the ability to design and construct such things. But it doesn’t stop here. In the next chapter, we move outdoors and harness the power of satellite navigation.
44.192.132.66