Physical Output

8.0 Introduction

You can make things move by controlling motors with Arduino. Different types of motors suit different applications, and this chapter shows how Arduino can drive many different kinds of motors. You’ll see how to work with servos, which are motors that have circuits within them to allow moving to a specific motor position or to spin at a specific speed. You’ll also learn about brushed and brushless motors, which use different designs to drive a motor that spins at varying speeds and directions. There are recipes in this chapter for stepper motors, which allow you to move a motor a specific number of steps in one direction or the other. In addition to motors that generate rotary motion, there are recipes for working with relays and solenoids.

Servos

Servos enable you to accurately control physical movement because they generally move to a position instead of continuously rotating. They are ideal for making something rotate over a range of 0 to 180 degrees. Servos are easy to connect and control because the motor driver is built into the servo.

Servos contain a small motor connected through gears to an output shaft. The output shaft drives a servo arm and is also connected to a potentiometer to provide position feedback to an internal control circuit (see Figure 8-1).

You can get continuous rotation servos that have the positional feedback disconnected so that you can instruct the servo to rotate continuously clockwise and counterclockwise with some control over the speed. These function a little like the brushed motors covered in Recipe 8.9, except that continuous rotation servos use the servo library code instead of analogWrite and don’t require a motor shield.

Continuous rotation servos are easy to use because they don’t need a motor shield—the motor drivers are inside the servo. The disadvantages are that the speed and power choices are limited compared to external motors, and the precision of speed control is usually not as good as with a motor shield (the electronics is designed for accurate positioning, not linear speed control). See Recipe 8.3 for more on using continuous rotation servos.

Elements inside a hobby servo

Servos respond to changes in the duration of a pulse. A short pulse of 1 ms or less will cause the servo to rotate to one extreme; a pulse duration of 2 ms or so will rotate the servo to the other extreme (see Figure 8-2). Pulses ranging between these values will rotate the servo to a position proportional to the pulse width. There is no standard for the exact relationship between pulses and position, and you may need to tinker with the commands in your sketch to adjust for the range of your servos.

Relationship between the pulse width and the servo angle; the servo output arm moves proportionally as the pulse width increases from 1 ms to 2 ms
Warning

Although the duration of the pulse is modulated (controlled), servos require pulses that are different from the Pulse Width Modulation (PWM) output from analogWrite. You can damage a hobby servo by connecting it to the output from analogWrite—you must use the Servo library instead.

Solenoids and Relays

Although most motors produce rotary motion, a solenoid produces linear movement when powered. A solenoid has a metallic core that is moved by a magnetic field created when current is passed through a coil. A mechanical relay is a type of solenoid that connects or disconnects electrical contacts (it’s a solenoid operating a switch). Relays are controlled just like solenoids. Relays and solenoids, like most motors, require more current than an Arduino pin can safely provide, and the recipes in this chapter show how you can use a transistor or external circuit to drive these devices.

Brushed and Brushless Motors

Most low-cost direct current (DC) motors are simple devices with two leads connected to brushes (contacts) that control the magnetic field of the coils that drives a metallic core (armature). The direction of rotation can be reversed by reversing the polarity of the voltage on the contacts. DC motors are available in many different sizes, but even the smallest (such as vibration motors used in cell phones) require a transistor or other external control to provide adequate current. The recipes that follow show how to control motors using a transistor or an external control circuit called an H-Bridge.

The primary characteristic in selecting a motor is torque. Torque determines how much work the motor can do. Typically, higher torque motors are larger and heavier and draw more current than lower torque motors.

Brushless motors usually are more powerful and efficient for a given size than brushed motors, but they require more complicated electronic control. Where the performance benefit of a brushless motor is desired, components called electronic speed controllers intended for hobby radio control use can be easily controlled by Arduino because they are controlled much like a servo motor.

Stepper Motors

Steppers are motors that rotate a specific number of degrees in response to control pulses. The number of degrees in each step is motor-dependent, ranging from one or two degrees per step to 30 degrees or more.

Two types of steppers are commonly used with Arduino: bipolar (typically with four leads attached to two coils) and unipolar (five or six leads attached to two coils). The additional wires in a unipolar stepper are internally connected to the center of the coils (in the five-lead version, each coil has a center tap and both center taps are connected together). The recipes covering bipolar and unipolar steppers have diagrams illustrating these connections.

Note

The most common cause of problems when connecting devices that require external power is neglecting to connect all the grounds together. Your Arduino ground must be connected to the external power supply ground and the grounds of external devices being powered.

8.1 Controlling Rotational Position with a Servo

Problem

You want to control rotation using an angle calculated in your sketch. For example, you want a sensor on a robot to swing through an arc or move to a position you select.

Solution

Use the Servo library distributed with Arduino. Connect the servo power and ground to a suitable power supply (a single hobby servo can usually be powered from the Arduino 5V line). You can connect the servo signal leads to any Arduino digital pin.

Here is the example Sweep sketch distributed with Arduino; Figure 8-3 shows the connections:

/*
 * Servo rotation sketch
 */
#include <Servo.h>

Servo myservo;  // create servo object to control a servo

int angle = 0;  // variable to store the servo position

void setup()
{
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object
}

void loop()
{
  for(angle = 0; angle < 180; angle += 1)  // goes from 0 degrees to 180 degrees
  {                                        // in steps of 1 degree
    myservo.write(angle);    // tell servo to go to position in variable 'angle'
    delay(20);               // waits 20ms between servo commands
  }
  for(angle = 180; angle >= 1; angle -= 1) // goes from 180 degrees to 0 degrees
  {
    myservo.write(angle);   // move servo in opposite direction
    delay(20);              // waits 20ms between servo commands
  }
}
Connecting a servo for testing with the example Sweep sketch

Discussion

This example sweeps the servo between 0 and 180 degrees. You may need to tell the library to adjust the minimum and maximum positions so that you get the range of movement you want. Calling Servo.attach with optional arguments for minimum and maximum positions will adjust the movement:

myservo.attach(9,1000,2000);  // use pin 9, set min to 1000us, max to 2000us

Because typical servos respond to pulses measured in microseconds and not degrees, the arguments following the pin number inform the Servo library how many microseconds to use when 0 degrees or 180 degrees are requested. Not all servos will move over a full 180-degree range, so you may need to experiment with yours to get the range you want.

