Automatic room temperature controlled fan using arduino uno microcontroller
ME 80 Lab Manual: Control Systems Experiments
1. Laboratory Manual:
ME 80: System Dynamics and Controls
John Webster, Susan Zheng, and William Messner
ME 94 Project—Fall 2014
2. 2
Table of Contents
2 - Table of Contents / Arduino Resources
3 - How to use this manual
4 - Lab 1: Introduction to Arduino and control systems
10 - Lab 2: System control with a photoresistor
15 - Lab 3: System control using a motor with quadrature
encoder
22 - Lab 4: System Identification from step response
26 - Lab 5: Time domain frequency response of motor system
A note on available Arduino resources:
The Arduino is an open source pla orm with many sample code op ons posted
on the Arduino playground site:
h p://playground.arduino.cc/
The code in these labs has been adapted from various examples on this site.
Some labs have links to specific topics for more informa on.
3. 3
How to use this manual:
Each Lab exercise in this manual is comprised of three parts:
1. Pre‐lab and setup: Before the lab, assemble the circuit using the diagram provided. For some of
the labs, there will be test or calibra on code to ensure the circuit is connected properly and the
Arduino is gathering data.
2. In Lab: Read and understand the included code, which is bordered in blue. There are comments
highligh ng important sec ons. Use this code to gather data, which can be cut and pasted into an
Excel spreadsheet.
3. Post‐Lab: Modify or write new code to change the func on of the circuit or controller. Some labs
include data analysis .
To ini ally set up your Arduino and breadboard, use the
adhesive to tape the breadboard in place, and carefully
snap the Arduino into the clips. Be gentle with this, as the
boards can snap easily during this step.
To connect the Ardunio to your computer,
connect the serial cable as shown. The
computer can only provide limited current,
so connect the 9V using the provide cable.
The power LED should light green.
4. 4
Lab 1:
Introduction to Arduino
and control systems
Before Class
1. Download the appropriate Arduino so ware for your OS from www.arduino.cc/en/Main/So ware
2. Assemble the circuit shown in Fig.1 below.
3. Read the breakdown of the code for Part 1. Most Arduino programs follow this structure.
4. Enter the code on the following page into the Arduino window to make your LED flash on and off every second.
Materials
The Arduino microcontroller is a powerful way to use your computer to control
devices in the real world. This lab will introduce you to the basics of se ng up
and using the Arduino to control a simple set of flashing LEDs.
Arduino board, breadboard, and USB cable
6 red LEDs
6 330 Ω resistors (informa on on resistor color‐coding can be found here: resistor chart )
1 10K Ω resistor
1 push bu on switch
Assorted jumper cables
Figure 1: Setup for 1 blinking LED. Note that LEDs have a posi ve and nega ve
side and must be oriented correctly to func on. For this circuit, the posi ve wire
should be connected to pin 2. Use a 330 Ω resistor in this circuit.
5. 5
//Blinking LED
//This code will cause 1 LED mounted on pin 2 to flash on and off every second
int LEDpin=2;
void setup() {
pinMode(LEDpin, OUTPUT);
}
Comment sign (//) allows for titles and explanations,
Light grey text indicates a comment
Creates an integer variable and assigns a value to it
Breakdown of Code for Part 1
Semicolon follows every command like
the period at the end of a sentence
In this case, LEDpin is assigned the value 2
Assigns functions to pins on the Arduino board
Sets pin 2 (LEDpin) to output mode
Brackets indicate beginning and end of void xxxx () commands
void loop(){
digitalWrite(LEDpin,HIGH);
delay(1000);
digitalWrite(LEDpin,LOW);
delay(1000);
}
Creates a repeating loop
Turns LEDpin (2) on to High (5 Volts)
Waits 1 second (1000 milliseconds)
Turns off LEDpin
Waits another second
Bracket indicates end of void loop () command
6. 6
//Blinking LED
//This code will cause 1 LED mounted on pin 2 to flash on and off every second
int LEDpin=2;
void setup() {
pinMode(LEDpin, OUTPUT);
}
void loop(){
digitalWrite(LEDpin,HIGH);
delay(1000);
digitalWrite(LEDpin,LOW);
delay(1000);
}
Copyable code for Part 1
In Lab
1. Expand the circuit to the 6 LED and switch configura on shown
in Fig. 2
2. Using the example code from Part 1, write your own code us‐
ing the same format to make the LEDs flash in sequence.
3. Using the code for Part 2, use the bu on to create a simple
counter.
Figure 2: Setup for part 2. The 10K Ω resistor connects the
push bu on switch to ground.
7. 7
Code for Part 2:
const int Button = 11;
int buttonPushCounter = 0;
int buttonState = 0;
int lastButtonState = 0;
void setup() {
pinMode(Button, INPUT);
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
Serial.begin(9600);
}
void loop() {
delay(100);
buttonState = digitalRead(Button);
Creates integer variables
Sets up board outputs
Initiates serial communication, click
icon to view
Checks if the button is pressed
Code con nued on next page
8. 8
if (buttonState != lastButtonState) {
if (buttonState == HIGH) {
buttonPushCounter++;
Serial.println("on");
Serial.print("number of button pushes:");
Serial.println(buttonPushCounter);
}
else {
Serial.println("off");
}
}
lastButtonState = buttonState;
if (buttonPushCounter == 1) {
digitalWrite(2, HIGH);
}
if (buttonPushCounter == 2) {
digitalWrite(3, HIGH);
}
if (buttonPushCounter == 3) {
digitalWrite(4, HIGH);
}
if (buttonPushCounter == 4) {
digitalWrite(5, HIGH);
}
If the button state has changed, a button
press is registered
The number of button presses is output
to the serial monitor (println is equivalent
to hitting Enter on the monitor)
A series of IF statements turns on one more
LED for every button press
Code con nued on next page
9. 9
if (buttonPushCounter == 5) {
digitalWrite(6, HIGH);
}
if (buttonPushCounter == 6) {
digitalWrite(7, HIGH);
}
if (buttonPushCounter > 6) {
buttonPushCounter = 0;
digitalWrite(2, LOW);
digitalWrite(3, LOW);
digitalWrite(4, LOW);
digitalWrite(5, LOW);
digitalWrite(6, LOW);
digitalWrite(7, LOW);
}
}
Post Lab
1. Draw the system block diagram of each varia on
of the circuit
2. Are any of these systems feedback loop systems?
3. Using the code for part 3, use the bu on circuit
to create a binary counter. Hint: (use if {}
statements for each of the LEDs and the %
command to determine remainders.
For example, the first LED lights on odd numbers, so
the code would be
if (buttonPushCounter %2 ==1) {
digitalWrite(2, HIGH);
}
else {digitalWrite(2, LOW);
}
After 6 presses, the counter resets to 0,
and all the LEDs are turned off
Summary of Deliverables:
1. Block diagram of the 2 LED systems
2. Arduino code for a Binary Counter
10. 10
Lab 2:
System identification and control with a
photoresistor
Before Class
1. Assemble the circuit shown in Fig. 1below, and place it in the kit box.
2. Using the instruc ons for Part 1 on the next page, write code to calibrate the LED
and photoresistor setup.
3. Using the output from the serial monitor, plot a graph of LED output vs.
photoresistor input in Excel.
Materials
The photoresistor adds a sensor component to control the brightness of the LEDs as a
closed loop system. This lab will implement two controllers: Propor onal and
Propor onal‐Integral, and compare their effec veness.
3 yellow LEDs (higher energy than red ones)
3 330 Ω resistors
1 10 KΩ resistor
1 photoresistor
Arduino and breadboard
Jumper cables and alligator clips as appropriate
Fig. 1. Circuit for Lab 2.
‐Note that the 330 Ω
resistors are for the
LEDS and the 10KΩ is
for the photoresistor.
‐Remember that LEDs
must be oriented
with short end to
ground
11. 11
Part 1: Calibra ng the photoresistor
With the Arduino and breadboard inside the kit box (to limit external light), implement the fol‐
lowing code to calibrate the photoresistor. Using the output from the serial monitor, plot the
resistor value vs. the output value by copying the serial values into Excel. Use values up to the
END OF READINGS message. Record the resistor value with output 0, the maximum resistor val‐
ue, and at what brightness it is reached.
//LAB 2 Part 1
int value=0;
int brightness = 0;
void setup () {
pinMode(A0, INPUT);
pinMode(3, OUTPUT);
Serial.begin(9600);
Serial.print("Value");
Serial.println(" Brightness");
}
void loop (){
analogWrite(3,brightness);
delay(100);
value=analogRead(A0);
Serial.print(value);
Serial.print(" ");
Serial.println(brightness);
delay(100);
brightness = brightness+1;
if (brightness >255){
Serial.println("END OF READINGS");
}
}
NOTE: The brightness is being changed using a
Pulse Width Modula on (PWM) signal. This
signal can vary from 0 (on 0%) of the me to
255 (always on). A PWM signal rapidly turns
the LED (or motor or anything else) on and off
to make it dimmer (or move more slowly).
Because the maximum PWM output is 255, the
program will output an END OF READINGS message
Every time the code completes 1 loop, the
brightness output increases by 1
Code con nued on next page
12. 12
//LAB TWO STEP RESPONSE:
int SensorValue;
double currentMillis;
double previousMillis = 0;
long interval = 10;
void setup(){
pinMode(A0, INPUT);
pinMode(3, OUTPUT);
Serial.begin(9600);
Serial.print(" ");
Serial.print(" ");
Serial.println("Time");
Serial.print(" Sensor Value");
}
void loop(){
digitalWrite(3, HIGH);
currentMillis = millis();
if (currentMillis - previousMillis > interval){
previousMillis = currentMillis;
SensorValue = analogRead(A0);
Serial.print(currentMillis/1000);
Serial.print(" ");
Serial.println(SensorValue);
}
}
Creates a 32 bit (10 digit)
integer
This code uses the milils() function instead
of a delay() function. millis() records the
number of milliseconds since the program
started running for more accurate timing.
With an interval value of 10, the code prints
a new sensor value every 10 milliseconds.
Part 2: Iden fying the system step
response
A number of factors in a system can be
iden fied by observing the system response to
a step input.
1. Enter the code provided and use the serial
monitor to plot the output vs. me in Excel
2. Using the methods described in Lecture 9,
iden fy the system parameters. Is this a 1st
or 2nd order system?
In Lab
13. 13
int brightness = 0;
float Kp = 0.1;
int outputValueint = 0;
int error=0;
int refValue = 800;
int SensorvValue;
float Output;
double Time;
void setup () {
pinMode(A0, INPUT);
pinMode(3, OUTPUT);
Serial.begin(9600);
Serial.print("Time");
Serial.print(" SensorValue");
Serial.print(" LEDbrightness");
Serial.println(" Error");
}
void loop (){
analogWrite(3,brightness);
delay(100);
int SensorValue = analogRead(A0);
error = refValue - SensorValue;
Time = millis();
Serial.print(Time);
Serial.print(" ");
Serial.print(SensorValue);
Serial.print(" ");
Serial.print(brightness);
Serial.print(" ");
Serial.println(error);
Output = Kp*error;
brightness = (int) Output;
if (brightness == 256){
brightness = 255;
}
if (brightness > 255){
brightness = 255;
}
if(brightness < 0){
brightness = 0;
}
}
Part 3: Propor onal Control
1. Using the code provided, implement a propor‐
onal controller on the system and plot the re‐
sponse vs. me in Excel.
2. Is there s ll error a er the system has been
running for a while? What is the value?
Your reference value should be below the
saturation value identified in Part 1
If the system is not arriving at a steady
state, reduce the Kp value.
14. 14
Post lab: Propor onal + Integral control
Using the above block diagram and the code provided for
Part 2, write code to add integral control to your propor on‐
al controller. The shell code is provided below:
Loop:
Write output to LED
Read sensor value
Error = Reference — sensor value
error_integral = Error + error_integral
Output = Kp *error + Ki* error_integral
Plot your output value in Excel and compare to propor onal
control. Is this controller faster? What is the difference in
steady state errors?
Summary of Deliverables:
1. Calibra on plot with photoresistor values at PWM of 0,
photoresistor satura on value, and PWM output where this
occurs.
2. System response plot of Propor onal controller and steady
state error value
3. PI controller response plot and steady state error value.
15. 15
Lab 3:
System control using a motor with a
quadrature encoder
Before Class
Materials
This lab will implement the same Propor onal and Propor onal‐Integral controllers
used in Lab 2 on a motor with a mounted encoder to control the motor speed.
1 Pololu 9.7:1 Gearmotor with 48 CPR Encoder
1 H bridge integrated circuit
Wires and jumper cables as necessary
Arduino and Breadboard
Fig. 1: Circuit for Lab 3
1. Assemble the circuit as show below, using the diagrams on the fol‐
lowing page to assist in connec ng the H‐bridge
2. Write code using the shell provided to power up the motor and en‐
sure it is working correctly
16. 16
Part 1: The H‐bridge
The H‐bridge integrated circuit contains two pairs of transistors which change the polarity of an
input voltage, allowing the direc on of the motor to be changed using code instead of physically
modifying the circuit. It also provides the maximum voltage and amperage to the motor while
using a digital PWM (see Lab 2, page 8 for an explana on) input to specify the speed.
Figure 2: Diagram H Bridge transistors
Figure 3: The H‐bridge chip. Note the indent marking
the top of the chip. Make sure this indent is poin ng
towards the top of the breadboard in your circuit.
17. 17
Figure 4: Wiring diagram for the H‐bridge. To
build the circuit in Figure 1 connect the pins as
follows:
H‐bridge pin Connects to
1 Arduino pin 9
2 Arduino pin 7
3 Motor Lead 1
4,13 GND
5,12 GND
6 Motor Lead 2
7 Arduino pin 6
8 Arduino Vin
16 +5V
To connect to the motor:
Color Func on
Red Motor Lead
Black Motor Lead
Green encoder GND
Blue +5V
Yellow
encoder A output (Arduino
pin 2)
White
encoder B output (Arduino
pin4)
To connect to the Arduino:
18. 18
Part 1: Tes ng the motor
To ensure the motor is working correctly, write
code to turn the motor on using the H‐Bridge.
Shell code is provided below:
1. void setup(){
—Set pins 6, 7, and 9 to OUTPUT
—Digital write pin 6 to HIGH and pin 7 to LOW
—Digital write pin 9 to HIGH
2. void loop(){
}
— leave this empty, this code just runs the motor
con nuously.
Part 2: Implemen ng Propor onal Control
1. Use the following code to implement a propor‐
onal controller for the speed of the motor.
This code uses an Interrupt to read the encoder
while rapidly switching back and forth to calculate
and output the velocity and run the controller.
More informa on here: interrupts
2. Plot the Motor Speed vs. Time and observe the
steady state error.
To ensure the H‐bridge is func oning properly, set
6 to LOW and 7 to HIGH and see that the motor
turns the other direc on.
19. 19
#define encoder0PinA 2
#define encoder0PinB 4
volatile long encoder0Pos=0;
long newposition;
long oldposition = 0;
double newtime;
double oldtime = 0;
double vel;
int refSpeed = 200;
int error = 0;
float Output;
float kp = 0.2;
void setup()
{
pinMode(9, OUTPUT);
pinMode(7, OUTPUT);
pinMode(6, OUTPUT);
digitalWrite(7, LOW);
digitalWrite(6, HIGH);
digitalWrite(9, HIGH);
pinMode(encoder0PinA, INPUT);
digitalWrite(encoder0PinA, HIGH);
pinMode(encoder0PinB, INPUT);
digitalWrite(encoder0PinB, HIGH);
attachInterrupt(0, doEncoder, RISING);
Serial.begin (9600);
Serial.println(" ");
Serial.println(" ");
Serial.print("Time");
Serial.print(" Motor Speed");
Serial.print(" Output");
Serial.println(" Error");
}
Code for Part 2
Calls doEncoder function as an
Interrupt
If speed is output as negative (or
a number greater than 1,000,
switch the HIGH and LOW values
on pins 6 and 7 to change motor
direction with the H-bridge
Code con nued on next page
Adjust Kp if system is not
stabilizing or is responding slowly.
The volatile keyword lets the pro-
gram know the value will change
with the Interrupt, so it will not
remain the same every loop
20. 20
void loop()
{
newposition = encoder0Pos;
newtime = (.000001*micros());
vel = abs((newposition-oldposition)/(newtime-oldtime));
error = refSpeed - vel;
Output = kp*error;
analogWrite(9,Output);
if(Output == 256){
Output = 255;
}
if(Output > 255){
Output = 255;
}
if(Output < 0){
Output = 0;
}
Serial.print (newtime);
Serial.print (" ");
Serial.print(vel);
Serial.print (" ");
Serial.print(Output);
Serial.print (" ");
Serial.println(error);
oldposition = newposition;
oldtime = newtime;
delay(50);
}
Reads current runtime in
microseconds and converts to
seconds
Calculates rotational speed in
counts/sec by dividing difference
in encoder position and dividing
by the elapsed time in seconds
Proportional controller section
(same as in Lab 2)
Code con nued on next page
21. 21
void doEncoder()
{
if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
encoder0Pos++;
} else {
encoder0Pos--;
}
}
The doEncoder function is
run every time the encoder
value changes in the
Interrupt
Post lab: Propor onal + Integral control
Using the above block diagram and the code provided for
Part 2, write code to add integral control to your propor on‐
al controller. The shell code is provided below:
Loop:
Write output to Motor
Read encoder value
Error = Reference — sensor value
error_integral = Error + error_integral
Output = Kp *error + Ki* error_integral
Plot your output value in Excel and compare to propor onal
control. Is this controller faster? What is the difference in
steady state errors?
Summary of Deliverables:
1. Plot of Motor Speed vs. Time for Propor onal and PI
controllers
2. Steady state error values
22. 22
Lab 4:
System Identification from Step Response
Before Class
Materials
This lab will use the same motor with encoder to determine the system proper es and
transfer func on from the motor response to a step input—in this case from a low to
high speed.
1 Pololu 9.7:1 Gearmotor with 48 CPR Encoder
1 H bridge integrated circuit
Wires and jumper cables as necessary
Arduino and Breadboard
Fig. 1: Circuit for Lab 4
1. Assemble the circuit as show below. It is the same circuit as Lab 3, just with a bu on connec ng
one of the motor leads to the 5V output on the Arduino.
2. Using the code you wrote in Lab 3, Part 1, test that the bu on is working correctly by uploading
the code and pressing the bu on. The motor should speed up. If it shuts off, connect the jumper
from the switch to the opposite motor lead and test again.
23. 23
Part 1: System Parameters from Step Response
#define encoder0PinA 2
#define encoder0PinB 4
volatile long encoder0Pos = 0;
int newposition;
int oldposition = 0;
double newtime;
double oldtime = 0;
double vel;
const int numReadings = 11;
int readings[numReadings];
int index = 0;
int total = 0;
int average = 0;
The Pololu encoder does not read steady state speeds very accurately, as small varia ons in
ming intervals can cause large varia ons in motor output speed. To limit this effect, this
code employs a smoothing algorithm to calculate a floa ng average motor speed (based off
of 11 samples). This allows for smoother measurements while s ll maintaining enough res‐
olu on to observe the step response.
Part 1: Code
Code con nued on next page
Sets number of values in each
calculated average
24. 24
void setup() {
pinMode(9, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
Serial.begin(9600);
Serial.println(" ");
Serial.println(" ");
Serial.print("Time");
Serial.println(" Motor Speed");
attachInterrupt(0, doEncoderA, CHANGE);
attachInterrupt(1, doEncoderB, CHANGE);
digitalWrite(9, HIGH);
digitalWrite(6, LOW);
digitalWrite(7, HIGH);
for (int thisReading = 0; thisReading < numReadings; thisReading++)
readings[thisReading] = 0;
}
void loop() {
newtime=(.000001*micros());
if ((newtime-oldtime)>.00001){
newposition=encoder0Pos;
vel=abs((newposition-oldposition)/(newtime-oldtime));
total= total - readings[index];
readings[index] = vel;
total= total + readings[index];
index = index + 1;
if (index >= numReadings){
index = 0;
}
average = total / numReadings;
Serial.print(newtime);
Serial.print(" ");
Serial.println(average);
oldposition=newposition;
oldtime=newtime;
}
}
Attaches interrupts both A and B
encoders for higher resolution
Measures average
speed from the
measured motor
speeds and number
of readings
Code con nued on next page
25. 25
void doEncoderB(){
if(digitalRead(encoder0PinA) == digitalRead(encoder0PinB)){
encoder0Pos++;
}
else{
encoder0Pos--;
}
}
void doEncoderA(){
if(digitalRead(encoder0PinA) == digitalRead(encoder0PinB)){
encoder0Pos--;
}
else{
encoder0Pos++;
}
}
Recording Data:
1. To start, upload the code and open the serial monitor. The motor should spin slowly and the
velocity output should be roughly constant (make sure autoscroll is turned on).
2. Push the bu on, and the motor will spin faster, as the values on the serial monitor increase to
a second steady state value.
3. Once the steady state value is reached, turn off autoscroll, and copy the me and motor speed
data into excel and plot.
Data Analysis:
1. Using the methods described in Lecture 9 and the 7‐fold Way, fit a transfer
func on to the data, and use MATLAB to generate the Bode plot.
26. 26
Lab 5:
Time domain frequency response of
the motor system
Before Class
Materials
This lab will compare the transfer func on generated in
lab 4 with an actual sinusoidal input to the motor.
1 Pololu 9.7:1 Gearmotor with 48 CPR Encoder
1 H bridge integrated circuit
Wires and jumper cables as necessary
Arduino and Breadboard
Fig. 1: Circuit for Lab 5
1. Assemble the circuit as show below — This is the same circuit as labs 3
and 4, but the bu on switch from lab 4 will not be used.
27. 27
//Lab 5 frequency response
#define encoder0PinA 2
#define encoder0PinB 4
volatile long encoder0Pos=0;
long newposition;
long oldposition = 0;
double newtime;
double oldtime = 0;
long interval = 20;
long vel;
long Output;
int ledPin = 10;
float sinVal;
int ledVal;
const int numReadings = 10;
int readings[numReadings];
int index = 0;
int total = 0;
int average = 0;
In Lab
Use the code provided to input a sinusoidal waveform into the motor and record the response.
Copy and paste the data into Excel to plot the Output and Motor Speed vs. Time. This data will be
compared with the transfer func on derived in Lab 4.
Code for Lab 5:
Code con nued on next page
29. 29
void loop(){
for (int x=0; x<360; x++) {
sinVal = (sin(radians(x)));
Output = int(sinVal*70+185);
delay(10);
analogWrite(9, Output);
newtime = (.000001*micros());
newposition = encoder0Pos;
vel = abs((newposition-oldposition)/(newtime-oldtime));
total= total - readings[index];
readings[index] = vel;
total= total + readings[index];
index = index + 1;
if (index >= numReadings)
index = 0;
average = total / numReadings;
Serial.print(newtime);
Serial.print(" ");
Serial.print(Output);
Serial.print(" ");
Serial.println(average);
}
if (newtime - oldtime > interval){
oldposition = newposition;
oldtime = newtime;
}
}
This For loop is the heart of this
lab exercise. It outputs a sine
wave varying from 115 (the
approximate output where the
motor will spin) and 255
(maximum motor speed)
NOTE: if your motor is stopping
at any point while the code is
running, shift the sine wave up by
adding a constant to the
sinVal*70+185 and subtracting half
of that value from 70
This exercise also incorporates the
smoothing algorithm to reduce
timing errors
Code con nued on next page
30. 30
void doEncoder()
{
if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
encoder0Pos++;
} else {
encoder0Pos--;
}
}
Post Lab
A er plo ng the data in Excel, use the graphical method in Lecture 9 to
plot the data as a point on the Bode Plot of the transfer func on obtained in
Lab 4. How close is this point to the actual curve?