Controlling Motors Arduino
Controlling Motors Arduino
Gary Hallberg
The idea underpinning the Arduino short reads series is to provide a comprehensive, easy to follow
tutorial set and reference guide for anybody wanting to learn about Arduino and basic electronics.
Having a short reads series means that students and hobbyists can select key topics of interest in the
field with minimal outlay. The series aims to provide an easy introduction to all topics of interest and
expand on these topics to provide a broader and deeper understanding of that focus area. The books
are currently only available in Kindle format to provide a very inexpensive package backed up by video
content and interactive social media.
The series is aimed at youngsters and adults interested in getting into electronics and it takes a modern
approach combining the use of the inexpensive software driven Arduino controller board with a
multitude of sensors and discreet electronic components. The experiments in this series of books are
easy to follow, inexpensive to implement and compelling for all interested in STEM education. I hope
to inspire anybody looking for a future career in technology or to simply to have fun.
The first book of this series looks at the Arduino microcontroller and explains its operation and purpose.
Experiments look to show you how to set up the programming environment and drive LEDs as well as
read input from switches, thermistors and photocells. Book 1 will give you a solid foundation of how
some basic electronic components work and how to use them to make simple designs.
Books 4 and 5 in this Arduino short read series are still being written but the series focuses on the
following:
• Book 1 – First Steps with Arduino (published)
• Book 2 – Working with Displays (published)
• Book 4 – Range Finding, Object Detection and Object Avoidance
• Book 5 – Building a Simple Rover
There will be more books to follow the first 5 covering a wide range of focus topics so watch this space!
If you find this series of books useful then please leave your review and rating on
Amazon.
Follow North Border Tech Training on Facebook and Twitter for the latest news and
insights as to how this series will develop.
Foreword
Book 1 of this series sets out to provide a basic understanding of the Arduino platform, how to program
it and how to interface simple electronics. In this book we build on those skills in the focus area of
controlling the many types of motor.
I have been looking forward to writing this book. Motors and other actuators such as solenoids are an
essential component in any control system and using the Arduino to control these devices is a natural
marriage. Controlling motors from the Arduino does present us with some challenges in that the
Arduino cannot provide the power to drive a motor directly. Therefore, we need more in the way of
interfacing electronics. Furthermore, if you cut off the power from a spinning motor, its inductive
properties, and the fact that it will act as a generator, will necessitate extra electronics to protect the
Arduino and the interfacing circuitry. Therefore, there will be much more emphasis on the electronic
engineering aspects around the Arduino and this is a positive deviation from the other books in the
series.
I have no doubt the skills learnt here will allow you to develop more engaging and useful projects.
Without further delay let us get into the content.
Prerequisites for this Book
This book assumes you have read Book 1 of the series (’First Steps with Arduino’) or you already have
some experience of the Arduino platform, how to set up the IDE and undertake some basic Arduino
programming tasks. Basic functions such as ‘digitalRead’ and ‘digitalWrite’ are not covered here
but are explained in Book 1. We will revisit Pulse Width Modulation as it is an essential technology for
speed control of DC motors. Transistors too are an essential device and although they were covered in
book 2, they will be covered again here as the books in this series are intended be standalone, but with
the basics covered in book 1. We will introduce capacitors to readers who are new to electronics and are
following this series of books sequentially.
Download the Code
You can download the Arduino Code for the experiments in this book from GitHub using the link below.
https://github.com/ghallberg-nbtt/cautious-spoon
I recommend that you do this as I have removed some of the comments from the code in the book
listings to aid readability in print form. I strongly recommend that you comment your code heavily to
facilitate understanding and to become familiar with best engineering practice.
Chapter 1: DC Motors
Direct Current or DC motors are by far the easiest type of motor to work with and at the same time they
allow us to address all the issues of driving the motors from an Arduino board. They are common
devices in all sorts of equipment such as toys, radio-controlled cars and DVD players. As a result, they
are cheap. Therefore, they are a great place the start.
Transistors
You can consider a transistor as a voltage-controlled switch. They also have applications as amplifiers,
but we will not cover that area in this book. Transistors come in many forms, but we will be using the
most common Bipolar Junction Transistor (BJT). Figure 1-4 shows the symbol for a BJT Transistor.
They come in two forms NPN and PNP. We will be using the NPN type.
Capacitors
Those who started with Books 1 and 2 of this series would not have been introduced to capacitors. So,
what is a capacitor? It is essentially a device that stores electrical energy. They are two terminal devices
and made from many types of material. All capacitors consist of at least 2 plates with a tiny gap between
each plate. This gap can be air, or some other insulating material called the dielectric as indicated in
Figure 1-7.
Figure 1-8: The Symbol for a Capacitor and an Image of various types
Image source: The author
The capacitance is measured in Farads and is a measure of the component’s ability to store electrical
charge. Farad is named after the English physicist Michael Faraday. The value of capacitance is stamped
on the body of the component. In our circuit we need a 0.1uF capacitor. The number ‘104’ will usually
identify the capacitor as 0.1uF. Just like resistors, it is a good idea to check the component using a
component tester as the values are often hard to read.
DC motors are electrically noisy, and the capacitor is there simply to filter out that noise (decoupling
application).
void setup() {
pinMode (MOTOR, OUTPUT); //set pin 3 for output
}
void loop() {
digitalWrite (MOTOR, HIGH);
delay (2000); //turn on motor for 2 seconds
digitalWrite (MOTOR, LOW);
delay (2000); //turn off motor for 2 seconds
}
void setup() {
pinMode (MOTOR, OUTPUT); //set pin 3 for output
}
void loop() {
speed = analogRead (POT); //read the pot value
speed = map (speed, 0, 1023, 0, 255); // Map the ADC value to PWM value
analogWrite(MOTOR, speed); //change speed of motor
delay (50);
}
Summary
In this chapter you learnt about the basic operation of a DC motor. You were introduced to transistors,
capacitors and general purpose diodes. You learnt how to interface the motor to the Arduino board to
protect it and protect the electronics from flyback currents.
You learnt about Pulse Width Modulation and how to use the technology to control the speed of a DC
motor.
Chapter 2: The H-Bridge
We cannot generate a negative voltage from the Arduino board to reverse the direction of the motor.
But what we can do is reverse the polarity of the supply by using a device called a H-Bridge. In this
chapter we will look at the H-Bridge and explore its operation.
Figure 2-3: The Breadboard Layout for Controlling a Motor with an H-Bridge
Image source: The author
We can structure the code by creating functions to control the motor direction and speed. We need 3
functions: forward, reverse and brake. These are shown below:
//Drive motor forward at the specified speed (0 – 255)
void forward (int speed)
{
digitalWrite (ENABLE1, LOW); //Disable H-Bridge
digitalWrite (INPUT1, HIGH);
digitalWrite (INPUT2, LOW);
analogWrite (ENABLE1, speed); //PWM to drive motor
}
//Drive motor backward at the specified speed (0 – 255)
void reverse (int speed)
{
digitalWrite (ENABLE1, LOW); //Disable H-Bridge
digitalWrite (INPUT1, LOW);
digitalWrite (INPUT2, HIGH);
analogWrite (ENABLE1, speed); //PWM to drive motor
}
//Stop motor
void brake ()
{
digitalWrite (ENABLE1, LOW); //Disable H-Bridge
digitalWrite (INPUT1, LOW);
digitalWrite (INPUT1, LOW);
analogWrite (ENABLE1, HIGH); //Brake motor
}
You must always disable the H-Bridge before changing the state of the internal switches. The
‘analogWrite’ function is used to enable the H-Bridge with the given PWM duty cycle, so controlling
the speed. We only need pass that speed to each function as an integer argument from the main loop.
The speed will be controlled by the potentiometer.
In the full Sketch, we will first read the state the switch to change direction if a ‘HIGH’ to ‘LOW’ transition
is detected. The switch will need to be debounced. Debouncing was covered in Book 1, but there will be
a brief explanation here. The Sketch will then read the value of the potentiometer and the appropriate
function will be called to control the motor direction, i.e. forward, reverse or brake. We will call the
‘brake’ function if the potentiometer value is 10 or less. Listing 2-1 shows the full Sketch.
/*
Controlling a Motor using a H-Bridge
Copyright 2020 Gary Hallberg
Licensed under MIT https://github.com/ghallberg-nbtt/cautious-
spoon/blob/master/LICENSE
*/
void setup() {
pinMode (ENABLE1, OUTPUT);
pinMode (INPUT1, OUTPUT);
pinMode (INPUT2, OUTPUT);
pinMode (SWITCH, INPUT_PULLUP);
brake (); //Stop motor
}
void loop() {
//Read the switch to check for direction toggle
currentButton = digitalRead(SWITCH); // Read the switch state
//Stop motor
void brake ()
{
digitalWrite (ENABLE1, LOW); //Disable H-Bridge
digitalWrite (INPUT1, LOW);
digitalWrite (INPUT2, LOW);
analogWrite (ENABLE1, HIGH); //Brake motor
}
We then read the value of the potentiometer to establish the speed and map the potentiometer reading
to the PWM limits.
speed = analogRead (POT);
speed = map (speed, 0, 1023, 0, 255);
The final part of the code calls the appropriate directional function based on the limits we set for the
braking, speed and direction.
if (speed <= 10) {
brake();
} else {
if (goForward == true) {
forward (speed);
} else {
reverse (speed);
}
}
#include <IRremote.h>
void setup() {
Serial.begin(9600);
irrecv.enableIRIn(); // Start the receiver
irrecv.blink13(true); // flash built in LED
}
void loop() {
if (irrecv.decode(&results)) // have we received an IR signal?
{
Serial.println(results.value, HEX);
irrecv.resume(); // receive the next value
}
}
This would be the first time I have mentioned Classes. Classes differ from functions in that functions
do things and Classes are things or objects. Classes have functions associated with them. The Classes
or objects we need within the library are for the IR receiver and a container for the results.
We first create an object of the IR receiver class in our main Sketch and pass the Arduino I/O pin
number as an argument. We do not need to set the ‘pinMode’ as input as the ‘IRremote’ library does
that for us. ‘IRrecv’ is the name of the Class we need to reference. ‘irrecv’ is the object name we gave
for that reference and ‘RECEIVER’ is the argument (the integer value 2).
IRrecv irrecv(RECEIVER);
Each time we want to access a function in that class, we precede it with ‘irrecv’ followed by the name
of the function.
Next, we want to create an object of the ‘decode_results’ class and we called the object ‘results’.
decode_results results;
In the ‘setup’ function, we initialize the serial monitor, start the receiver, and enable the LED built
into the Arduino board to flash when data is being received.
Serial.begin(9600);
irrecv.enableIRIn(); // Start the receiver
irrecv.blink13(true); // flash built in LED
It is then simply a case looping until we receive a signal and print that result to the serial monitor in
hexadecimal format.
if (irrecv.decode(&results)) // have we received an IR signal?
{
Serial.println(results.value, HEX);
irrecv.resume(); // receive the next value
}
Run the Sketch and open the serial monitor. The output obtained from my remote is shown in Figure
2-9.
Figure 2-9: The Serial Monitor Output Showing the Remote Codes
Image source: The author
We have the following codes for our final Sketch
• FF629D – Up arrow: forward
• FFA857 – Down arrow: reverse
• FFC23D – Right arrow: turn right
• FF22DD – Left arrow: turn left
• FF02FD – OK button: stop
• FF6897 – Number 1: low speed
• FF9867 – Number 2: medium speed
• FFB04F – Number 3: top speed
You may be wondering what the ‘FFFFFFFF’ codes are. These are repeat codes and can be ignored. Bear
in mind that the program is scanning extremely fast and it will detect that a button is still depressed on
the second pass. The ‘decode_results’ class will return the code ‘FFFFFFFF’ so that we only act on
the first detected button press.
Before developing the final Sketch, let us look at the vehicle chassis.
Figure 2-10 is an image of the chassis kit I am using. This style of kit is common and inexpensive. I have
gone for the two-wheel drive version to save cost and allow us to use the existing H-Bridge. This kit has
the advantage that is has pre-drilled holes in the acrylic with spacing suitable for mounting many types
of sensor such as object detection and white line following sensors as well as Arduino and Raspberry Pi
boards. Any premade, 2 wheeled chassis will do the job, or you can make your own. The key is to use
geared motors. A DC motor can spin at up to 4000rpm. We can convert that energy into torque at the
wheels using a gearing system and be able to carry greater loads or work in rougher terrain. The motors
I am using have a 48:1 gear reduction. They draw 160mA and so will work with the L293D H-Bridge.
Make sure the motors you use are within the 600mA limit of the L293D H-Bridge. If you want to take
a closer look at this chassis kit and another I have, then you can watch the video here
(https://youtu.be/r9DWmWnpvdo).
#include <IRremote.h>
//Remote code definitions
const long FORWARD = 0xFF629D;
const long REVERSE = 0xFFA857;
const long RIGHT = 0xFFC23D;
const long LEFT = 0xFF22DD;
const long STOP = 0xFF02FD;
const long LOW_SPEED = 0xFF6897;
const long MEDIUM_SPEED = 0xFF9867;
const long TOP_SPEED = 0xFFB04F;
//pin assignments
const int RECEIVER = 12;
const int ENABLE1 = 6;
const int INPUT1 = 4;
const int INPUT2 = 5;
const int ENABLE2 = 9;
const int INPUT3 = 7;
const int INPUT4 = 10;
//variables
int vehicleSpeed = 150;
long remoteCode = 0;
String direction = "stop";
void setup() {
pinMode (ENABLE1, OUTPUT);
pinMode (INPUT1, OUTPUT);
pinMode (INPUT2, OUTPUT);
pinMode (ENABLE2, OUTPUT);
pinMode (INPUT3, OUTPUT);
pinMode (INPUT4, OUTPUT);
Serial.begin(9600); //Only needed for troubleshooting
irrecv.enableIRIn(); // Start the receiver
irrecv.blink13(true); // flash built in LED
brake ("left");
brake ("right");
}
void loop() {
//First read the remote
if (irrecv.decode(&results)) // have we received an IR signal?
{
remoteCode = results.value;
switch (remoteCode) {
case (FORWARD): {
Serial.println ("Forward");
forward ("left", vehicleSpeed);
forward ("right", vehicleSpeed);
break;
}
case (REVERSE): {
Serial.println ("Reverse");
reverse ("left", vehicleSpeed);
reverse ("right", vehicleSpeed);
break;
}
case (RIGHT): {
Serial.println ("Right");
forward ("left", 255);
forward ("right", 150);
break;
}
case (LEFT): {
Serial.println ("left");
forward ("left", 150);
forward ("right", 255);
break;
}
case (STOP): {
Serial.println ("Stop");
brake ("left");
brake ("right");
break;
}
case (LOW_SPEED): {
Serial.println ("Low speed");
vehicleSpeed = 150;
break;
}
case (MEDIUM_SPEED): {
Serial.println ("Medium speed");
vehicleSpeed = 200;
break;
}
case (TOP_SPEED): {
Serial.println ("Top speed");
vehicleSpeed = 255;
break;
}
}
irrecv.resume(); // receive the next value
}
delay (50);
}
if (motor == "left") {
digitalWrite (ENABLE1, LOW); //Disable H-Bridge
digitalWrite (INPUT1, HIGH);
digitalWrite (INPUT2, LOW);
analogWrite (ENABLE1, speed); //PWM to drive left motor
Serial.println (speed);
} else {
digitalWrite (ENABLE2, LOW); //Disable H-Bridge
digitalWrite (INPUT3, HIGH);
digitalWrite (INPUT4, LOW);
analogWrite (ENABLE2, speed); //PWM to drive right motor
Serial.println (speed);
}
}
//Stop motor
void brake (String motor)
{
if (motor == "left") {
digitalWrite (ENABLE1, LOW); //Disable H-Bridge
digitalWrite (INPUT1, LOW);
digitalWrite (INPUT2, LOW);
analogWrite (ENABLE1, HIGH); //Brake left motor
} else {
digitalWrite (ENABLE2, LOW); //Disable H-Bridge
digitalWrite (INPUT3, LOW);
digitalWrite (INPUT4, LOW);
analogWrite (ENABLE2, HIGH); //Brake right motor
}
}
Troubleshooting Experiment 4
Compared to other experiments, Experiment 4 is complex and involves aspects of coding, a micro-
controller, electronic parts, mechanical parts, and power considerations. There is a good chance it will
not work first time. Here are some considerations to help with troubleshooting.
A potential problem is a conflict between the ‘IRremote’ library and the PWM operation of the Arduino
Uno. There are 3 timers on board the Arduino Uno referred to as Timer 0, Timer 1 and Timer 2. The
‘IRremote’ library needs to use Timer 2. However, PWM pins 3 and 11 also use this timer. Therefore,
if you try to use these pins as the enable inputs to the H-Bridge there will be a conflict. You will find
that only a PWM value of 255 will work. For all other values, the motor will stop and so the pins can
only be used as digital outputs. If this occurs, use any PWM pin other than 3 and 11. Incidentally, the
following PWM pins are associated with the following timers:
• Timer 0 - 6 and 5
• Timer 1 - 9 and 10
• Timer 2 - 11 and 3
This Experiment is hungry on batteries. You are best first using a mains PSU adapter to debug and test
the vehicle. A battery containing 4 x AA cells may not last long or may not provide enough power to get
the vehicle moving. Lithium-ion batteries are effective, but expensive. You may have some in a radio-
controlled vehicle you can use. A cheaper alternative is to use a PP3 9V battery. Again, it must be new
and of high quality. I am using 6V motors but driving these for a short time at 9V will not do any harm.
The extra torque this increased voltage delivers makes the vehicle work better. This is only a temporary
build and for the short term, so this approach is OK, but not recommended for long term use.
You will also need to tune the speed levels for the motors you are using. I worked out that I needed a
PWM value over 110 just to overcome the inertia and move the vehicle at minimum speed. I chose 150
as a minimum speed value, 200 as medium and 255 as top speed.
If the motors run the wrong way, simply swap the leads around, but really check your wiring. It is critical
that the motor and +5V logic supplies are kept separate and wired to the correct components or you
could damage your Arduino board. However, both supply grounds must be tied together so that the
complete system works off the same voltage reference.
Summary
In this chapter you learnt how to use a H-Bridge to control the speed and direction of a motor. You were
introduced to a common and inexpensive device all the L293D. You learnt how to develop Sketches and
projects that use infrared remote control. You learnt how to apply this knowledge to build a simple IR
controlled vehicle and learnt about some of the challenges this can present.
On the coding side you were introduced to the concept of Object-Oriented Programming, libraries and
the ‘switch’ and ‘case’ statement.
Chapter 3: Servo Motors
DC motors are great for driving systems, but angular control is next to impossible as well as holding a
set position. This is what servo motors are designed to do as are stepper motors. We will cover stepper
motors in the Chapter 4.
Servo Operation
You will see that all servos have 3 wires. Usually red and black (or brown) are +5V and ground
respectively, and the positional control wire is usually orange. They work on the principle of PWM to
define the angle. They require a positional feedback mechanism and the simplest type is a
potentiometer built into the motor casing.
PWM operation of the servo motor is illustrated in Figure 3-2. A PWM pulse is triggered every 20ms.
This is generally consistent across all DC servo motors and so makes programming easy with the use of
an Arduino library. If a 1ms pulse is present in the period, the servo rotates to 00. If the pulse is 1.5ms,
the servo rotates 900. Finally, if the pulse is 2ms, the servo rotates to 1800. If the pulse is between these
values the servo rotates to the proportionate angle.
Figure 3-2: Servo PWM Pulses
Image source: The author
If you want the servo to hold its position and resist any rotational torque from the load, simply resend
the same pulse width every 20ms.
#include <Servo.h>
Servo myServo;
int val = 0; //Value of potentiometer
void setup()
{
//Create the Servo Object and pass pin number
myServo.attach(SERVO);
}
void loop()
{
val = analogRead(POT); //Read Pot
val = map(val, 0, 1023, 0, 179); //scale it to servo range
myServo.write(val); //moves the servo to desired angle
delay(15); //waits for the servo to finish moving
}
#include <Servo.h>
/*
This Sketch was written with SimpleDHT version 1.0.11
The code may need to be modified if other versions are used
*/
#include <SimpleDHT.h>
void setup()
{
//Create the Servo Object and pass pin number
myServo.attach(SERVO);
Serial.begin (9600);
}
void loop()
{
Assuming we have a valid reading, the next tasks are to print the temperature to the serial monitor,
map the temperature to a servo angle and print that angle to the serial monitor. I added the
‘constrain’ function to be sure we do not drive the servo beyond 00 and 1800. The delay is needed as
we cannot sample the DHT-11 too fast. You will notice that I mapped 0C to 1800 and 50C to 00. I needed
to do this simply because of the way I mounted the motor and armature, I could flip it around, but it
was just as easy to modify the code. Be sure to check your motor orientation and make the appropriate
changes.
A photograph of the finished project can be seen in Figure 3-10.
Figure 3-10: The Finished Servo Controlled Thermometer
Image source: The author
Summary
In this chapter you learnt about servos and how they work to deliver positional control. You learnt about
power requirements of servos and were introduced to the voltage regulator to provide an external power
supply.
You also learnt about the DHT-11 temperature and humidity sensor and how feedback from sensors
can be used to control servos.
Chapter 4: Stepper Motors
In this final chapter we will look at stepper motors. Stepper motors provide a means for precise
positional control and are capable of great torque but use more power. Due to their superior accuracy
over servos, they are used in robotics and items such as printers, scanners and cutting beds. In this
chapter we will look at two types of motor, both are significant in the educational and hobby arena.
These are the NEMA-17 stepper motor and the 28BYJ-48 stepper motor. Each unit uses a stepper motor
controller board.
Figure 4-6: The 28BYJ-48 Stepper Motor and ULN2003 Driver Board
Image source: The Web (public domain)
Figure 4-7: The 28BYJ-48 Stepper Motor Wiring
Image source: The author
//L298N pins
const int INPUT1 = 2;
const int INPUT2 = 3;
const int INPUT3 = 4;
const int INPUT4 = 5;
// Initialize the stepper library - pass it the Switch control pins
Stepper myStepper(STEPS_PER_REVOLUTION, INPUT1, INPUT2, INPUT3, INPUT4);
void setup()
{
//Set the stepper speed
myStepper.setSpeed(60); //60 RPM
}
void loop()
{
// step one revolution in one direction:
myStepper.step(STEPS_PER_REVOLUTION);
delay(500);
We defined a constant to inform the servo library of the steps needed for 1 revolution
const int STEPS_PER_REVOLUTION = 200;
We then defined the Arduino pins that will connect to the L298N inputs.
const int INPUT1 = 2;
const int INPUT2 = 3;
const int INPUT3 = 4;
const int INPUT4 = 5;
We then created an object of the servo library and pass the steps per revolution and the Arduino pin
numbers as arguments.
Stepper myStepper(STEPS_PER_REVOLUTION, INPUT1, INPUT2, INPUT3, INPUT4);
In the setup function we defined the speed for any sequence of steps and here we set the speed to 60rpm.
myStepper.setSpeed(60);
We simply move the motor by calling in the function below. We chose to move the motor 200 steps for
1 revolution, but we could pass any number as the argument. You need to note that the ‘step’ function
is blocking. This means that the code will wait until this function has finished moving the motor and
only then will the code resume.
myStepper.step(STEPS_PER_REVOLUTION);
Passing the number of steps as a negative number changes the direction of the motor.
//ULN2003 pins
const int IN1 = 2;
const int IN2 = 3;
const int IN3 = 4;
const int IN4 = 5;
void setup()
{
//Set the stepper speed
myStepper.setSpeed(10); //10 RPM
}
void loop()
{
// step one revolution in one direction:
myStepper.step(STEPS_PER_REVOLUTION);
delay(500);
//Variables
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir = "";
boolean previousButton = HIGH;
boolean currentButton = HIGH;
void setup() {
void loop() {
// If last and current state of CLK are different, then pulse occurred
// React to only 1 state change to avoid double count
if ((currentStateCLK != lastStateCLK) && (currentStateCLK == 1)) {
Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);
}
Listing 4-3: The Sketch to Control the Motor with a Rotary Encoder
Listing source: The author
There is quite a lot going on here, but only the code for the rotary encoder is new to us, so let us break
down what is going on. You will notice I set the steps per revolution to 32. This in turn means I can set
the stepper motor speed to much more than 10rpm. The 28BYJ-48 can cope with about 600 or 700rpm
so I set the speed to 600rpm. The idea is that we move the stepper motor on each increment of the
rotary encoder. However, if we move the motor 1 step for every encoder increment, we will see little
movement because of the gear ratio. I therefore set a multiplier so that we move the motor 50 steps for
each encoder increment.
We have defined several variables in the Sketch.
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir = "";
boolean previousButton = HIGH;
boolean currentButton = HIGH;
There is a counter to keep track of the encoder increments. This will increase by 1 for each clockwise
increment and decrement by 1 for each counter-clockwise increment. We need to track the current state
of the CLK output of the encoder to determine if the encoder has moved and in what direction. We do
this by comparing this to the last or previous state of the CLK output. The current direction of rotation
is only needed for the output to the serial monitor. Finally, we need to check for a button press and so
the previous button state and the current button state need to be tracked.
In the ‘setup’ function, we need to read the state of the CLK output and set it to the last state simply
to initialize the variable.
lastStateCLK = digitalRead(CLK);
In the main loop we get the latest state of the CLK output.
currentStateCLK = digitalRead(CLK);
We now need to check for a state change in the CLK output to detect that the encoder is rotating. We
only want to compare the state to the DT output on 1 transition, otherwise we will double count rotation.
We chose the low to high transition so the current state of the CLK output must not equal the previous
state and we only want to compare the DT output if the current state of the CLK output is 1 or ‘HIGH’.
if ((currentStateCLK != lastStateCLK) && (currentStateCLK == 1)) {
If the DT output is low, then we know that the encoder is rotating counter-clockwise. Otherwise, it is
rotating clockwise. If the encoder is rotating counter-clockwise, then we decrement the counter and
move the motor 50 steps counter-clockwise. If the encoder is rotating clockwise, then we increment the
counter and move the motor 50 steps clockwise. We have some code in this block to print the counter
and direction to the serial monitor and it is useful to view the output.
We can then look to the state of the switch.
currentButton = digitalRead(SWITCH); // Read the switch state
Much of this block of code is to debounce the switch and this is familiar to you from Experiment 3. On
detecting a valid button press, we can move the stepper back to that start position but multiplying the
current count by -1 and factoring the multiplier. Finally, we reset the counter to 0.
You can see a video demonstration of this project here (https://youtu.be/Y3ekvR_BrEQ).
Summary
In this chapter you learnt about the purpose of stepper motors and their operation. You compared 2
popular stepper motors in the NEMA-17 and the 28BYJ-48. You learnt that stepper motors can draw
lots of current and so must be matched to a capable controller. You learnt about the L298N and
ULN2003 controllers. You learnt about rotary encoders and how to write code to support their
operation.
Epilogue
So, we have come to the end of this part of the journey and I hope to have given you a good
understanding of how to drive various types of motor with the Arduino platform. There are many
examples on the Web of how to use the Arduino microcontroller for some very interesting projects, but
the purpose of this book is to give you all of the basic information you need in one place to get you
started and present it in a clear and systematic way. In the subsequent books in the series we will use
this knowledge to look at other focus areas and create ever more challenging, creative and interesting
projects. Whatever your goals are, I hope to have inspired you and helped you some way to achieving
those goals.
If you like this book and the series of books, please leave a review on the Amazon website. For the latest
news on other books in the series you can following my Facebook page, Twitter or follow me as an
author on Amazon.
About the Author
Gary Hallberg has over 34 years of experience in the telecommunications and data communications
industries. He started his career working with circuit switched voice and data technologies in PDH and
SDH environments and then moved on to work with early packet switched networks using Frame Relay
and ATM. His career shifted focus to IP routing when the Internet started to grow, designing large scale
service provider networks using leading edge equipment from vendors such as Juniper and Cisco. Gary
attained Cisco Certified Professional status during this time. Presently, he is working for a global
equipment vendor with a focus on Ethernet networks and is at the forefront of network evolution,
providing infrastructures for SD-WAN, Network Functions Virtualization, SDN and 5G backhaul. Gary
has a Bachelor of Engineering degree in electronic engineering and a Master of Philosophy degree in
robotics and manufacturing.