The parameters for servo.attach(pin, min, max) are the following:

pin

The pin number that the servo is attached to (you can use any digital pin)

min (optional)

The pulse width, in microseconds, corresponding to the minimum (0-degree) angle on the servo (defaults to 544)

max (optional)

The pulse width, in microseconds, corresponding to the maximum (180-degree) angle on the servo (defaults to 2,400)

Note

The Servo library can handle up to 12 servos on most Arduino boards, but 48 on the Arduino Mega. On the Uno and other boards based on the ATmega328, you will give up analogWrite() (PWM) on pins 9 and 10, even if you’re not connecting a servo to those pins. The Arduino Mega is an exception, and you will likely find that some 32-bit boards do not have this limitation either. See the Servo library reference for more information: http://arduino.cc/en/Reference/Servo.

Power requirements vary depending on the servo and how much torque is needed to rotate the shaft.

Note

You may need an external source of 5 or 6 volts when connecting multiple servos. Four AA cells work well if you want to use battery power. Remember that you must connect the ground of the external power source to Arduino ground.

8.2 Controlling Servo Rotation with a Potentiometer or Sensor

Problem

You want to control rotational direction and position with a potentiometer. For example, you want to control the pan and tilt of a camera or sensor. This recipe can work with any variable voltage from a sensor that can be read from an analog input.

Solution

You can use the same library as in Recipe 8.1, with the addition of code to read the voltage on a potentiometer. This value is scaled so that the position of the pot (from 0 to 1023) is mapped to a range of 0 to 180 degrees. The only difference in the wiring is the addition of the potentiometer; see Figure 8-4:

/*
 * Servo With Sensor sketch
 * Control a servo with a sensor.
 */

#include <Servo.h>

Servo myservo;  // create servo object to control a servo

int potpin = A0; // analog pin used to connect the potentiometer
int val;         // variable to read the value from the analog pin

void setup()
{
  myservo.attach(9); // attaches the servo on pin 9 to the servo object
}

void loop()
{
  val = analogRead(potpin);            // reads the value of the potentiometer
  val = map(val, 0, 1023, 0, 180);     // scale it to use it with the servo
  myservo.write(val);                  // sets position to the scaled value
  delay(15);                           // waits for the servo to get there
}
Controlling a servo with a potentiometer
Note

Hobby servos have a cable with a 3-pin female connector that can be directly plugged in to a “servo” header fitted to some shields, such as the Adafruit Motor Shield. The physical connector is compatible with the Arduino connectors so you can use the same wire jumpers to those used to connect Arduino pins. Bear in mind that the color of the signal lead is not standardized; yellow is sometimes used instead of white. Red is always in the middle and the ground lead is usually black or brown.

Discussion

Anything that can be read from analogRead (see Chapter 5 and Chapter 6) can be used—for example, the gyro and accelerometer recipes in Chapter 6 can be used, so that the angle of the servo is controlled by the yaw of the gyro or angle of the accelerometer.

Note

Not all servos will rotate over the full range of the Servo library. If your servo buzzes due to hitting an end stop at an extreme of movement, try reducing the output range in the map function until the buzzing stops. For example:

val=map(val,0,1023,10,170); // most function over this range

8.3 Controlling the Speed of Continuous Rotation Servos

Problem

You want to control the rotational direction and speed of servos modified for continuous rotation. For example, you are using two continuous rotation servos to power a robot and you want the speed and direction to be controlled by your sketch.

Solution

Continuous rotation servos are a form of gear-reduced motor with forward and backward speed adjustment. Control of continuous rotation servos is similar to normal servos. The servo rotates in one direction as the angle is increased from 90 degrees; it rotates in the other direction when the angle is decreased from 90 degrees. The actual direction forward or backward depends on how you have the servos attached. Figure 8-5 shows the connections for controlling two servos.

Controlling two servos

Servos are usually powered from a 4.8V to 6V source. Heavier duty servos may require more current than the Arduino board can provide through the +5V pin and these will require an external power source. Four 1.2V rechargeable batteries can be used to power Arduino and the servos. If you are thinking of powering your Arduino from these batteries as well, bear in mind that you are in something of a grey area: you could, in theory, power the Arduino by connecting the batteries’ positive lead to Arduino’s 5V pin. However, this bypasses the voltage regulator and is not a great idea. The other option is to supply power to Arduino’s VIN pin, which requires a minimum of 6 volts. But that’s not great either, because anything less than 7 volts could make the Arduino unstable. You’ll need to balance out these constraints with the power needs of your servo motors to strike the right balance.

The sketch sweeps the servos from 90 to 180 degrees, which translates to a variable speed with continuous rotation servos. So, if the servos were connected to wheels, the vehicle would move forward at a slowly increasing pace and then slow down to a stop. Because the servo control code is in loop, this will continue for as long as there is power:

/*
 * Continuous rotation
 */
#include <Servo.h>

Servo myservoLeft;   // create servo object to control a servo
Servo myservoRight;  // create servo object to control a servo

int angle = 0;    // variable to store the servo position

void setup()
{
  myservoLeft.attach(9);   // attaches left servo on pin 9 to servo object
  myservoRight.attach(10); // attaches right servo on pin 10 to servo object
}

void loop()
{
  for(angle = 90; angle < 180; angle += 1)  // goes from 90 to 180 degrees
  {                                         // in steps of 1 degree.
                                            // 90  degrees is stopped.
                                          
    myservoLeft.write(angle);        // rotate servo at speed given by 'angle'
    myservoRight.write(180-angle);   // go in the opposite direction

    delay(20);                       // waits 20ms between servo commands
  }
  for(angle = 180; angle >= 90; angle -= 1) // goes from 180 to 90 degrees
  {
    myservoLeft.write(angle);        // rotate at a speed given by 'angle'
    myservoRight.write(180-angle);   // other servo goes in opposite direction
  }
}

Discussion

You can use similar code for continuous rotation and normal servos, but be aware that continuous rotation servos may not stop rotating when writing exactly 90 degrees. Some servos have a small potentiometer you can trim to adjust for this, or you can add or subtract a few degrees to stop the servo. For example, if the left servo stops rotating at 92 degrees, you can change the lines that write to the servos as follows:

   myservoLeft.write(angle+TRIM); // declare int TRIM=2; at beginning of sketch

