Skill Level: Intermediate
Table Of Contents
- Introduction
- What Is Needed
- Background Information
- Building The Circuit
- Creating The Sketch
- Testing The Design
- Adding Fault Checking
- Ramping Up And Down The Speed
- Using Variable Control
- Additional Resources
- Summary
Introduction
This tutorial will teach you how to drive small DC motors with your Arduino Uno. Specifically, we will cover
- Connecting a motor driver board and two motors to an Arduino Uno,
- Constructing a distinct Motor class that interfaces with the motor driver board to provide various methods for controlling a motor,
- Using the Motor class in a sketch to perform basic operations,
- Enhancing the sketch by checking for any faults that may be occurring on the motor driver,
- Showing how to ramp up and down a motor’s speed over a specific time frame, and
- Demonstrating how to provide variable speed control using a potentiometer.
A basic understanding of electronics and programming is expected along with some familiarity with the Arduino platform. If you are new to Arduino, or would just like to refresh your knowledge, please see our Blink: Making An LED Blink On An Arduino Uno tutorial before proceeding with this one.
This tutorial uses a solderless breadboard to build a circuit from a schematic diagram. The All About Circuit’s Understanding Schematics, SparkFun’s How to Read a Schematic, Core Electronics’ How to Use Breadboards, and Science Buddies’ How to Use a Breadboard guides are good resources for learning how to translate a schematic to a breadboard.
The resources created for this tutorial are available on GitHub for your reference.
What Is Needed
- Linux, macOS, Or Windows Based Computer With A USB Port
- Arduino IDE
- Arduino Uno (R3 available on Arduino and SparkFun; WiFi Rev2 on Arduino and SparkFun) With Compatible USB Cable
- Soldering Station (for attaching headers to motor driver board)
- Solderless Breadboard (available on Adafruit and SparkFun)
- Preformed Breadboard Jumper Wire Kit (available on SparkFun and CanaKit)
- 9 x Male/Male Jumper Wires (available on Adafruit and Arrow)
- 6 x Alligator Clip Test Leads (available on Adafruit and SparkFun)
- Adafruit DRV8833 DC/Stepper Motor Driver Breakout Board (available on Adafruit and Digi-Key)
- 1 or 2 DC Motors (available on Adafruit and PiShop) with optional propellers (available on Adafruit) and motor mounts (available on Adafruit)
- 10 KΩ Linear Potentiometer (available on Adafruit and Sparkfun)
- 6-9 Volt Power Supply, e.g. A Benchtop Power Supply (recommended) Or A 9V Battery With Corresponding Battery Clip (available on Digi-Key and Jameco)
- Small Flat Head Screwdriver
Background Information
You can find many designs, integrated circuit chips, and breakout boards used for controlling motors when searching the internet. Almost all of them are based on the H-bridge circuit that uses four transistors to drive a motor forward as well as in reverse.
I initially set out to write an article on how to build your own custom H-bridge based motor controller and interface it to your Arduino board. However, my research showed that modeling a motor in a circuit simulator is not an easy task and requires specialized test equipment that I do not have. Since I wanted to provide the appropriate details to back up my design choices, this route proved insufficient. I chose instead to use a pre-built motor controller breakout board. Going this route also has the benefits of providing extra features, e.g. over current and fault detection, that I was not planning to add to the original design.
After researching the motor controller boards available, I chose the Adafruit DRV8833 DC/Stepper Motor Driver Breakout Board based on the DRV8833 Dual H-Bridge Motor Driver chip from Texas Instruments. This breakout board has the ability to drive two DC motors (including speed control with PWM) or one stepper motor. It also includes reverse EMF and over current protection circuitry. The board also supports a motor supply voltage range of 2.7 – 10.8 V and an input logic range of 2.7 – 5.75 V.
I am using the Arduino Uno WiFi Rev2 development board connected to a macOS based computer running the desktop Arduino IDE. If you are using a different Arduino board or computer setup, the vast majority of this tutorial should still apply, however, some minor changes may be necessary.
If you need assistance with your particular setup, post a question in the comments section below and I, or someone else, can try to help you.
Building The Circuit
Before connecting any circuitry to your Arduino board, disconnect it from power and your computer. This avoids accidental damage during wiring.
The headers of the Adafruit DRV8833 DC/Stepper Motor Driver Breakout Board are not pre-attached and need to be soldered onto the board in order to easily attach the breakout board to a solderless breadboard.
Place the components and wire up the circuit on a breadboard according to the schematic diagram shown below.
If you have a limited number of Arduino GPIO pins available in your project, you can save a couple of pins without losing too much functionality. If you are not interested in directly enabling and disabling the motor driver, you can tie the SLP pin on the driver board high to leave the driver always enabled. Likewise, the board’s FLT pin can remain disconnected if you do not care about checking for driver faults.
A 9V battery can be used instead of a benchtop power supply to power the motor(s), but it may only be able to drive one motor at a time and the battery could drain quickly. Alternatively, four AA batteries in series could also be used providing a total voltage of 6 volts, but this will drastically limit your speed control capability.
The circuit should look similar to the one shown below once completed.
As you can see, I added some propellers to my motors to make it easier for me to see when the motors are active and at what speed they are running.
Once the circuit is built, connect your Arduino to your computer with the USB cable.
Creating The Sketch
Open the Arduino IDE and create a sketch named Motors. Leave the default code in the sketch for now as we will update it later.
Creating The Motor Class
We will be creating and utilizing a Motor
class that will handle all of the control mechanisms for driving a motor with the Adafruit DRV8833 DC/Stepper Motor Driver Breakout Board. Although this class was written for use with this specific breakout board in mind, it is a generic design that should still work across a wide range of motor driver boards and circuits.
Create two new tabs (files) within the Arduino IDE named Motor.h and Motor.cpp in the Motors sketch. These will be the header and implementation files respectively for the new Motor
class. To create new files within a sketch, click the down arrow on the right side of the tab bar (the bar containing the Motors.ino filename), and select New Tab from the drop down menu. Once all of the files are created, you should now see three tabs (files) in your Arduino IDE named Motors.ino, Motor.h, and Motor.cpp.
Open the Motor.h tab and add the code shown below.
#ifndef Motor_H #define Motor_H #include "Arduino.h" enum MotorCommand {Forward, Reverse, Brake, Coast}; // available motor commands class Motor { char _name[21]; // motor name (20 characters max) MotorCommand _command; // motor command uint8_t _speed; // motor speed (percentage of maximum speed) uint8_t _posPin; // motor driver positive input control pin uint8_t _negPin; // motor driver negative input control pin void _driveMotor(); // drive motor with current command and speed attributes public: Motor(uint8_t posPin, uint8_t negPin, MotorCommand command = Coast, uint8_t speed = 0, const char* name = "Unknown"); // motor instance constructor const char* name(); // get motor name void setName(const char* name); // set motor name MotorCommand command(); // get motor command void setCommand(MotorCommand command); // set motor command uint8_t speed(); // get motor speed void setSpeed(uint8_t speed); // set motor speed void drive(MotorCommand command = Forward, uint8_t speed = 100); // drive motor with specified attributes void stop(MotorCommand command = Coast); // stop motor with specified command }; #endif
The MotorCommand
enumeration on line 6 defines the available commands that we can give the motor. The Forward
and Reverse
commands drive the motor forward or in reverse. The Coast
command tells the motor to passively coast to a stop. The Brake
command actively brakes the motor, quickly bringing it to a stop.
Lines 9-13 define the Motor
class’ private member variables. These are the attributes that are used to describe and control a specific motor instance. The _ prefix denotes that the attributes are private members of the class.
The private _driveMotor()
method, declared on line 15, is used to send control signals directly to the motor driver. It is used internally by many of the class’ public methods.
Lines 18-26 declare the public methods that will be available for us to use in our sketch. The first, on line 18, is the motor instance constructor, Motor()
, that is used to create and configure a new motor instance. It also defines the default values for the command
, speed
, and name
attributes if they are not specified. The specified attributes are actively applied upon creation. For instance, if the command
and speed
arguments of Reverse
and 50
are specified, the motor will immediately move in reverse at 50% throttle.
Lines 19-24 consist of methods for retrieving and setting the various attributes of the motor, i.e. the motor’s name, current command, and speed.
The drive()
method, on line 25, drives the motor with the optional command
and speed
arguments if specified. If no arguments are specified, this method will drive the motor forward at 100% throttle. It is expected that only the Forward
and Reverse
commands will be used with this method, but it can appropriately handle the Coast
and Brake
commands as well.
The stop()
method, on line 26, stops the motor with the optional command
argument if specified. If no argument is specified, the motor will coast to a stop. It is expected that only the Coast
and Brake
commands will be used with this method, but if any command other than Brake
is specified, the method will default to Coast
. For instance, if the Forward
or Reverse
command is specified, the Coast
command will be set instead.
Open the Motor.cpp tab and add the code shown below.
#include "Motor.h" Motor::Motor(uint8_t posPin, uint8_t negPin, MotorCommand command, uint8_t speed, const char* name) { _posPin = posPin; _negPin = negPin; _command = command; _speed = speed; strcpy(_name, name); pinMode(posPin, OUTPUT); pinMode(negPin, OUTPUT); _driveMotor(); } const char* Motor::name() { return _name; } void Motor::setName(const char* name) { strcpy(_name, name); } MotorCommand Motor::command() { return _command; } void Motor::setCommand(MotorCommand command) { _command = command; _driveMotor(); } uint8_t Motor::speed() { return _speed; } void Motor::setSpeed(uint8_t speed) { _speed = speed; _driveMotor(); } void Motor::drive(MotorCommand command, uint8_t speed) { _command = command; _speed = speed; _driveMotor(); } void Motor::stop(MotorCommand command) { _command = (command == Brake) ? Brake : Coast; _driveMotor(); } void Motor::_driveMotor() { if (_command == Brake || _command == Coast) _speed = 0; // set speed to 0 if motor is stopping _speed = constrain(_speed, 0, 100); // constrain speed to valid percentage range switch (_command) { case Forward: digitalWrite(_negPin, LOW); analogWrite(_posPin, map(_speed, 0, 100, 0, 255)); // use PWM to adjust speed break; case Reverse: digitalWrite(_posPin, LOW); analogWrite(_negPin, map(_speed, 0, 100, 0, 255)); // use PWM to adjust speed break; case Brake: digitalWrite(_posPin, HIGH); digitalWrite(_negPin, HIGH); break; default: // Coast digitalWrite(_posPin, LOW); digitalWrite(_negPin, LOW); break; } }
All of the public methods should be fairly easy to understand. They are essentially just retrieving or setting the motor’s various attributes. Many of them call the internal _driveMotor()
method which is where the magic really happens. Within this method, we first make sure that the motor’s speed is set to an appropriate value and within a valid range. Then we send the appropriate signals to the motor driver’s input control pins (_posPin
and _negPin
) based on the motor’s current command and speed attributes.
The _posPin
and _negPin
pin pairs of the class correspond to the IN1 and IN2 pin pairs of the motor driver board. The board’s AIN1 / AIN2 and BIN1 / BIN2 input pin pairs control the motor driver’s AOUT1 / AOUT2 and BOUT1 / BOUT2 output pin pairs for the motors A and B respectively. The truth table below provides the basic operations.
IN1 | IN2 | OUT1 | OUT2 | Function |
---|---|---|---|---|
Low | Low | High Z | High Z | Passively coast to a stop |
Low | High | Low | High | Drive backwards |
High | Low | High | Low | Drive forward |
High | High | Low | Low | Actively brake to a stop |
This control logic is typical for most H-bridge based motor drivers and is not specific to the DRV8833 chip and board.
We can also apply pulse width modulation (PWM) to the high side of the input pins to control the motor’s speed. This is accomplished by using the Arduino analogWrite()
function where the PWM value (speed) is translated from 0-100% to the full range of PWM values (0-255) allowed on the GPIO pin.
Writing The Sketch
Now that our Motor
class is written, let’s turn our attention to using it in a sketch. Open the Motors.ino tab and replace the code with that shown below.
#include "Motor.h" #define DEBUG 1 // mode of operation; 0 = normal, 1 = debug #define OP_DURATION 5000 // operation duration in milliseconds const uint8_t DRV8833_SLP = 2; // SLP (sleep) pin of DRV8833 board const uint8_t DRV8833_AIN1 = 5; // AIN1 (motor A control 1) pin of DRV8833 board const uint8_t DRV8833_AIN2 = 6; // AIN2 (motor A control 2) pin of DRV8833 board const uint8_t DRV8833_BIN1 = 9; // BIN1 (motor B control 1) pin of DRV8833 board const uint8_t DRV8833_BIN2 = 10; // BIN2 (motor B control 2) pin of DRV8833 board Motor motorA = Motor(DRV8833_AIN1, DRV8833_AIN2); // motor A instance (uses default arguments) Motor motorB = Motor(DRV8833_BIN1, DRV8833_BIN2, Coast, 0, "B"); // motor B instance (all arguments specified) void setup() { // Serial Monitor if (DEBUG) { Serial.begin(9600); // initialize serial bus while (!Serial); // wait for serial connection Serial.println(F("Running in DEBUG mode. Turn off for normal operation.")); } // Pin configurations pinMode(DRV8833_SLP, OUTPUT); // Enable (turn on) motor driver digitalWrite(DRV8833_SLP, HIGH); } void loop() { basicOperations(); // perform basic motor control operations on motor A } void basicOperations() { // Basic operations with default attributes motorA.drive(); // drive forward at full throttle if (DEBUG) printMotorStatus(motorA); delay(OP_DURATION); motorA.stop(); // coast (soft) to a stop if (DEBUG) printMotorStatus(motorA); delay(OP_DURATION); // Basic operations with specific attributes motorA.drive(Forward, 75); // drive forward at 75% throttle if (DEBUG) printMotorStatus(motorA); delay(OP_DURATION); motorA.stop(Coast); // coast (soft) to a stop if (DEBUG) printMotorStatus(motorA); delay(OP_DURATION); motorA.drive(Reverse, 50); // drive in reverse at 50% throttle if (DEBUG) printMotorStatus(motorA); delay(OP_DURATION); motorA.stop(Brake); // brake (hard) to a stop if (DEBUG) printMotorStatus(motorA); delay(OP_DURATION); // Setting attributes motorA.setName("A"); // change name of motor to A if (DEBUG) printMotorStatus(motorA); motorA.setCommand(Forward); // drive forward at previously set speed if (DEBUG) printMotorStatus(motorA); delay(OP_DURATION); motorA.setSpeed(75); // set speed to 75% throttle with previously set command if (DEBUG) printMotorStatus(motorA); delay(OP_DURATION); motorA.setSpeed(0); // set speed to 0% throttle with previously set command if (DEBUG) printMotorStatus(motorA); delay(OP_DURATION); motorA.setCommand(Reverse); // drive in reverse with previously set speed if (DEBUG) printMotorStatus(motorA); delay(OP_DURATION); motorA.setSpeed(75); // set speed to 75% throttle with previously set command if (DEBUG) printMotorStatus(motorA); delay(OP_DURATION); motorA.setCommand(Coast); // coast (soft) to a stop if (DEBUG) printMotorStatus(motorA); delay(OP_DURATION); // Getting attributes // The command(), name(), and speed() methods retrieve the motor instance's // current command, name, and speed values respectively and are utilized // within the printMotorStatus() function used above. } void printMotorStatus(Motor motor) { const char* const command_states[] = {"Forward", "Reverse", "Brake", "Coast"}; // constant array of constant strings Serial.print(F("Motor ")); Serial.print(motor.name()); Serial.print(F(": Command = ")); Serial.print(command_states[motor.command()]); Serial.print(F(", Speed = ")); Serial.println(motor.speed()); }
Lines 6-10 define the pins of the Adafruit DRV8833 DC/Stepper Motor Driver Breakout Board connected to the Arduino.
Two instances of our Motor
class, motorA
and motorB
, are defined on lines 12 and 13. motorA
is created by specifying only the positive and negative motor control input pins leaving all other attributes set to their default values of command
= Coast
, speed
= 0, name
= "Unknown"
. motorB
is created by specifying all the attributes.
The setup()
function begins by initializing the serial bus for use with the Serial Monitor if the DEBUG
flag is set to 1
. The SLP pin of the motor driver board is pulled low by default which disables the motor driver itself. If you connected this pin to the Arduino, versus directly tying it high, then line 27 pulls the pin high to enable the driver for use.
Since we will be covering multiple examples of functionality, I separated them into their own distinct functions. Therefore, the basicOperations()
function, this first example, is the only function I am currently calling within the loop()
function. This example function covers the basic operations of controlling a motor with the Motor
class.
We first look at the basic operations (methods) of the Motor
class using default values. Beginning on line 36, motor A is driven forward at full throttle with the drive()
method. We then print the status of the motor to the Serial Monitor and wait for a period of time. The delay time is determined by OP_DURATION
(line 4), initially set to 5 seconds, which can be adjusted higher or lower to give you enough time to inspect the motor attributes in the Serial Monitor and see how the motor reacts to the command just given. We then proceed to stop the motor, using the default value of Coast
in this case.
The next section, beginning in line 43, covers the same drive()
and stop()
methods, but this time specifying the parameters. The motor is driven at different speeds and is stopped using both the Coast
and Brake
commands.
Setting specific motor attributes (i.e. name, command, and speed) are covered in the next section beginning on line 57.
The last section, really just a comment, informs the user that the printMotorStatus()
function can be viewed to show you how to retrieve the values of the various motor attributes. The command_states
array is used within the printMotorStatus()
function to print the human readable forms of the commands. Otherwise, you would just see the integer values as defined by the MotorCommand
enumeration within the Motor
class header file.
If there is something that needs further explanation, please let me know in the comment section and I will try to answer your question.
Save your program when you are done editing.
Testing The Design
Now that our circuit is built and our software is written, it is time to run and test our program.
Open the Serial Monitor window (Main Menu > Tools > Serial Monitor) so that we can see the program’s output. Upload (Main Menu > Sketch > Upload) the sketch to the board and you should see the motor start running through the basic operations while displaying the status of each operation within the Serial Monitor.
Notice how the name of the motor changes from “Unknown” to “A” partway through the operations. Also, watch how the motor responds to the various changes in direction, speed, and stopping commands.
We have covered all of the basic operations for controlling a motor with the Motor
class and an attached motor driver board. You are now armed with the information you need to use them for your own ideas and projects. But I thought it would be nice to cover some additional functionality and offer you some more examples of how you could control the motor. Please continue reading if you are interested.
Adding Fault Checking
The DRV8833 motor driver chip used in the motor driver board provides a pin, named FLT, that is driven low when the chip detects a fault, e.g. thermal shutdown or overcurrent situations. When this happens, the chip shuts down the offending H-bridge but leaves the other one operational. Once the fault has cleared, the pin is driven high again. We can watch this pin to let us know if faults are occurring during our motor control operations.
Add the DRV8833 fault pin to the list of pin constants located towards the top of the Motors.ino sketch file.
const uint8_t DRV8833_FLT = 3; // FLT (fault) pin of the DRV8833 board
Then add the following two lines just after the pin constants but before the motorA
and motorB
instance declarations.
volatile bool motorDriverFaultDetected = false; // fault detected status volatile bool motorDriverFaultCleared = false; // fault cleared status
We will use an interrupt service routine (ISR) to check for changes in the FLT pin. This routine will set the motorDriverFaultDetected
and motorDriverFaultCleared
flags as those changes occur. The volatile
keyword is used in the declarations to notify the compiler that the variables can be changed at any time by the ISR.
Next, add the ISR to the end of the sketch.
void motorDriverFaultPinChanged() { if (digitalRead(DRV8833_FLT) == LOW) motorDriverFaultDetected = true; else motorDriverFaultCleared = true; }
In the setup()
routine, add the following line to the pin configurations section to define the FLT pin as an input connected to the internal pull-up resistor of the microcontroller.
pinMode(DRV8833_FLT, INPUT_PULLUP);
After that line, but before enabling the motor driver, add the following to attach the ISR that will run when a change is detected on the FLT pin.
// Initialize interrupt service routine // Calls motorDriverFaultPinChanged() if change detected on DRV8833_FLT pin attachInterrupt(digitalPinToInterrupt(DRV8833_FLT), motorDriverFaultPinChanged, CHANGE);
Next, add the following to the top of the loop()
function so that we can check for faults on each loop cycle
checkForMotorDriverFault();
and then add the function to the end of the sketch.
void checkForMotorDriverFault() { if (motorDriverFaultDetected) { if (DEBUG) Serial.println(F("Motor driver fault detected.")); motorDriverFaultDetected = false; // reset detected flag } if (motorDriverFaultCleared) { if (DEBUG) Serial.println(F("Motor driver fault cleared.")); motorDriverFaultCleared = false; // reset cleared flag } }
This function just alerts the user if a fault was detected or cleared and resets the flags appropriately. Since the DRV8833 chip itself takes care of shutting down the offending circuity, checking for faults isn’t really necessary, but it is nice to know when it happens. Also, since this function is only run once during each loop pass, it will not show if multiple faults occurred within a single pass. For instance, if multiple faults happen to occur while running the basicOperations()
example function, only one fault will be reported. If you want more granularity in your fault reporting, you could add a checkForMotorDriverFault()
call after each operation.
Save your work and upload the updated sketch to test the code. It should function in the same manner as before, but this time, it will also let you know if any faults are occurring. When I run the sketch, I do see a fault happening on the first pass of the loop but not on subsequent passes; I don’t know why.
Ramping Up And Down The Speed
This example code shows how you can utilize the Motor
class routines to ramp up and down the speed of a motor over a specific time frame. Add the following routines to the bottom of your sketch.
void rampUp(Motor motor, MotorCommand command, unsigned long duration) { if (command == Forward || command == Reverse) { motor.setCommand(command); for (uint8_t speed = 0; speed <= 100; speed++) { motor.setSpeed(speed); delay(duration/100); } } } void rampDown(Motor motor, MotorCommand command, unsigned long duration) { if (command == Forward || command == Reverse) { motor.setCommand(command); for (int8_t speed = 100; speed >= 0; speed--) { // signed integer used to avoid rollover issues motor.setSpeed(speed); delay(duration/100); } } }
Given the command, Forward
or Reverse
, and the length of time to take to ramp up (down) the speed from 0% to 100% (100% to 0%) throttle, these routines use a for
loop to increase (decrease) the speed and pause for the appropriate amount of time between speed changes.
To utilize these functions, let’s create another example function, at the end of the sketch, that uses these functions
void rampingSpeed() { rampUp(motorA, Forward, OP_DURATION); // ramp up forward speed for OP_DURATION ms rampDown(motorA, Forward, OP_DURATION); // ramp down forward speed for OP_DURATION ms }
and then add it to our loop()
function. In addition, comment out the basicOperations()
function so that it no longer runs.
void loop() { checkForMotorDriverFault(); // basicOperations(); // perform basic motor control operations on motor A rampingSpeed(); // ramp up and down the speed of motor A }
Save your work and upload the updated sketch to test the code. You should now see motor A ramp up and down continuously.
Using Variable Control
We haven’t used motor B yet, let’s fix that by incorporating it into this last example. Here we will utilize the potentiometer attached to the Arduino to manually adjust the speed of the motor.
Add the potentiometer to our list of pins towards the top of the sketch.
const uint8_t Pot = A0; // pin connected to 10 KΩ potentiometer
Then add the potentiometerControl()
example function to the bottom of the sketch.
void potentiometerControl() { static int previousReading = 0; int currentReading = analogRead(Pot); if (abs(currentReading - previousReading) > 10) { // minimize unnecessary updates if (DEBUG) { Serial.print(F("Potentiometer reading: ")); Serial.println(currentReading); } motorB.drive(Forward, map(currentReading, 0, 1023, 0, 100)); // maps ADC reading range to percentage range before driving if (DEBUG) printMotorStatus(motorB); previousReading = currentReading; } }
This function reads the analog value of the potentiometer (line 3) and then maps that value, a range between 0 and 1023, to a range that is understood by our Motor
class, 0 to 100, to drive the motor forward (line 9) at the user selected speed. Lines 2, 4, and 11 are used to limit constantly updating the motor by only changing the motor speed when the user actually adjusts the potentiometer and in increments understood by the Motor
class (1023 / 100 ≈ 10).
If you are only using one motor in your design, remove the instantiation of motorB
, towards the top of the sketch, and change the reference to motorB
in the potentiometerControl()
function to motorA
.
Finally, add the example function to the loop()
function.
void loop() { checkForMotorDriverFault(); // basicOperations(); // perform basic motor control operations on motor A // rampingSpeed(); // ramp up and down the speed of motor A potentiometerControl(); // control motor B speed with a potentiometer }
Save your work and upload the updated sketch. When you adjust the potentiometer, you should now see the speed of the motor change accordingly along with the potentiometer and motor values being displayed in the Serial Monitor. You will also notice that the motor stalls at slower speeds. The stall speed is dependent on the specifications of the attached motor along with the motor supply voltage being used.
Before we end, now would be a good time to upload the BareMinimum sketch (Main Menu > File > Examples > 01.Basics > BareMinimum) to reset all pins back to their default states. This ensures no outputs are being driven when plugging in your board for your next project.
Additional Resources
The following is a list of additional resources you may find helpful.
- Basic DC Motor Circuits Guide
- H-Bridge on Wikipedia
- Adafruit DRV8833 DC/Stepper Motor Driver Breakout Board Guide
- Adafruit DRV8871 Brushed DC Motor Driver Breakout Guide
- Adafruit TB6612 1.2A DC/Stepper Motor Driver Breakout Board Guide
- Improve Brushed DC Motor Performance Guide
- Texas Instruments DRV8833 Product Page
- DRV8833 Dual H-Bridge Motor Driver Datasheet
- L298N Motor Library
Summary
In this tutorial, we learned how to connect and control a small DC motor with an Arduino. We built a distinct Motor class that connects to a motor driver board and provides various methods for controlling the motor. We then utilized that class in our sketch to control a motor in a variety of ways, from performing basic operations to using a potentiometer to control the speed of the motor. We even added the ability to detect and report faults occurring on the motor driver board.
The final source code and schematic used for this tutorial are available on GitHub. The GitHub version of the code is fully commented to include additional information, such as the program’s description, circuit connections, code clarifications, and other details. The comments are also Doxygen compatible in case you want to generate the code documentation.
Thank you for joining me on this journey and I hope you enjoyed the experience. Please feel free to share your thoughts or questions in the comments section below.
This tutorial is provided as a free service to our valued readers. Please help us continue this endeavor by considering a donation.
Leave a Comment