Tutorial 7: Arduino, Motors and Controls An electric motor is a device that converts electric current (AC or DC) into rotary motion. The speed of a DC motor varies with the voltage applied to the terminals – higher voltage results in higher speeds. This suggests a simple way to control the speed of a DC motor: simply raise the voltage if we need more speed, or lower it if we want the motor to slow down. Controlling the Speed of the Motor In examining the Arduino board, however, we notice that there are no analog outputs. The digital outputs can take on two states, 0V and 5V, but nothing in between. It would seem that we are limited to turning the motor off, or running it at full speed – not a happy situation! Luckily, we have another alternative: pulse-width modulation, or PWM. In PWM, we send voltage pulses to the motor. If we want the motor to spin slowly, we send narrow pulses, and if we want high speed, we send wide pulses. Note that in all cases the frequency of the pulses remains unchanged, we vary the duty cycle of the pulses, as shown in the figure below. V V t Low duty cycle (12.5%) t High duty cycle (87.5%) The pulses are sent to the motor at a very high frequency (approx. 490Hz, except for pins 5 and 6, which are approx. 980Hz) such that the inertia of the rotor prevents noticeable fluctuations in speed. The speed of the motor is then governed by the average voltage, which is given by 𝑉𝑎𝑣𝑔 = duty cycle · 𝑉ℎ𝑖 where Vhi is the voltage at the high state, or 5V. The Atmega328 makes PWM very easy to implement. Simply use the analogWrite statement to send a digital output pin a value between 0 and 255 (8 bits) and the Atmega converts this to a series of high-frequency pulses with duty cycle between 0 and 100%. Powering the Motor The LED circuits that we made last week required very low current. A motor requires much more current, especially if we are using it to drive a load. If we hooked the motor directly to the Arduino output, we would likely send too much current through the Atmega328, causing damage. Instead, we will use the Arduino to switch on and off a power transistor, which can handle much higher currents. 1 The circuit below shows one way of driving a motor with a transistor. The current is switched on and off by the (very small) current flowing into the base of the transistor. A diode is connected between the collector and emitter to prevent reverse voltages from developing when the motor stops suddenly. Note that one of the motor leads is connected to 9V (the voltage delivered by the wall-wart), which is available from pin Vin on the Arduino board. 9V (Vin) PM From pin 3 R1 We will use the TIP102 power transistor in this laboratory, which can handle up to 8A of current flowing through its collector. Because it is actually two transistors in one package, it has a terrifically high current gain (approx. 1000). Thus, we need only supply a small amount of base current to be able to switch on high currents to the motor. Let us aim for a base current of 1mA. According to the datasheet, the voltage drop between the base and the emitter is 2.8V. The voltage across the resistor is then (5V - 2.8V = 2.2V). The current flowing into the base is then 𝑖𝑏 = 2.2𝑉 𝑅1 For a base current of 1mA, we should choose a resistor of 𝑅1 = 2.2𝑉 = 2.2𝑘Ω 1𝑚𝐴 The Code The code fragment below reads the voltage input from analog input 0 (the potentiometer) and sends a PWM signal to digital output pin 3. The duty cycle of the PWM signal is proportional to the voltage read by analog input 0. Since the PWM requires a value between 0 and 255 (8 bits), we must divide the analog input (10 bits) signal by 4. The nice part about PWM in the Atmega chip is that the PWM signal will continue to be sent to the digital output pin even when the processor is busy doing something else (like writing to the serial port). 2 Notice also that we are defining the potentiometer pin to be number 14. Arduino numbers the analog pins 14-19 internally (the digital pins are numbered 0-13). // Motor drive using PWM // This program uses a potentiometer to // control the speed of a motor. int potValue; int potPin = 14; int PWMPin = 3; void setup() { Serial.begin(9600); // initialize serial communication } void loop() { potValue = analogRead(potPin); // read value from analog input 0 Serial.println(potValue); // send value through serial port delay(10); // wait for ADC to stabilize analogWrite(PWMPin,potValue/4); } Challenge 1: Build a circuit and write an Arduino program to control the speed of the fan using the potentiometer. Display the potentiometer reading on the Serial Monitor. 9V (Vin) PM From pin 3 R1 C1 Simple Filtering You probably noticed that the fan motor makes an awful whining noise, especially at low speeds. This occurs not because the fan is cheap (which it is!) but because of the constant turning on and off from the electrical pulses being sent by Arduino. In fact, if you measured the noise, you would find that its frequencies correspond to the PWM frequency, plus several harmonics. It would be nice to smooth out the pulses, so that the transistor receives a steady (or almost steady) voltage from Arduino. 3 Luckily, there is a simple fix for this problem – a filter circuit. The diagram above shows that by adding a capacitor we can create a first-order lowpass filter. Remember that the name indicates that it passes low frequencies through, while attenuating high frequencies. In the configuration above, the cutoff frequency is calculated as 𝑓𝑐 = 1 2𝜋𝑅1 𝐶1 If we choose 10μF for the capacitor (and keeping 2.2k for the resistor), the cutoff frequency is 7.2Hz, which is well below the 490Hz PWM frequency. The only downside to this filter is that the fan may be a little more sluggish in responding to our commands. Try adding the capacitor and observe the difference in noise and responsiveness. On-Off Control In the preceding exercise, you controlled the speed of a DC motor through Pulse-Width Modulation. When you wanted the motor to spin faster, you rotated the potentiometer and Arduino sent pulses of longer duration to the motor. If you wanted to, you could create a plot of potentiometer position vs. fan speed, so that another user could make the fan spin at a desired speed simply by rotating the potentiometer to the correct position. A schematic of this control scheme is shown in the figure below. It is called “open loop” control, because there is no sensor to tell if the fan is actually spinning at the desired speed. Potentiometer (setpoint) Analog Input (ADC) Digital Output (PWM) Motor/Fan System Fan speed (Output) Arduino The main problem with this type of control happens when there is a long-term disturbance in the fan system. For example, a piece of paper might partially block the fan, which would slow it down. In this situation we would like Arduino to send more power to the fan, but Arduino has no way of knowing that the fan has slowed down. To make our system more robust, we might choose to include a speed sensor, or tachometer. If we feed the tachometer signal back into the Arduino, it will know if the fan is moving too fast or too slow, and can be programmed to respond accordingly. The simplest type of response is called “onoff” control. In on-off control, if the fan is moving too slowly, we apply full power, and if the fan is moving too quickly, we shut power off. A block diagram of on-off control is shown below. 4 Potentiometer (setpoint) +_ Error Positive error turn on motor Negative error turn off motor Motor/Fan System Fan speed (Output) Optical sensor Tachometer Arduino There is an important new feature in the diagram above: the summation block. In this block, the setpoint and tachometer signals are subtracted from each other – the result is the error. In on-off control, a positive error indicates that the fan is moving too slowly, and we should apply full power. Conversely, a negative error means the setpoint is lower than the actual speed, and we should cut power to the fan. Test tube Fan 33 Resistor (heater) LM34 The Temperature Control Project The diagram above shows the system we wish to control. In a sense, it is identical to the temperature control system in your house: if the temperature is too low, the heat is turned on, and if it is too high, a cooling system is applied (in this case, a fan). The heat is supplied by a 33Ω resistor rated at 1/4 W. Since the wall-wart power supply is rated at 7.5V, and there is a 2V drop across the transistor, we can expect 5.5V = 167mA 33Ω to flow through the resistor. This translates into (167mA)2 (33Ω) = 0.92W of power, which is more than the 1/4 W for which the resistor is rated. In other words, the resistor will get hot when power is applied (don’t touch it!) Power to the resistor is controlled by another transistor, in the same way we control power to the fan motor (see figure below). 5 7.5V (Vin) LM34 33 , 1/4W resistor 2k From digital output pin Gnd V+ Vout We measure temperature using the LM34 “Precision Fahrenheit Temperature Sensor”. The LM34 is a sensor whose voltage output is linearly proportional to temperature, with a scaling factor of 10.0mV/°F. Thus, at 72°F the LM34 outputs a voltage of 0.72V. The LM34 is wired as shown in the diagram at right. The formula for temperature conversion in Arduino is then 𝑇 (℉) = ( 𝑉𝑓𝑠 (V) 1000mV ℉ )( )( ) · 𝑁𝑎 1023 V 10mV where Vfs is the full-scale voltage of the analog input (5V by default, but see below) and Na is the number returned by the ADC (between 0 and 1023). The Analog Reference Pin We expect that the temperature inside the incubator will vary across a limited range; say between 50° F and 120° F. For the LM34, this corresponds to an output voltage of between 0.5V – 1.2V, which is only a small fraction of the 0-5V range of the analog input. Recall that the range of 0-5V is represented by the numerical range 0-1023 after conversion in the ADC. The range 0.5V – 1.2V will be converted to numbers between 102 and 246 by the ADC, which gives only 246 − 102 = 144 possible values for temperature between 50° and 120°F. T t The result of this is called quantization error. Quantization error occurs when an analog signal is converted into a limited range of digital values, as shown in the figure above. This occurs for two reasons, typically. First, an inexpensive ADC may only have 6 bits (64 values) or so to represent the 6 full analog range. This does not pertain to our situation, since we have a full 10-bit converter. Second, we may only need a limited part of the full analog range; e.g. 0.5-1.2V out of a full range of 0-5V. 3.3V R Analog Reference voltage 32k Arduino The solution to this problem is to use a different analog reference for full range. Luckily, the Atmega chip has a built-in analog reference pin, which can be used to specify a different full-scale analog voltage than the default 5V. The way this works is shown in the diagram above. The Atmega chip has an internal 32k resistor; the analog reference voltage is taken at the junction between it and a usersupplied external resistor. We use the voltage divider law to find the analog reference: 𝑣𝑟𝑒𝑓 = 32kΩ · 3.3V 𝑅 + 32kΩ For example, if we use R = 47k, then the analog reference voltage would be 1.34V. In the Arduino code we use the statement analogReference(EXTERNAL) to tell Arduino that 1.34V should now be converted to 1023, instead of the default 5V. As it turns out, this reference voltage will work well for the LM34, since it allows for a maximum temperature of 134°F – slightly higher than what we expect for the incubator. When you connect your own resistor, make sure to measure the actual AREF voltage so that your program is calibrated correctly. Challenge 2: The Incubator Build the “incubator” as shown in the diagram at the middle of page 5. Write a program that keeps the inside of the incubator between 80°F and 82°F. Display the temperature (in °F) using the Serial Monitor. Answer the following questions (verbally). 1. What is the overshoot and undershoot of your system? 2. How long does the air temperature keep rising after you turn off the heat? 3. Why does it do this? Construction Hints 1. Use a “star ground” approach for building this circuit, especially for the ground pin of the LM34. That is, connect the ground pin of the LM34 directly to a GND pin on the Arduino 7 board, instead of through a ground bus on the breadboard. Since the ground bus on the breadboard will be handling large currents (for the fan and resistor) there will be a voltage drop between the ground bus and the GND pin on the Arduino board – the ground bus will not remain exactly at ground! Since the temperature reading is tied to the voltage between the middle and ground pins on the LM34, a nonzero ground pin will give erroneous temperature readings. The star ground approach is commonly used in hi-fi systems for the same reason. 2. Use CAT5 wires to make the connections on the breadboard, and make everything as neat as possible. Since this is a more complicated circuit than before, troubleshooting will probably be necessary. Troubleshooting a “spaghetti circuit” is very difficult and time-consuming! Parts List Resistors 100Ω, 1/2W (1) 2.2k (2) 47k (1) potentiometer (1) Capacitors 10μF electrolytic (1) Semiconductors 1N4148 small signal diode (2) TIP102 Darlington transistor (2) LM34 temperature sensor (1) Hardware 5V computer fan (1) small test tube/beaker (1) “wall wart” power supply (1) Arduino UNO (1) breadboard (1) jumpers and CAT5 wire 8