8.4 Controlling Servos Using Computer Commands

Problem

You want to provide commands to control servos from the serial port. Perhaps you want to control servos from a program running on your computer.

Solution

You can use software to control the servos. This has the advantage that any number of servos can be supported. However, your sketch needs to constantly attend to refreshing the servo position, so the logic can get complicated as the number of servos increases if your project needs to perform a lot of other tasks.

This recipe drives four servos according to commands received on the serial port. The commands are of the following form:

  • 180a writes 180 to servo a

  • 90b writes 90 to servo b

  • 0c writes 0 to servo c

  • 17d writes 17 to servo d

Here is the sketch that drives four servos connected on pins 7 through 10:

/*
 * Computer Control of Servos sketch
 */
#include <Servo.h>  // the servo library

#define SERVOS 4 // the number of servos
int servoPins[SERVOS] = {7,8,9,10}; // servos on pins 7 through 10

Servo myservo[SERVOS];

void setup()
{
  Serial.begin(9600);
  for(int i=0; i < SERVOS; i++)
  {
    myservo[i].attach(servoPins[i]);
  }
}

void loop()
{
  serviceSerial();
}

// serviceSerial checks the serial port and updates position with received data
// it expects servo data in the form:
//
//  "180a" writes 180 to servo a
//  "90b writes 90 to servo b
//
void serviceSerial()
{
  if ( Serial.available()) 
  {
    int pos = Serial.parseInt();
    char ch = Serial.read();
    if(ch >= 'a' && ch < 'a' + SERVOS) // If ch is a valid servo letter
    {
      Serial.print("Servo "); Serial.print(ch - 'a');
      Serial.print(" set to "); Serial.println(pos);
      myservo[ch - 'a'].write(pos);  // write the position to the corresponding servo
    }
  }
}

Discussion

Connecting the servos is similar to the previous recipes. Each servo line wire gets connected to a digital pin. All servo grounds are connected to Arduino ground. The servo power lines are connected together, and you may need an external 5V or 6V power source if your servos require more current than the Arduino power supply can provide.

An array named myservo (see Recipe 2.4) is used to hold references for the four servos. A for loop in setup attaches each servo in the array to consecutive pins defined in the servoPins array.

The sketch uses parseInt to collect integer values over the serial port. If the character is the letter a, the position is written to the first servo in the array (the servo connected to pin 7). The letters b, c, and d control the subsequent servos.

See Also

See Chapter 4 for more on handling values received over serial.

8.5 Driving a Brushless Motor (Using a Hobby Speed Controller)

Problem

You have a hobby brushless motor and you want to control its speed.

Solution

This sketch uses the same code as Recipe 8.2. The wiring is similar, except for the speed controller and motor. A hobby electronic speed controller (ESC) is a device used to control brushless motors in radio-controlled vehicles. Because these items are mass produced, they are a cost-effective way to drive brushless motors. You can find a selection by typing “esc” into the search field of your favorite hobby store website or searching for “electronic speed controller” on Amazon, eBay, or AliExpress.

Brushless motors have three windings and these should be connected following the instructions for your speed controller (see Figure 8-6). Check the documentation for your ESC to see if there are any special considerations when working with Arduino. For example, the ESC from https://www.rcelectricparts.com suggests initializing the Servo library with Servo.attach(pin, 1000, 2000).

Connecting an electronic speed controller

Discussion

Consult the documentation for your speed controller to confirm that it is suitable for your brushless motor and to verify the wiring. Brushless motors have three connections for the three motor wires and two connections for power. Many speed controllers provide power on the center pin of the servo-style connector. Unless you want to power the Arduino board from the speed controller, you must disconnect or cut this center wire.

Warning

If your speed controller has a feature that provides 5V power to servos and other devices (called a battery eliminator circuit or BEC for short), do not connect this wire to your Arduino (see Figure 8-6).

8.6 Controlling Solenoids and Relays

Problem

You want to activate a solenoid or relay under program control. Solenoids are electromagnets that convert electrical energy into mechanical movement. An electromagnetic relay is a switch that is activated by a solenoid.

Solution

Most solenoids require more power than an Arduino pin can provide, so a transistor is used to switch the current needed to activate a solenoid. Activating the solenoid is achieved by using digitalWrite to set the pin HIGH.

This sketch turns on a transistor connected as shown in Figure 8-7. The solenoid will be activated for one second every hour:

/*
 * Solenoid sketch
 */
int solenoidPin = 2;            // Solenoid connected to transistor on pin 2

void setup()
{
  pinMode(solenoidPin, OUTPUT);
}

void loop()
{
  long interval = 1000 * 60 * 60 ;   // interval = 60 minutes
  
  digitalWrite(solenoidPin, HIGH); // activates the solenoid
  delay(1000);                     // waits for a second
  digitalWrite(solenoidPin, LOW);  // deactivates the solenoid
  delay(interval);                 // waits one hour
}
Driving a solenoid with a transistor

Discussion

The choice of transistor is dependent on the amount of current required to activate the solenoid or relay. The datasheet may specify this in milliamperes (mA) or as the resistance of the coil. To find the current needed by your solenoid or relay, divide the voltage of the coil by its resistance in ohms. For example, a 12V relay with a coil of 185 ohms draws 65 mA: 12 (volts) / 185 (ohms) = 0.065 amps, which is 65 mA. If your transistor is not capable of handling the current you drive through it, it will overheat and may burn out.

Small transistors such as the 2N2222 are sufficient for solenoids requiring up to a few hundred milliamps. Larger solenoids will require a higher power transistor, like the TIP102/TIP120 or similar. There are many suitable transistor alternatives; see Appendix B for help reading a datasheet and choosing transistors.

The purpose of the diode is to prevent reverse EMF from the coil from damaging the transistor (reverse EMF is a voltage produced when current through a coil is switched off). The polarity of the diode is important; there is a colored band indicating the cathode—this should be connected to the solenoid positive power supply.

Electromagnetic relays are activated just like solenoids. A special relay called a solid state relay (SSR) has internal electronics that can be driven directly from an Arduino pin without the need for the transistor. Check the datasheet for your relay to see what voltage and current it requires; anything more than 40 mA at 5 volts will require a circuit such as the one shown in Figure 8-7.

