A recent post appeared on the element14 community blog on the difference between different kind of motors led me to write this post. I am going to describe how to control a DC motor using Arduino UNO boards with a motor shield. There is a variety of methods to use an Arduino board to run a motor: they differ in complexity and cost. Using a motor shield makes running a motor relatively easy, even if it may be a bit expensive. However, the motor shield can drive other kind of motors and soon or later you will find it useful to have one of them.
DC motors have two wires: the wires must be connected to a DC voltage source to let the motor spin. They can rotate in both direction, depending how the wires are connected to the voltage source. Supposing you have a DC motor with a red and a green wire, if the motor rotates clockwise when the red wire is connected to the positive lead of a battery, it will rotate anticlockwise inverting the polarity.
DC motors usually work in a relatively wide range of voltages, usually between 9 and 12 V. It is important to avoid using voltages higher than recommended by the manufacturer, while if you use a voltage source that is too low, the motor does not get enough power to rotate. The speed at which the DC motor rotate depends on the voltage applied at its terminal. Hence, if the voltage is not constant, the speed will not be constant as well. Having some inertia, a DC motor will continue spinning for a while when the power is disconnected, so, if you connect and disconnect the external power rapidly, the motor will rotate at a speed that result from the weighted average of the speeds at 0 V and at the nominal voltage applied. The weighted depends clearly on the fraction of time the voltage is in fact applied to the motor leads: the higher the fraction the higher the speed.
Arduino boards provide a source of PWM signals that can be used to this aim. PWM stands for Pulse Width Modulation. In practice you can configure some of the Arduino pins to work as PWM pins on which a 5V signal is present only for a fraction of the time, the rest of the time being at 0V. The signal, then, appears as a square wave: the duration of the pulses ranging from 0 to T, where T is a characteristic time that may depend on the particular board used and its configuration (I will return on details about that in one of the next posts). Writing a 0 on a PWM pin results in having no signal at all on that pin, because the duration of the pin is 0. In this case we say that the duty cycle of the pin is 0. Setting the pin to 255, the pin stays at 5V 100% of the time T, hence we say that its duty cycle is 100%. Of course, any intermediate value can be used and putting the pin to 125 makes the pin to stay on for T/2 and off for the next T/2. In this case the duty cycle is 50% and the average voltage on that pin is 2.5V.
A DC motor cannot be directly connected to a PWM pin because it requires too much current. That's why we need a motor shield. An Arduino shield is a board that plugs on the Arduino one, in such a way it gets connected to all Arduino pins. The shield will use some of them, the others are left free to the user.
The motor shield I am using has two DC motor channel, called A and B. According to the documentation, both channels use 4 pins. In particular, channel A, uses the digital pins 3, 9 and 12 as PWM, brake and direction control, respectively, and the analog pin A0 for sensing the current driven by the motor.
The DC motor is connected to the + and - terminals corresponding to the A channel. Another pair of terminals are used to connect an external power source, such as a battery (labelled Vin and GND), while a second motor can be connected to another pair of terminals, labelled B (see the picture, where the terminals are highlighted by a red rectangle).
The PWM pin (pin 3) is used to regulate the speed of the motor. The maximum speed can be obtained setting this duty cycle at 100%, i.e. setting the pin at its maximum vale (255). The brake pin has two states: LOW and HIGH. When HIGH, the brake prevent the motor from moving. The direction pin, too, can be either LOW or HIGH: depending on the way in which the motor is connected to the terminals it set the direction of the rotation to be clockwise or anticlockwise.
Let's then look at the code to be deployed on the Arduino memory to use the motor, starting from the setup method.
#define ADIR 12 #define APWM 3 #define ABRAKE 9 #define ASENSE A0 void setup() { Serial.begin(9600); pinMode(ADIR, OUTPUT); pinMode(ABRAKE, OUTPUT); }
The #define directives are used for convenience. This way we can identify the pin using a descriptive string rather than using integers, for which it can be difficult to remember the meaning. Sometimes you can find that this constants are not defined as above, but declaring global variables as
int ADIR = 12; int PWM = 3; int ABRAKE = 9; int ASENSE = A0;
(I put the words "global variables" in italic, because being the Arduino programming language C++, they should be called members of the main class). This is not a very good idea, because variables takes space in memory and accessing them can result in a slower execution. If you define constants by directives as above, it helps in keeping the program shorter and faster.
The code above just tells to Arduino to consider both pin 12 and pin 9 as output digital pins. The Serial.begin(9600) statement is only needed if you want to write something on the serial monitor, as we are going to do below. We can then define a function (a method, using the appropriate language) to make the motor spin for a given time in a given direction with a given speed:
float go(int speed, int direction, int duration) { int count = 0; float averageCurrent = 0.; digitalWrite(ABRAKE, LOW); digitalWrite(ADIR, direction); int startTime = millis(); int endTime = startTime + duration; analogWrite(APWM, speed); while (startTime < endTime) { averageCurrent += analogRead(ASENSE); count++; startTime = millis(); } digitalWrite(ABRAKE, HIGH); return averageCurrent/count; }
After setting to zero two variables (count and averageCurrent), this function release the brake and set the direction either LOW or HIGH. The direction is taken as a parameter and must be chosen by the programmer. It then put the current time in the startTime variable. In fact the time stored in this variable is given as the number of milliseconds elapsed since the beginning of the program, as returned by the millis() function. If the motor must stop rotating after duration milliseconds, the time at which we should stop the motor is given by startTime + duration. Setting the speed writing a number between 0 and 255 to the PWM pin the motor starts rotating (note that if speed is too low the motor may stay at rest).
We then start a loop in which we continuously measure since when the motor is running, such that when millis() returns a value higher than endTime we abandon the loop. Within the loop we also measure the current driven by the motor by means of analogRead(ASENSE), whose value is summed to averageCurrent. The count++ statement increments by one the counter called count.
When exiting the loop, the brake is activated, in such a way the motor stops. The method returns, then, the average value of the current reading (the average is the sum of the readings, divided by the number of them). It must be noted that what you measure with this statement is something that is related to the current required by the motor, but not as easy to use. First of all, the reading is a number between 0 and 1023 that correspond to a voltage between 0 and 5V. What you get from analogRead, then, is a voltage, not a current. Its value is the voltage drop across a resistor on the shield. Such a voltage raises when the PWM signal becomes on, remains constant while it stays on and decreases when it goes off, giving rise to a wave that is approximately rectangular. That means that what you measure with the above code is the average current driven by the motor in arbitrary units.
The loop() method looks like the following:
void loop() { float currentRequired = go(128, LOW, 3500); Serial.println(currentConsumption); delay(1000); currentRequired = go(128, HIGH, 3500); Serial.println(currentRequired); delay(1000); }
When Arduino is powered, the motor starts rotating in one direction (say clockwise) at half of its maximum speed (128) for 3500 milliseconds. Then stops and we get the current required on average as the return value of the go() method. After showing the result on the serial monitor, we wait one second (delay(1000)), then restart the motor in the opposite direction (same speed, same duration).
Of course this is for demonstration purposes only. You can now use the go() method to let the motor rotate as you need. In the pictures below you can see my motor connected to the Arduino shield and to a 9V battery.
The picture on the right shows how the connections are done on the motor shield.
As usual this blog post will appear on my personal web page, and its content will be part of the Scientific Arduino e-book: a freely available e-book in PDF that can be used as a standalone manual or as an addendum to Scientific Programming, a textbook to learn how to program in C for scientists published by World Scientific (available in Italian from Pearson).
Top Comments