8.7 Making an Object Vibrate

Problem

You want something to vibrate under Arduino control. For example, you want your project to shake for one second every minute.

Solution

Connect a vibration motor as shown in Figure 8-8. You can use a ceramic capacitor for the 0.1 uF capacitor, but if you use an electrolytic capacitor, make sure the positive lead goes to the 33 Ohm resistor that’s connected to +5V.

The following sketch will turn on the vibration motor for one second each minute:

/*
 * Vibrate sketch
 * Vibrate for one second every minute
 */

const int motorPin  = 3;  // vibration motor transistor is connected to pin 3

void setup()
{
  pinMode(motorPin, OUTPUT);
}

void loop()
{
  digitalWrite(motorPin,  HIGH);  // vibrate
  delay(1000); // delay one second
  digitalWrite(motorPin,  LOW);   // stop vibrating
  delay(59000); // wait 59 seconds.
}
Connecting a vibration motor

Discussion

This recipe uses a motor designed to vibrate, such as the SparkFun ROB-08449. If you have an old cell phone you no longer need, it may contain tiny vibration motors that would be suitable. Vibration motors require more power than an Arduino pin can provide, so a transistor is used to switch the motor current on and off. Almost any NPN transistor can be used; Figure 8-3 shows the common 2N2222. A 1 kilohm resistor connects the output pin to the transistor base; the value is not critical, and you can use values up to 4.7 kilohm or so (the resistor prevents too much current flowing through the output pin). The diode absorbs (or snubs—it’s sometimes called a snubber diode) voltages produced by the motor windings as it rotates. The capacitor absorbs voltage spikes produced when the brushes (contacts connecting electric current to the motor windings) open and close. The 33 ohm resistor is needed to limit the amount of current flowing through the motor.

This sketch sets the output pin HIGH for one second (1,000 milliseconds) and then waits for 59 seconds. The transistor will turn on (conduct) when the pin is HIGH, allowing current to flow through the motor.

Here is a variation of this sketch that uses a sensor to make the motor vibrate. The wiring is similar to that shown in Figure 8-8, with the addition of a photocell connected to analog pin 0 (see Recipe 6.3):

/*
 * Vibrate_Photocell sketch
 * Vibrate when photosensor detects light above ambient level
 */

const int motorPin  = 3;  // vibration motor transistor is connected to pin 3
const int sensorPin = A0; // Photodetector connected to analog input 0
int sensorAmbient = 0;             // ambient light level (calibrated in setup)
const int thresholdMargin = 100;   // how much above ambient needed to vibrate

void setup()
{
  pinMode(motorPin, OUTPUT);
  sensorAmbient = analogRead(sensorPin); // get startup light level
}

void loop()
{
  int sensorValue = analogRead(sensorPin);
  if( sensorValue > sensorAmbient + thresholdMargin)
  {
    digitalWrite(motorPin,  HIGH);  // vibrate
  }
  else
  {
    digitalWrite(motorPin,  LOW);   // stop vibrating
  }
}

Here the output pin is turned on when a light shines on the photocell. When the sketch starts, the background light level on the sensor is read and stored in the variable sensorAmbient. Light levels read in loop that are higher than this will turn on the vibration motor.

8.8 Driving a Brushed Motor Using a Transistor

Problem

You want to turn a motor on and off. You may want to control its speed. The motor only needs to turn in one direction.

Solution

This sketch turns the motor on and off and controls its speed from commands received on the serial port. Connect the components as shown in Figure 8-9. You can use a ceramic capacitor for the 0.1 uF capacitor, but if you use an electrolytic capacitor, make sure the positive lead goes to +5V.

/*
 * SimpleBrushed sketch
 * commands from serial port control motor speed
 * digits '0' through '9' are valid where '0' is off, '9' is max speed
 */

const int motorPin = 3; // motor driver is connected to pin 3

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  if (Serial.available()) 
  {
    char ch = Serial.read();

    if(isDigit(ch))  // is ch a number?
    {
      int speed = map(ch, '0', '9', 0, 255);
      analogWrite(motorPin, speed);
      Serial.println(speed);
    }
    else
    {
      Serial.print("Unexpected character ");
      Serial.println(ch);
    }
  }
}
Driving a brushed motor

Discussion

This recipe is similar to Recipe 8.7; the difference is that analogWrite is used to control the speed of the motor. See “Analog Output” for more on analogWrite and Pulse Width Modulation (PWM).

8.9 Controlling the Direction of a Brushed Motor with an H-Bridge

Problem

You want to control the direction of a brushed motor—for example, you want to cause a motor to rotate in one direction or the other from serial port commands.

Solution

An H-Bridge is a component that can reverse the polarity of a motor, or stop it completely. Its name derives from the shape of the schematic representation of an H-Bridge circuit, but this recipe uses an integrated circuit version of an H-Bridge that can control two brushed motors. Figure 8-10 shows the connections for the L293D H-Bridge IC; you can also use the SN754410, which has the same pin layout. You should use ceramic capacitors for the 0.1 uF capacitors. Here’s the sketch:

/*
 * Brushed_H_Bridge_simple sketch
 * commands from serial port control motor direction
 * + or - set the direction, any other key stops the motor
 */

const int in1Pin = 5;  // H-Bridge input pins
const int in2Pin = 4;

void setup()
{
  Serial.begin(9600);
  pinMode(in1Pin, OUTPUT);
  pinMode(in2Pin, OUTPUT);
  Serial.println("+ - to set direction, any other key stops motor");
}

void loop()
{
  if ( Serial.available()) {
    char ch = Serial.read();
    if (ch == '+')
    {
      Serial.println("CW");
      digitalWrite(in1Pin,LOW);
      digitalWrite(in2Pin,HIGH);
    }
    else if (ch == '-')
    {
      Serial.println("CCW");
      digitalWrite(in1Pin,HIGH);
      digitalWrite(in2Pin,LOW);
    }
    else if (ch != '
' && ch != '
') // ignore cr or lf
    {
      Serial.print("Stop motor");
      digitalWrite(in1Pin,LOW);
      digitalWrite(in2Pin,LOW);
    }
  }
}
Connecting two brushed motors using an L293D H-Bridge

Discussion

Table 8-1 shows how the values on the H-Bridge input affect the motor. In the sketch in this recipe’s Solution, a single motor is controlled using the IN1 and IN2 pins; the EN pin is permanently HIGH because it is connected to +5V.

Logic table for H-Bridge
EN IN1 IN2 Function

HIGH

LOW

HIGH

Turn clockwise

HIGH

HIGH

LOW

Turn counterclockwise

HIGH

LOW

LOW

Motor stop

HIGH

HIGH

HIGH

Motor stop

LOW

Ignored

Ignored

Motor stop

Figure 8-10 shows how a second motor can be connected. The following sketch controls both motors together:

/*
 * Brushed_H_Bridge_simple2 sketch
 * commands from serial port control motor direction
 * + or - set the direction, any other key stops the motors
 */

const int in1Pin = 5;  // H-Bridge input pins
const int in2Pin = 4;

const int in3Pin = 3;  // H-Bridge pins for second motor
const int in4Pin = 2;

void setup()
{
  Serial.begin(9600);
  pinMode(in1Pin, OUTPUT);
  pinMode(in2Pin, OUTPUT);
  pinMode(in3Pin, OUTPUT);
  pinMode(in4Pin, OUTPUT);
  Serial.println("+ - sets direction of motors, any other key stops motors");
}

void loop()
{
  if ( Serial.available()) {
    char ch = Serial.read();
    if (ch == '+') 
    {
      Serial.println("CW");
      // first motor
      digitalWrite(in1Pin,LOW);
      digitalWrite(in2Pin,HIGH);
      //second motor
      digitalWrite(in3Pin,LOW);
      digitalWrite(in4Pin,HIGH);
    }
    else if (ch == '-')
    {
      Serial.println("CCW");
      digitalWrite(in1Pin,HIGH);
      digitalWrite(in2Pin,LOW);

      digitalWrite(in3Pin,HIGH);
      digitalWrite(in4Pin,LOW);
    }
    else if (ch != '
' && ch != '
') // ignore cr or lf
    {
      Serial.print("Stop motors");
      digitalWrite(in1Pin,LOW);
      digitalWrite(in2Pin,LOW);
      digitalWrite(in3Pin,LOW);
      digitalWrite(in4Pin,LOW);
    }
  }
}

8.10 Controlling the Direction and Speed of a Brushed Motor with an H-Bridge

Problem

You want to control the direction and speed of a brushed motor. This extends the functionality of Recipe 8.9 by controlling both motor direction and speed through commands from the serial port.

Solution

Connect a brushed motor to the output pins of the H-Bridge as shown in Figure 8-11. You should use ceramic capacitors for the 0.1 uF capacitors.

Connecting a brushed motor using analogWrite for speed control

This sketch uses commands from the Serial Monitor to control the speed and direction of the motor. Sending 0 will stop the motor, and the digits 1 through 9 will control the speed. Sending “+” and “-” will set the motor direction:

/*
 * Brushed_H_Bridge sketch with speed control
 * commands from serial port control motor speed and direction
 * digits '0' through '9' are valid where '0' is off, '9' is max speed
 * + or - set the direction
 */

const int enPin  = 5;  // H-Bridge enable pin
const int in1Pin = 7;  // H-Bridge input pins
const int in2Pin = 4;

void setup()
{
  Serial.begin(9600);
  pinMode(in1Pin, OUTPUT);
  pinMode(in2Pin, OUTPUT);
  Serial.println("Speed (0-9) or + - to set direction");
}

void loop()
{
  if ( Serial.available()) 
  {
    char ch = Serial.read();

    if(isDigit(ch)) // is ch a number?
    {
      int speed = map(ch, '0', '9', 0, 255);
      analogWrite(enPin, speed);
      Serial.println(speed);
    }
    else if (ch == '+')
    {
      Serial.println("CW");
      digitalWrite(in1Pin,LOW);
      digitalWrite(in2Pin,HIGH);
    }
    else if (ch == '-')
    {
      Serial.println("CCW");
      digitalWrite(in1Pin,HIGH);
      digitalWrite(in2Pin,LOW);
    }
    else if (ch != '
' && ch != '
') // ignore cr or lf
    {
      Serial.print("Unexpected character ");
      Serial.println(ch);
    }
  }
}

Discussion

This recipe is similar to Recipe 8.9, in which motor direction is controlled by the levels on the IN1 and IN2 pins. But in addition, speed is controlled by the analogWrite value on the EN pin (see Chapter 7 for more on PWM). Writing a value of 0 will stop the motor; writing 255 will run the motor at full speed. The motor speed will vary in proportion to values within this range.

8.11 Using Sensors to Control the Direction and Speed of Brushed Motors

Problem

You want to control the direction and speed of brushed motors with feedback from sensors. For example, you want two photo sensors to control motor speed and direction to cause a robot to move toward a beam of light.

Solution

This Solution uses similar motor connections to those shown in Figure 8-10, but with the addition of two photoresistors (or phototransitors; see Recipe 1.6 for details), as shown in Figure 8-12. You should use ceramic capacitors for the 0.1 uF capacitors.

Two motors controlled using sensors

The sketch monitors the light level on the sensors and drives the motors to steer toward the sensor detecting the brighter light level:

/*
 * Brushed_H_Bridge_Direction sketch
 * uses photo sensors to control motor direction
 * robot moves in the direction of a light
 */

int leftPins[]  = {5,7,4};  // on pin for PWM, two pins for motor direction
int rightPins[] = {6,3,2};

const int MIN_PWM        = 64;  // this can range from 0 to MAX_PWM
const int MAX_PWM        = 128; // this can range from around 50 to 255
const int leftSensorPin  = A0;  // analog pins with sensors
const int rightSensorPin = A1;

int sensorThreshold = 0; // must have this much light on a sensor to move

void setup()
{
  for(int i=1; i < 3; i++)
  {
    pinMode(leftPins[i], OUTPUT);
    pinMode(rightPins[i], OUTPUT);
  }
}

void loop()
{
  int leftVal = analogRead(leftSensorPin);
  int rightVal = analogRead(rightSensorPin);
  
  if(sensorThreshold == 0) // have the sensors been calibrated?
  {  
    // if not, calibrate sensors to something above the ambient average  
    sensorThreshold = ((leftVal + rightVal) / 2) + 100 ;  
  }  

  if( leftVal > sensorThreshold || rightVal > sensorThreshold)
  {
    // if there is adequate light to move ahead
    setSpeed(rightPins, map(rightVal,0,1023, MIN_PWM, MAX_PWM));
    setSpeed(leftPins,  map(leftVal ,0,1023, MIN_PWM, MAX_PWM));
  }
}

void setSpeed(int pins[], int speed )
{
  if(speed < 0)
  {
    digitalWrite(pins[1],HIGH);
    digitalWrite(pins[2],LOW);
    speed = -speed;
  }
  else
  {
    digitalWrite(pins[1],LOW);
    digitalWrite(pins[2],HIGH);
  }
  analogWrite(pins[0], speed);
}

Discussion

This sketch controls the speed of two motors in response to the amount of light detected by two photocells. The photocells are arranged so that an increase in light on one side will increase the speed of the motor on the other side. This causes the robot to turn toward the side with the brighter light. Light shining equally on both cells makes the robot move forward in a straight line. Insufficient light causes the robot to stop.

Note

If you build this into a robot and have inadvertently created a light-fearing, rather than light-following robot, try reversing the polarity of both motors. If your robot spins in place when it should be moving forward, try reversing the polarity of just one of the motors.

Light is sensed through analog inputs 0 and 1 using analogRead (see Recipe 6.3). When the program starts, the ambient light is measured and this threshold is used to determine the minimum light level needed to move the robot. A margin of 100 is added to the average level of the two sensors so the robot won’t move for small changes in ambient light level. Light level as measured with analogRead is converted into a PWM value using the map function. Set MIN_PWM to the approximate value that enables your robot to move (low values will not provide sufficient torque; find this through trial and error with your robot). Set MAX_PWM to a value (up to 255) to determine the fastest speed you want the robot to move.

Motor speed is controlled in the setSpeed function. Two pins are used to control the direction for each motor, with another pin to control speed. The pin numbers are held in the leftPins and rightPins arrays. The first pin in each array is the speed pin; the other two pins are for direction.

An alternative to the L293 is the Toshiba TB6612FNG. This can be used in any of the recipes showing the L293D. Figure 8-13 shows the wiring for the TB6612 as used on the Pololu breakout board (Pololu item 713).

H-Bridge wiring for the Pololu breakout board

You can reduce the number of pins needed by adding additional hardware to control the direction pins. This is done by using only one pin per motor for direction, with a transistor or logic gate to invert the level on the other H-Bridge input. You can find circuit diagrams for this in the Arduino wiki, but if you want something already wired up, you can use an H-Bridge shield such as the Arduino Motor Shield (7630049200371) or the Ardumoto from SparkFun (DEV-09213). Both are based on the L298, an alternative to the L293 that can drive more current. These shields plug directly into Arduino and only require connections to the motor power supply and windings.

Here is the sketch revised for the Arduino Motor Shield (Analog pins 0 and 1 are used for current sensing, so the sketch uses A2 and A3):

/*
 * Brushed_H_Bridge_Direction sketch for motor shield
 * uses photo sensors to control motor direction
 * robot moves in the direction of a light
 */

int leftPins[]  = {3,12};   // one pin for PWM, one pin for motor direction
int rightPins[] = {11,13};

const int MIN_PWM        = 64;  // this can range from 0 to MAX_PWM
const int MAX_PWM        = 128; // this can range from around 50 to 255
const int leftSensorPin  = A2;  // analog pins with sensors
const int rightSensorPin = A3;

int sensorThreshold = 0; // must have this much light on a sensor to move

void setup()
{
  pinMode(leftPins[1], OUTPUT);
  pinMode(rightPins[1], OUTPUT);
}

void loop()
{
  int leftVal = analogRead(leftSensorPin);
  int rightVal = analogRead(rightSensorPin);
  if(sensorThreshold == 0) // have the sensors been calibrated?
  {  
     // if not, calibrate sensors to something above the ambient average  
     sensorThreshold = ((leftVal + rightVal) / 2) + 100 ; 
  }
  
  if( leftVal > sensorThreshold || rightVal > sensorThreshold)
  {
    // if there is adequate light to move ahead
    setSpeed(rightPins, map(rightVal,0,1023, MIN_PWM, MAX_PWM));
    setSpeed(leftPins,  map(leftVal, 0,1023, MIN_PWM, MAX_PWM));
  }
}

void setSpeed(int pins[], int speed )
{
  if(speed < 0)
  {
    digitalWrite(pins[1], HIGH);
    speed = -speed;
  }
  else
  {
    digitalWrite(pins[1], LOW);
  }
  analogWrite(pins[0], speed);
}

The loop function is identical to the preceding sketch. setSpeed has less code because hardware on the shield allows a single pin to control motor direction.

The Ardumoto Shield uses different pins, so you’d need to modify the code as shown:

int leftPins[]  = {3, 2};  // one pin for PWM, one pin for motor direction
int rightPins[] = {11, 4};

Here is the same functionality implemented using the Adafruit Motor Shield V2; see Figure 8-14. This uses a library named Adafruit_MotorShield that you can install using the Library Manager.

Using the Adafruit Motor Shield

The Adafruit shield supports four connections for motor windings; the sketch that follows has the motors connected to connectors 3 and 4:

/*
 * Brushed_H_Bridge_Direction sketch for Adafruit Motor shield
 * uses photo sensors to control motor direction
 * robot moves in the direction of a light 
 */

#include <Wire.h>
#include <Adafruit_MotorShield.h> // Adafruit motor shield library

// Create an object for the shield
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 

Adafruit_DCMotor *leftMotor = AFMS.getMotor(1);
Adafruit_DCMotor *rightMotor = AFMS.getMotor(2);

const int MIN_PWM        = 64;  // this can range from 0 to MAX_PWM
const int MAX_PWM        = 128; // this can range from around 50 to 255
const int leftSensorPin  = A0;  // analog pins with sensors
const int rightSensorPin = A1;

int sensorThreshold = 0;  // must be more light than this on sensors to move

void setup()
{
  AFMS.begin();  // create with the default frequency 1.6KHz
}

void loop()
{
  int leftVal  = analogRead(leftSensorPin);
  int rightVal = analogRead(rightSensorPin);
 
  if(sensorThreshold == 0) // have the sensors been calibrated?
  {  
    // if not, calibrate sensors to something above the ambient average  
    sensorThreshold = ((leftVal + rightVal) / 2) + 100 ;  
  }   
 
  if( leftVal > sensorThreshold || rightVal > sensorThreshold)  
  {   
    // if there is adequate light to move ahead
    setSpeed(rightMotor, map(rightVal,0,1023, MIN_PWM, MAX_PWM));
    setSpeed(leftMotor,  map(leftVal ,0,1023, MIN_PWM, MAX_PWM));
  }
}

void setSpeed(Adafruit_DCMotor *motor, int speed )
{
  if(speed < 0)
  {
    motor->run(BACKWARD);
    speed = -speed;
  }
  else
  {
    motor->run(FORWARD);
  }
  motor->setSpeed(speed);
}

If you have a different shield than the ones mentioned in this recipe, you will need to refer to the datasheet and make sure the values in the sketch match the pins used for PWM and direction.

See Also

The datasheet for the Pololu board: http://www.pololu.com/file/0J86/TB6612FNG.pdf

The product page for the Ardumoto shield: http://www.sparkfun.com/commerce/product_info.php?products_id=9213

Documentation for the Arduino Motor Shield is located here Adafruit Motor Shield V2 documentation and library can be found here: https://store.arduino.cc/usa/arduino-motor-shield-rev3

The Adafruit Motor Shield V2 documentation and library can be found here: https://learn.adafruit.com/adafruit-motor-shield-v2-for-arduino

8.12 Driving a Bipolar Stepper Motor

Problem

You have a bipolar (four-wire) stepper motor and you want to step it under program control using an H-Bridge.

Solution

This sketch steps the motor in response to serial commands. A numeric value followed by a + steps in one direction; a - steps in the other. For example, “24+” steps a 24-step motor through one complete revolution in one direction, and “12-” steps half a revolution in the other direction. Connect the components as shown in Figure 8-15. You should use ceramic capacitors for the 0.1 uF capacitors. Here’s the sketch:

/*
 * Stepper_bipolar sketch
 * stepper is controlled from the serial port.
 * a numeric value followed by '+' or '-' steps the motor
 */

#include <Stepper.h>

// change this to the number of steps on your motor
#define STEPS 24

// create an instance of the stepper class, specifying
// the number of steps of the motor and the pins it's
// attached to
Stepper stepper(STEPS, 2, 3, 4, 5);

int steps = 0;

void setup()
{
  // set the speed of the motor to 30 RPM
  stepper.setSpeed(30);
  Serial.begin(9600);
}

void loop()
{
  if ( Serial.available()) 
  {
    char ch = Serial.read();

    if(isDigit(ch)) // is ch a number?
    { 
      steps = steps * 10 + ch - '0'; // yes, accumulate the value
    }
    else if(ch == '+')
    {
      stepper.step(steps);
      steps = 0;
    }
    else if(ch == '-')
    {
      stepper.step(steps * -1);
      steps = 0;
    }
  }
}
Four-wire bipolar stepper using L293 H-Bridge

Discussion

This type of motor has two discrete groups of coils, and each group makes up a phase of the stepper motor. When a phase is energized in a particular direction, the motor turns a step in that direction. By pulsing the phases alternately, the motor can move several steps. There are four wires, and each pair of wires corresponds to a phase. You must consult the datasheet or other documentation for your motor to ensure that you are using the correct voltage and the correct number of steps (#define STEPS) with it, but if you don’t know the wiring arrangement of it, there is a simple test you can do with a multimeter. Measure the resistance between different pairs of wires: you will find two pairs of wires that have the same resistance, and all the other pairs will have infinite resistance because there is no connection between them.

If your stepper requires a higher current than the L293 can provide (600 mA for the L293D), you can use the SN754410 chip for up to 1 amp with the same wiring and code as the L293. For current up to 2 amps, you can use the L298 chip. The L298 can use the same sketch as shown in this recipe’s Solution, and it should be connected as shown in Figure 8-16. You should use a ceramic capacitor for the 0.1 uF capacitor.

Unipolar stepper with L298
Warning

Stepper motors can draw a substantial amount of current, including when they are not moving. If you (gently; do not actually turn it) try to turn an energized stepper motor, you will feel resistance. An L293 or even L298 on a breadboard will get very hot over time, quite possibly too hot for the plastic composition of the breadboard. For this reason, we strongly suggest you use a motor shield, described next. Motor shields will include the appropriate heat sink and thermal resilience for this application. If you want to save power, you can turn off the stepper motors when you are not actively using them by taking the ENA and ENB pins low when not in use. This often defeats the purpose of using a stepper motor (that is, it will hold its position in between active stepping). See Recipe 8.13 for an example that uses a timeout to turn off motors when not in use.

A simple way to connect an L298 to Arduino is to use the Arduino Motor Shield (7630049200371). This plugs on top of an Arduino board and only requires external connection to the motor windings; the motor power comes from the Arduino Vin (external Voltage Input) pin. In1/2 is controlled by pin 12, and ENA is pin 3. In3/4 is connected to pin 13, and ENB is on pin 11. Make the following changes to the code to use the preceding sketch with the Arduino Motor Shield:

Stepper stepper(STEPS, 12, 13);

Replace all the code inside of setup() with the following:

pinMode(3, OUTPUT);
digitalWrite(3, HIGH);   // enable A, use LOW to turn off the motor

pinMode(11, OUTPUT);
digitalWrite(11, HIGH);   // enable B, use LOW to turn off the motor

stepper.setSpeed(60);   // set the speed of the motor to 60 rpm

Serial.begin(9600);

The loop code is the same as the previous sketch.

See Also

For more on stepper motor wiring, see Tom Igoe’s stepper motor notes: http://www.tigoe.net/pcomp/code/circuits/motors.

Documentation for the Stepper library: http://www.arduino.cc/en/Reference/Stepper

Adafruit’s stepper motor tutorial: https://learn.adafruit.com/all-about-stepper-motors

8.13 Driving a Bipolar Stepper Motor (Using the EasyDriver Board)

Problem

You have a bipolar (four-wire) stepper motor and you want to step it under program control using the EasyDriver board.

Solution

This Solution is similar to Recipe 8.12, and uses the same serial command protocol described there, but it uses the popular EasyDriver board. Figure 8-17 shows the connections.

Connecting the EasyDriver board

The following sketch controls the step direction and count from the serial port. Unlike the code in Recipe 8.12, it does not require the Stepper library, because the EasyDriver board handles the control of the motor coils in hardware:

/*
 * Stepper_Easystepper sketch
 * stepper is controlled from the serial port.
 * a numeric value followed by '+' or '-' steps the motor
 * a numeric value followed by 's' changes the speed
 */

const int dirPin  = 2;
const int stepPin = 3;
const int enPin   = 4;

int speed = 100;    // desired speed in steps per second
int steps = 0;      // the number of steps to make
long last_step = millis();
long timeout = 30 * 1000; // turn off the motor after 30 secs of inactivity

void setup()
{
  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(enPin, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  if (millis() > last_step + timeout)
  {
    digitalWrite(enPin,HIGH); // Turn off the motor
  }  

  if ( Serial.available()) 
  {
    char ch = Serial.read();

    if(isDigit(ch)) // is ch a number?
    {
      steps = steps * 10 + ch - '0'; // yes, accumulate the value
    }
    else if(ch == '+')
    {
      step(steps);
      steps = 0;
    }
    else if(ch == '-')
    {
      step(-steps);
      steps = 0;
    }
    else if(ch == 's')
    {
      speed = steps;
      Serial.print("Setting speed to "); Serial.println(steps);
      steps = 0;
    }
  }
}

void step(int steps)
{
  int stepDelay = 1000 / speed;  //delay in ms for speed given as steps per sec
  int stepsLeft;

  digitalWrite(enPin,LOW); // Enable the motor
  last_step = millis();

  // determine direction based on whether steps_to_mode is + or -
  if (steps > 0)
  {
    digitalWrite(dirPin, HIGH);
    stepsLeft = steps;
  }
  if (steps < 0)
  {
    digitalWrite(dirPin, LOW);
    stepsLeft = -steps;
  }
  // decrement the number of steps, moving one step each time
  while(stepsLeft > 0)
  {
    digitalWrite(stepPin,HIGH);
    delayMicroseconds(1);
    digitalWrite(stepPin,LOW);
    delay(stepDelay);
    stepsLeft--; // decrement the steps left
  }
}

Discussion

The EasyDriver board is powered through the pins marked M+ and Gnd (shown in the upper right of Figure 8-17). The board operates with voltages between 8 volts and 30 volts; check the specifications of your stepper motor for the correct operating voltage. If you are using a 5V stepper, you must provide 5 volts to the pins marked Gnd and +5V (these pins are on the lower left of the EasyDriver board) and cut the jumper on the printed circuit board marked APWR (this disconnects the on-board regulator and powers the motor and EasyDriver board from an external 5V supply).

This sketch reduces power consumption when the motor has not moved for more than 30 seconds by setting the Enable pin HIGH to disable output (a LOW value enables output). You can adjust this timeout by changing the last_step variable.

Stepping options are selected by connecting the MS1 and MS2 pins to +5V (HIGH) or Gnd (LOW), as shown in Table 8-2. With the board connected as shown in Figure 8-17, it will use full-step resolution: MS1 and MS2 are both LOW) Additionally, note that the reset pin is in its default state when not wired to GND (HIGH). Pulling it LOW will turn off stepper control.

Microstep options
Resolution MS1 MS2

Full step

LOW

LOW

Half step

HIGH

LOW

Quarter step

LOW

HIGH

Eighth step

HIGH

HIGH

You can modify the code so that the speed value determines the revolutions per second as follows:

// use the following for speed given in RPM
int speed = 100;    // desired speed in RPM
int stepsPerRevolution = 200;  // this line sets steps for one revolution

Change the step function so that the first line is as follows:

int stepDelay = 60L * 1000L / stepsPerRevolution / speed; // speed as RPM

Everything else can remain the same, but now the speed command you send will be the RPM of the motor when it steps.

8.14 Driving a Unipolar Stepper Motor with the ULN2003A

Problem

You have a unipolar (five- or six-wire) stepper motor and you want to control it using a ULN2003A Darlington driver chip.

Solution

Connect a unipolar stepper as shown in Figure 8-18. The +V connection goes to a power supply rated for the voltage and current needed by your motor. You should use a ceramic capacitor for the 0.1 uF capacitor.

The following sketch steps the motor using commands from the serial port. A numeric value followed by a + steps in one direction; a - steps in the other:

/*
 * Stepper sketch
 * stepper is controlled from the serial port.
 * a numeric value followed by '+' or '-' steps the motor
 */

#include <Stepper.h>

// change this to the number of steps on your motor
#define STEPS 24

// create an instance of the stepper class, specifying
// the number of steps of the motor and the pins it's
// attached to
Stepper stepper(STEPS, 2, 3, 4, 5);

int steps = 0;

void setup()
{
  stepper.setSpeed(30);    // set the speed of the motor to 30 RPMs
  Serial.begin(9600);
}

void loop()
{
  if ( Serial.available()) 
  {
    char ch = Serial.read();

    if(isDigit(ch)) // is ch a number?
    {
      steps = steps * 10 + ch - '0';        // yes, accumulate the value
    }
    else if(ch == '+')
    {
      stepper.step(steps);
      steps = 0;
    }
    else if(ch == '-')
    {
      stepper.step(steps * -1);
      steps = 0;
    }
    else if(ch == 's')
    {
      stepper.setSpeed(steps);
      Serial.print("Setting speed to "); Serial.println(steps);
      steps = 0;
    }
  }
}
Unipolar stepper connected using ULN2003 driver

Discussion

This type of motor has two coils, and each coil has a connection to the center. Motors with only five wires have both center connections brought out on a single wire. If the connections are not marked, you can identify the wiring using a multimeter. Measure the resistance across pairs of wires to find the two pairs of wires that have the maximum resistance. The center tap wire should have half the resistance of the full coil. A step-by-step procedure is available at http://techref.massmind.org/techref/io/stepper/wires.asp.

See Also

For more on stepper motor wiring, see Tom Igoe’s stepper motor notes: http://www.tigoe.net/pcomp/code/circuits/motors.

Documentation for the Stepper library: http://www.arduino.cc/en/Reference/Stepper

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.120.109