Académique Documents
Professionnel Documents
Culture Documents
sadikdelioglu@gmail.com
Peter Dalmaris
Lecture 2
Section 1 Lecture 2
The Arduino ecosystem
Before setting up the "hello world" demo app of the electronics world (the blinking LED),
let's have a really quick look at the Arduino ecosystem. The members of this ecosystem are
the bits and pieces that come together when you build an Arduino project.
1.
2.
3.
4.
5.
Shields
6.
Components
In the beginning, way back in 2005, there was only one board. Rumour has it that it was
called the "Arduino" in honour of the place where the idea for an open-source hardware
prototyping platform was conceived. Today there are numerous boards, with a wide range
of capabilities. Processing, input/output, power consumption, size, and price are various
metrics in which modern Arduino boards dier from each other.
!Arduino-ocial boards are made by companies that work in collaboration with the Arduino
team to ensure compatibility. They are the only ones licensed to use the name "Arduino". In
return, makers of ocial Arduino boards pay a fee to the Arduino project. There are also
Peter Dalmaris
Lecture 2
many clones, derivatives and counterfeit boards that while using the open-sourced Arduino
software and schematics do not contribute to the project financially. They also are not
providing any guarantee of compatibility and quality. I recommend you only purchase an
ocial Arduino board for both peace of mind and for the nice feeling of contribution to the
project.
The Arduino IDE, or Integrated Development Environment, is the tool you use to create an
Arduino program and upload it to your board. An Arduino program is called a sketch, a
reminder that what you are actually doing is prototyping, which in turn means that anything
you do is subject to trial, testing, and change.
Peter Dalmaris
Lecture 2
The Arduino IDE can be downloaded from the Arduino website and it works on Mac, Linux
and Windows computers. Its only requirement is the Java Runtime Environment (JRE).
Simply download the IDE installer for your computer, and run it. The installer will let you
know if you need to download the JRE. Get it from http://arduino.cc/en/Main/Software.
With the IDE installed, you will be able to type, upload to the board, and debug (ie. fix) your
sketches. You also use the IDE to communicate both ways with the board: to upload your
sketches to the Arduino, and to receive messages from the Arduino.
When you download the Arduino IDE, you also get the Arduino core library. This library is a
collection of functions or methods that allow you to control the various aspects of your
board's functionality. For example, if you want to read an analogue value from pin 1, you
call the relevant method like this:
analogRead(1);
Awesomely simple! If you want to turn an LED light on, that requires "writing" a digital value
1 to a digital pin. Connect the LED through a resistor to digital pin 13 and use this method:
digitalWrite(13, HIGH);
Apart from methods like these, meant to interact with the environment, the Arduino core
library gives you many others. There are control structures, arithmetic operators,
mathematics functions and many others. All of them are listed and documented in the
references page of the Arduino project's web site.
I will be explaining the use of many of these methods as we use them throughout this
course.
Lot's of people have contributed to the Arduino ecosystem by writing their own libraries
and making them available to others. These third-party libraries often address gaps in
functionality that the Core library perhaps does not oer, or it does not oer well. In other
cases, these libraries oer alternative ways of doing things.
For example, this library allows you to add infra-red remote control capability to your
project. This one implements a web server that can run on your board, which is very useful
for remote sensing or remote control applications.
Shields
Very often, certain hardware components are used so widely that eventually a company
assembles them together on a printed circuit board so that it is easy to plug into an Arduino
board without any wires. These extension boards are known as "shields".
Peter Dalmaris
Lecture 2
The Ethernet shield is very common. It makes it very easy to add Ethernet and Internet
communications to your project. You could just buy the WizNet controller, connector and
other parts and create your own Ethernet adaptor, but it would be silly to do that.
Remember that our mission is to build prototypes so that we can implement our ideas, not
to fiddle around with problems already solved well by others!
Another popular shield is the Arduino Motor Shield through which you can control all sorts
of motors, very useful if you want to build a robot, a racing car, or a remote control lawn
mower.
Components
Finally, there are the individual components. These are typically small devices that you plug
into your board by using jumper cables and breadboards.
For example, if you want to take temperature and humidity readings, you could use the
DHT11 sensor.
If you need to measure distance, then you could use an ultrasonic distance sensor.
In sections 2 and 3 of this course we will be experimenting with many useful components.
Conclusion
Ok, that concludes a brief dive into the Arduino ecosystem. Please take a few minutes to
answer a few quiz questions before continuing with Lecture 3 where we examine the
Arduino tools and the prototyping process.
Peter Dalmaris
Lecture 3
Section 1 Lecture 3
Tools and the prototyping process
In this lecture I will show you the tools and process involved in prototyping with the
Arduino.
There is nothing Arduino-specific about either these tools or process. The Arduino is an
incredibly eective creative tool because it accelerates prototyping dramatically.
!
It all begins with an idea, a problem or a thing that you would like to build. Usually you will
have some prior knowledge of the problem area, or some informed assumptions that can
help you get started. But at the very beginning, you will not have the details or at least
some of the knowledge necessary to help you in actually building whatever you are trying to
build.
So, you go ahead and use what you know to build a prototype, version 1. This prototype
may barely work at all, but it should be doing something that you can test and learn from.
Testing yields information that you can use to adjust your original assumptions. This
adjustment is called refinement.
Peter Dalmaris
Lecture 3
Iteration after iteration will yield more and more information and result to more and more
refinements until at some point your prototype, be it version 100 or version 1000, is the
product or solution you aimed for.
Of course, the Arduino is not necessary in the prototyping process. You could do
prototyping using bare-bones electronic components down to the transistor. However with
the Arduino, you can prototype much faster because (1) you will be using general purpose
and already tested components which allow you to focus on your actual objective and (2)
you will be using high-level tools to do so.
Tools
Yet another really nice thing about prototyping with the Arduino is that you only need few
and very basic tools.
Not including the board itself, shields and other components mentioned in Lecture 2, you
can build great projects with just these items:
1.
2.
Solderless Breadboards
3.
Jump wires
4.
A multimeter
A multimeter is not required for the majority of the projects you will be building as a
beginner. However having one and getting to know how to use it will help you a lot later on.
1. Voltage AC and DC
2. Resistance
!
2
Peter Dalmaris
Lecture 3
Solderless Breaboards
!
!
Jumper wires
A jumper wire is a thin cable with pins at either end. Use them to
make connections on a breadboard and on the Arduino headers.
They come in many colours and lengths. One thing to remember:
you don't want to run out of jumper cables during your
prototyping so make sure you have plenty available!
Peter Dalmaris
1.
2.
Lecture 3
Any other colours can be used to signify the connection of dierent kind of components or
functions that each one serves. For example, you could use light coloured wires for sensors
and dark for buttons or displays. Up to you.
Power supply
The Arduino can provide power for itself and for the circuit you are building from the USB
cable that is used to connect it to your computer.
However, there are three obvious cases where you will need an external power supply:
1. If you want your sketch and gadget to continue its operation after your computer is
turned o.
2. If your circuit becomes large enough so that the Arduino can't provide enough
power
3.
Here are three things you can do to deal with such situations:
A battery pack
!
4
Peter Dalmaris
Lecture 3
Ok, with all the logistics out of the way, time to get into our first project: let's go to Lecture 4
and make an LED blink!
Peter Dalmaris
Lecture 4
Section 1 Lecture 4
The blinking LED
The basics
This is what an LED device looks like. Notice that there is a short and a long "leg". The
short leg is called "the cathode", often noted with a "k" and should be connected to
the negative ("-") voltage. The longer leg is called "the anode", often noted with an "a"
and should be connected to the positive ("+") voltage. Other devices of note that are
polarised and use similar or same terminology are transistors and certain types of
capacitors.
Symbolically, i.e. in diagrams depicting electronic circuits, an LED is depicted like this:
!
The basic characteristic of a diode is that it is a semiconducting device that allows the flow
of electricity (electrons) only towards one direction. Think of it as the equivalent of a
plumbing valve that allows water to flow only in one direction. Therefore, diodes are used in
situations where we want to restrict the directionality of electricity. Diodes are used
Peter Dalmaris
Lecture 4
extensively in applications like the conversion of current from alternating to direct, in radio
transmitters for the modulation of signals, and many others.
The symbol of a diode is almost the same as the one for an LED. The only dierence is the
presence of three little arrows which show that light is emitted from an LED:
!
Let's experiment with an LED
With the background and theory behind us, lets implement our first Arduino circuit. The aim
is to become familiar with plugging components into the breadboard, uploading and
running a sketch.
Peter Dalmaris
Lecture 4
get that I=V/R. For an LED, R is almost zero, so no matter what the V is, the I will be a very
large number.
!
Sketch
The LED isn't doing anything at the moment. To get it to light up and blink, we need to
upload a sketch to the Arduino with the appropriate instructions.
Here's the program we'll use. I'll explain how it works. You can either type it into a new
Arduino IDE editor window, or load it by selecting File > Examples > 01. Basics > Blink from
the Arduino IDE menu. If you do this, remember to change the number "13" to "9" for
variable "led". I also made it available as a text file download from the materials tab.
!
!
!
!
!
!
!
!
!
!
3
Peter Dalmaris
Lecture 4
Because this is your first ever Arduino program, I will explain a few things before continuing:
Comments
Any text following "//" or in-between "/*" and "*/" is a comment, and the Arduino will ignore
it. People use these symbols to type comments, like in this example.
Functions
An Arduino program can be broken down in parts by using functions. Functions make it
easy to create little programs within a large program, and to call each of these little
programs by name. In this example, we used 2 functions with names setup() and loop().
These are special functions that the Arduino will call itself. When the Arduino starts, it will
first call the setup() method and execute any commands it finds inside. Then, it will call
loop() again and again until you turn o the power, every time executing whatever
commands it finds inside. You can create your own functions and name them whatever you
like, as long as you don't use a reserved name (like "loop" or "setup"). Function names
can't have white spaces or other "special" characters inside them.
A function may or may not return a value when it finishes its execution. Notice that loop is
declared as void loop()? The void means that loop does not return anything when it
finishes its execution. Same thing happens with setup().
Peter Dalmaris
Lecture 4
int led = 9;
This creates a global variable named led, and stores the value 9 in it, which is of type int
(integer). A global variable is accessible from anywhere in your sketch. In this example, you
can see that inside the setup() function, there is a reference to "led", and similarly there is a
reference to "led" from inside the loop() function. On the other hand, a local variable is one
that is only accessible from within it's own context. If we had declared the led variable
inside the setup() function, then it would only be accessible by other statements inside the
setup() function and would not be accessible from the loop() function.
Arduino functions
Arduino's magic is in the functions that are build-in to the language. These functions make
it easy to control many aspects of our hardware.
Notice that in the setup() function, there is a call to the function pinMode. This function
takes in two parameters: first the number of the pin we want to configure, and second the
mode that we want to assign to this pin.
With the setup() function complete, the Arduino then starts calling the loop() function. The
first thing that happens there is calling the digitalWrite function:
digitalWrite(led, HIGH);
With digitalWrite, we assign a new state to a pin. We can rewrite this statement as
digitalWrite(9, HIGH);, and, as you can probably guess, we are changing the state
of pin 9 to HIGH, which is 5V. As soon as this happens, your LED will light up!
We want to keep this LED lit for a little while, so we use the instruction delay to keep
things as they are. delay accepts one parameter, that is the number of milliseconds to wait
for. So in our case:
delay(1000);
means: "wait for 1000 milliseconds", which is 1 second.
Then we call digitalWrite again, but this time we change the state of pin 9 to LOW,
which is 0 Volts.
digitalWrite(9, LOW);
Peter Dalmaris
Lecture 4
We wait at this state for another second, then the loop starts all over again.
So there you have it, your first Arduino circuit, and a blinking LED!
In the next Lecture, we will make the same LED, using the exact same circuit, fade on and
o, giving us a much nicer visual eect to look at. Before that, please complete the Lecture
4 quiz.
Peter Dalmaris
Lecture 5
Section 1 Lecture 5
The fading LED
!
!
!
!
!
!
!
!
!
Peter Dalmaris
Lecture 5
The most important dierence between this sketch and the one in Lecture 4, is that we now
use analogWrite instead of digitalWrite. While digitalWrite can only output a HIGH
or LOW value, analogWrite allows us to output any value from 0 to 255, or, to be more
precise, any of 256 voltage levels from 0V to 5V. The higher the voltage on the pin to which
the LED is connected, the brighter the LED will be lit.
In the loop() function, we first set the brightness of the LED, using the analogWrite
function, by selecting the pin to which the LED is connected, and the 'brightness'.
Brightness is a global variable of type integer that was initialised to be 0 when the program
starts. So, the first time that the program runs in the Arduino, this instruction will look like
this:
analogWrite(9, 0);
In the next instruction, after the comment, we calculate a new value for the brightness. The
new brightness is equal to the old brightness plus the fadeAmount, the value stored in
another global variable that we set to be 5 in the very start of the program. So, the first time
the program runs, this instruction will look like this:
brightness = 0 + 5;
Therefore, brightness will now become 5.
Peter Dalmaris
Lecture 5
Next, we use a control structure to determine if we have reached a limit for the brightness,
either the lower limit (0) or the upper limit (255). If we have, then we switch the sign of the
fadeAmount variable. The eect is that if the LED was becoming brighter because the
fadeAmount was positive, then once it reaches it brightest setting (when brightness equals
255), then fadeAmount will be changed its negative (-5) and brightness will start moving
towards zero.
In the statement:
In the same statement, the "||" is the boolean operator "OR". You can join two conditions
together, and the result will be true if one of them is true. So, in our example, whatever is
between the curly brackets will be executed if either brightness is zero OR brightness is
255.
Section 1 complete
This concludes the lectures in Section 1 of our course! In Section 2 we will look into various
sensors that allow your Arduino project to take measurements from the world around it.
Peter Dalmaris
Lecture 6
Lecture 6
About sensors
With the basics right behind us, we can now move on to the next set of lectures where you
get to learn about how to connect and use various kinds of sensors with your Arduino.
Sensors are the eyes and ears of machines: they provide environmental data. There are all
sorts of sensors, some more exotic than others. Here's a shortlist from Wikipedia:
Light
Motion
Temperature
Magnetic fields
Gravity
Vibration
Pressure
Electrical fields
Sound
Clever gadgets combine multiple sensors in order to capture a more complete snapshot of
their environment. This is similar to our human perception of the environment that is based
on multiple senses, like sight and hearing.
Each sensor that is attached to a machine requires processing power. The more sensors
attached, the greater the processing requirements on the machine. In the Arduino Uno, the
ATMega328 micro-controller is a simple computer running at a clock speed of 16MhZ
(mega-hertz). This means that this Arduino can process roughly 16 million instructions every
second. This processing resource has to be shared between all the things that your Arduino
is supposed to do, like reading values from its sensors, doing calculations, updating a
screen or other outputs, communicating with other devices, and interacting with the user.
The Arduino is fast, but it has a limit, and your design must take that into consideration.
Peter Dalmaris
Lecture 6
In the lectures in this section, we will play with the following sensors:
Orientation
Simple sensors, like the photo-resistor for measuring light, work by measuring the voltage
they provide to one of the analog sockets in the Arduino. You can do this by using the
analogRead function (the opposite of the analogWrite function that we saw in the last
lecture, Lecture 5). Other sensors are a bit more involved, and they require special software
libraries to work with the hardware. More often than not, however, these libraries are very
easy to learn and they provide useful extra features at no additional cost.
Let's get right into it, and have a look at the photo-resistor.
Peter Dalmaris
Lecture 7
Lecture 7
Measuring light
Measuring light is really easy. There are many sensors capable of detecting or measuring
light, but the photo-resistor is one of the easiest to use. A photo-resistor is simply a resistor
in which the resistance changes in accordance to its exposure to light.
You can find these devices on eBay for less that $3 for a
pack of ten. Think about what you can do with a device that
can detect light. Of course, your gadget will be able to know
if its day or night, or if the lights are on. So you could build a
gadget that that turns on a small light at the entrance of you
home when it darkens, so you don't have to walk in the dark.
You could also use a photo-sensitive device to allow two
gadgets to communicate with light; this is the principle
behind the typical television remote control where the
remote control and the television communicate using
infrared light. You could also build a simple robot that follows
a bright or dark line on the floor. Can you think of anything else you could do with a light
sensor?
We'll now create a circuit that contains a photo-resistor, and we'll use an Arduino sketch to
take light intensity readings from it.
Look at the circuit in the image below, and try to copy it. Here are a few things to be extra
careful about:
Counting from the right, the photo-resistor is connected to a socket in column 1. Its
second leg is connected to a socket in column 4.
We use a 220 Ohm resistor (or close, the exact rating is not important in this exercise) in
series with the photo-resistor in order to create a "voltage divider". More about this in a
minute.
Connect the black jumper wire to the GND socket on the Arduino, and the red to 5V. Even
if you switch these connections, this circuit would still work because we are not using any
polarised components.
Lastly, connect a green jumper wire from a socket in column 4 to A0, which is the Arduino
analog port 0.
Peter Dalmaris
Lecture 7
Notice how you connected the green cable in column 4, where the resistor and the photoresistor meet? This type of wiring is called a "voltage divider" or "impedance divider", and
its purpose is to create an output voltage that is a fraction
of the input voltage to the divider. Look at the diagram
(right):
The higher the measurement in socket A0, the more intense the light is. The closer to zero it
gets, the darker our lab is.
That's enough for now with the hardware. Let's look at the software.
Peter Dalmaris
Lecture 7
The sketch
!
Much of this sketch should be familiar to you now. There's
setup(), loop(), and delay(), which we have seen before.
Serial.begin(9600);
This creates a serial connection which the Arduino can
use to send text output to our terminal. This way the
Arduino can "talk" to us. This terminal can be opened by
clicking on Tools > Serial Monitor, and it looks like this:
!
3
Peter Dalmaris
Lecture 7
!
We will be printing the light intensity value to the serial monitor in a moment.
Next, have a look in the loop() function. In the first actual statement after the comment, we
use the analogRead(A0) function to get a reading from socket A0 ("Analog 0") and store it to
the local integer variable sensorValue. Easy, right?
We now have a value captured from the photo-resistor's voltage divider circuit, let's print it
to the monitor so we can actually see it. Also easy, just do this:
Serial.println(sensorValue);
This statement, says: "Go to the serial port, print line with content 'sensorValue'". The "ln"
part of the println function means that this particular function will create a new line after
it prints out the text that is contained within the parentheses. You could use just
Serial.print(sensorValue), but then the output would look like this:
9469459469459459469459469469469459469
... not very useful, very hard to read.
When you send this program to the Arduino, wait for it to upload, and then open up the
monitor. You will see something like this:
946
946
946
946
946
946
946
945
946
946
!
4
Peter Dalmaris
Lecture 7
The actual values will vary because of the dierences in the components you used in your
circuit compared to mine, and of course the lighting conditions in our two labs are probably
dierent. But as long as you see similar values (above 0 and below 1024), then it worked!
Questions
That was easy, right? Please try out these questions before moving on to the next Lecture,
where you will learn about measuring temperature and humidity.
Peter Dalmaris
Lecture 8
Lecture 8
Measuring temperature and humidity
In this lecture you will learn how to measure temperature and humidity. To do this, we will
use a sensor from the DHT family of temperature and humidity sensors.
Inside, this sensor is fairly sophisticated and contains hardware that does a lot of the work
that otherwise we would have to do in our Arduino sketch. As a result, as you'll see in a
minute, all we have to do is take a reading, there is no need for any conversion calculations
or any other hardware at all.
The DHT11 and DHT22 sensors, within their plastic case, contain:
A thermistor
Capacitive humidity sensors are robust solutions for measuring humidity if our application is
ok with the relatively low accuracy of this device. Both DHT11 and DHT22 have a humidity
accuracy of around 5%, and are calibrated in the factory. In a capacitive humidity sensor, a
polymer or metal-oxide material is exposed to the environment and its dielectric properties
change as the humidity changes.
Peter Dalmaris
Lecture 8
Assembly
The Arduino
A DHT11 or DHT22
A 10kOhm resistor
Peter Dalmaris
Lecture 8
Sketch
With the demo circuit assembled, lets now turn to the Arduino sketch that will drive it.
//
//
//
//
void setup() {
Serial.begin(9600);
Serial.println("DHT test");
dht.begin();
}
void loop() {
float h = dht.readHumidity(); //*Set local float variable for humidity*
float t = dht.readTemperature();//Set local float variable for temperature
// *check if returns are valid, if they are *
// NaN (not a number) then something went wrong!
if (isnan(t) || isnan(h)) {
Serial.println("Failed to read from DHT");
} else {
Serial.print("Humidity: ");
Serial.print(h);
Serial.print(" %\t");
Serial.print("Temperature: ");
Serial.print(t);
Serial.println(" *C);
//*problem*
}
delay(500); //*Introduce a delay because these sensors are* //*slow*
}
Notice that there is an inclusion of the DHT.h library in the first line.
Peter Dalmaris
Lecture 8
This library makes it easy to communicate with the DHT device. We will need to download it
and make it available to our sketch.
Now we'll run that sketch and look at the monitor output:
And that is how you connect and use the DHT22 or DHT11 sensor to the Arduino!
Questions
Please attempt the lecture questions before moving on to
lecture 9.
Peter Dalmaris
Lecture 9
Lecture 9
Barometric pressure
Measuring the atmospheric pressure has several applications. Obviously, if you are a
meteorologist, knowing the pressure at a geographical location helps forecasting the
weather. But there's more.
Atmospheric pressure is defined as the weight of a column of air above an object. As the
height of a column of air above an object changes depending on its altitude, so does the
weight of that column. Atmospheric pressure at the surface of the sea is higher than that on
the top of a tall mountain because the column of air above it is higher. Therefore, measuring
the atmospheric pressure is also a simple way of figuring out your altitude, or the altitude of
one of your flying gadgets.
The standard unit for measuring atmospheric pressure is "Pa", or "Pascals". At sea level,
the standard pressure is defined to be 101.325 kPa, or 101,325 Pa.
We will be measuring atmospheric pressure by using the BMP085 sensor. This sensor costs
around $8 on eBay. It can measure pressure from 300hPa to 1100hPa, which converts to
around 500 meters below sea level to 9,000 meters above sea level. It's accuracy is also
excellent, around 0.03hPa. "hPa" is pronounced "hectoPascal".
Another nice thing about this sensor is that it also measures the temperature.
The BMP085 talks to other devices via the I2C interface, a digital serial communications
interface that only needs two wires for communication, and two for power. One
communication wire is called SDA, and it transmits
data, while the second, SCL, is for the clock signal.
A clock signal is needed because I2C is a
synchronous interface.
!
The BPM085 measures pressure by taking
advantage of the piezo-resistive property that silicon and germanium have. This property
involves the change in resistance in those materials depending on the amount of
mechanical load that is put on them.
!
1
Peter Dalmaris
Lecture 9
Assembly
Let's puts together this circuit and try out the sensor.
We will need:
The Arduino
Peter Dalmaris
Lecture 9
Sketch
Just like in Lecture 10, to use the barometric sensor we include an external library in our
sketch. This comes from Adafruit, and you can find it on Github. Follow the instructions in
the README.txt file for setting it up, and don't forget to restart your Arduino IDE!
Here's the sketch, it comes straight of the examples that are included with the Arduino IDE.
I have added some comments to help you understand what is going on:
#include <Adafruit_BMP085.h>
Adafruit_BMP085 bmp;
void setup() {
Serial.begin(9600); //*Setup serial communication and speed*
if (!bmp.begin()) { //*Try to start the device*
//*If it is not starting, print message*
Serial.println("Could not find a valid BMP085 sensor, check
wiring!);
while (1) {}
}
void loop() {
Serial.print("Temperature = ");
Serial.print(bmp.readTemperature()); //*Read and *
//*print temperature*
Serial.println(" *C");
Serial.print("Pressure = ");
Serial.print(bmp.readPressure()); //*Read and print*
//*pressure*
Serial.println(" Pa");
// Calculate altitude assuming 'standard' barometric
// pressure of 1013.25 millibar = 101325 Pascal
Serial.print("Altitude = ");
Serial.print(bmp.readAltitude()); //*Read and print*
//*altitude*
Serial.println(" meters");
3
Peter Dalmaris
//
//
//
//
Lecture 9
And that is how you connect and use the BMP085 barometric sensor with the Arduino!
Questions
Peter Dalmaris
Lecture 10
Lecture 10
Motion
!
Knowing if something is moving is useful in many applications. The classic example is
security, where an alarm system can detect an intruder moving inside a room, so that it can
notify the police. Another common use is in home and oce automation, where you could
get the lights to turn on and o automatically depending on whether someone is still in the
room or turning on the flood light in your driveway as
your car approaches.
!
An ultrasonic motion sensor uses ultrasounds
to detect moving objects. Just like bats, an
ultrasonic sensor emits ultrasounds at
frequencies from 30khz to 50kHz and then
picks up their echo. These sensors can often
measure the time a signal takes to return, and
from that it can calculate the distance to the
object. Therefore, ultrasonic sensors can
calculate both distance from an object as well
as whether the object is moving. We will be
looking at ultrasonic sensors in the following
Lecture.
!
A microwave motion sensor works on the same principle as the ultrasonic sensor, except
that instead of ultrasounds it emits microwaves. They are still relatively cheap, and because
microwaves are much higher in terms of frequency than ultrasounds, motion can be
1
Peter Dalmaris
Lecture 10
!
!
In this lecture, we will connect a passive infrared sensor to our Arduino, calibrate it, and
turn an LED on every time motion is detected.
Assembly
Let's puts together this circuit and test out the motion sensor.
We will need:
The Arduino
One resistor, 1k
One LED
Peter Dalmaris
Lecture 10
!
Sketch
/*
* PIR sensor tester
*/
int
int
int
int
ledPin = 13;
inputPin = 2;
pirState = LOW;
val = 0;
void setup() {
pinMode(ledPin, OUTPUT); // declare LED as output
pinMode(inputPin, INPUT); // declare sensor as input
Serial.begin(9600);
}
void loop(){
val = digitalRead(inputPin); // read input value
if (val == HIGH) { // check if the input is HIGH
digitalWrite(ledPin, HIGH); // turn LED ON
if (pirState == LOW) { // we have just turned on
Serial.println("Motion detected!);
// We only want to print
// on the output change, not state
pirState = HIGH;
}
} else {
digitalWrite(ledPin, LOW); // turn LED OFF
if (pirState == HIGH){
// we have just turned off
Serial.println("Motion ended!");
// We only want to print on
// the output change, not state
pirState = LOW;
}
}
Peter Dalmaris
Lecture 10
By now, this sketch should be easy to read and understand. We start by setting constants
for the pins and values. The LED is connected to digital pin 13, and the sensor's output to
digital pin 2. We also assume that when the Arduino starts, there is no motion, so variable
pirState is set to LOW, and val, the variable to which the output state of the PIR sensor is
stored, is 0 (LOW).
In the setup() function, we set pin 13 to be output, and pin 2 to be input. We also initialise
the serial port so that we can see text output in the monitor window.
In the loop() function, we constantly read the value of the PIR sensor by using the
digitalRead(inputPin) function. This function reads voltage in the range of 0V to 3.3V (at
least for the sensor I am using), and the Arduino translates that to LOW and HIGH
respectively.
If HIGH is detected, the Arduino will set pin 13 to HIGH and this will activate the LED. If the
previous state of the sensor was LOW, then the Arduino detects this as new motion, so it
will print a message to the monitor, and set pirState to HIGH. This will prevent the Arduino
from continuously printing out that new motion was detected while the actual motion is still
continuing.
Upload it to see it working, don't forget to open up the monitor window (Tools > Serial
Monitor). You should see something like this:
!
If you are not sure what the pirState variable is actually doing, do this little experiment:
Peter Dalmaris
Lecture 10
if (pirState == LOW) {
Serial.println("Motion detected!");
pirState = HIGH;
}
with only:
Serial.println("Motion detected!");
if (pirState == HIGH){
Serial.println("Motion ended!");
pirState = LOW;
}
Serial.println("Motion ended!");
Upload the edited sketch. Open the monitor and activate the sensor by waving your hand
above it. What can you see in the monitor?
You can calibrate the sensitivity and amount of time that the sensor stays activated by
turning the two small orange knobs. Experiment with them to see the eect they have on
the sensor's output.
That's it with the PIR sensor for now. Please attempt the quiz questions before continuing
with the next lecture, were you'll learn about the ultrasonic distance sensor.
Peter Dalmaris
Lecture 11
Lecture 11
Distance
There are lots of applications where we not only need to know that an object, or a person,
in nearby, but also how far they are. Imagine a robot moving around in a room. The robot
can use a distance (or proximity) sensor to detect that it is approaching a wall or another
object. Or, you could use a proximity sensor to automatically open a door if a person is
within a meter of the sensor. You find
such sensors in cars (to help with
parking and to avoid small accidents),
and in smart phones where the
smartphone can detect, for example,
that the phone is held against the
user's head, useful so that the screen
is turned o to avoid accidental
touchscreen input.
Ultrasonic sensors are solid-state devices, very reliable and cheap. Especially in indoor
environments, and for small spaces (or measuring small distances), these sensors represent
a good choice. Anything that is solid enough to allow sounds to bounce will work with
these sensors. If you want to measure or detect things like smoke and clouds, you will need
to use something else, perhaps a microwave doppler radar.
For the Arduino, a commonly used proximity sensor is the HC-SR04. You can find them on
Ebay for less than $2 each.
!
1
Peter Dalmaris
Lecture 11
Assembly
We will need:
The Arduino
An ultrasonic sensor, like
the HC-SR04.
!
Sketch
#define trigPin 13
#define echoPin 12
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}
void loop() {
long duration, distance;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration/2) / 29.1;
2
Peter Dalmaris
Lecture 11
delay(500);
}
!
There's quite a lot happening in this small amount of code.
We define the sensor's trigger and echo pins to be 13 and 12 respectively. In the setup
function, we initialise the monitor, and set pin 13 to be the output and pin 12 to be the
input.
Through pin 13, the Arduino will ask the sensor to trigger a ping, similar to the "boing" noise
that submarines emit when they use their sonar. This ping, assuming it bounces of an
object in range, will come back and will be picked up by the sensor's receiver. The Arduino
will know when that happens because it is monitoring pin 12, which is connected to the
sensor's echo pin.
In the loop() function, we first setup two variables of type long. Long numbers are 4 bytes in
size, a total of 32 bits, and can hold very large numbers: -2,147,483,648 to 2,147,483,647.
The variable duration will hold the total number of microseconds that it took for the ping to
reach the object and return to the sensor. The variable distance will contain the distance to
that object in centimetres.
0.5
The Arduino is triggering a ping by writing to the trigger pin three pulses: first, a digital LOW
for 2 microseconds, then a digital HIGH for 10 microseconds, and finally a digital LOW
which stays low until the next iteration of the loop.
Ping 1
Ping 2
Ping 2
!
It then uses the function pulseIn to get the number of microseconds in takes of the ping to
come back. PulseIn accepts two parameters: a pin number (in our case it is 12, stored in
variable echoPin), and the pulse level we want to detect, in our case it is HIGH because we
3
Peter Dalmaris
Lecture 11
want to detect the 10 microsecond ping we just emitted. As soon as the Arduino calls the
pulseIn function, it starts timing. It returns the number of microseconds from the time the
function was called until it detects the ping echo.
The distance is calculated by the Arduino. It divides by two the duration that the pulseIn
function returned, since the ping travels a total of twice the distance to the object (going
there and its echo coming back). It then divides again by the "magic number" 29.1. This
number derives from this calculation:
The speed of sound at 0 degrees celsius is measured to be 331.5 meters per second. At
dierent temperatures, the speed of sound is calculated by adjusting 331.5m/s for the
temperature by multiplying by 0.6:
We need to convert the seconds to microseconds and the length from meters to
centimetres:
Limitations
If the distance to a target is over 200 centimetres, the Arduino reports that the target is out
of range, since at that distance measurements are not reliable. The same happens if the
distance is negative (question to consider, why do we need to test for negative
distance???). Any other distance condition is valid, so the monitor will print out the distance
in centimetres.
Peter Dalmaris
Lecture 12
Lecture 12
Measuring acceleration
Acceleration is defined as the rate by which the velocity of an object changes. The velocity
of an object changes when a force is applied to it. Acceleration is quantified by a direction
and a magnitude. Direction is the way to which a force is directing the object, and
magnitude relates to the strength of the applied force. Acceleration is described by
Newton's Second Law.
Having the ability to measure acceleration is very useful. For this purpose, we use an
accelerometer. An accelerometer measures the force that is applied to a small test mass.
This test mass is placed inside the device and is held in place by one or more springs (or
something equivalent). As gravity, or other forces, are applied on the test mass, they make
it move towards a particular direction. The device measures the distance this mass travels
from its resting position. The longer the distance, the stronger the force.
Imagine the accelerometer (or your self) in free fall, the test mass, as it is falling, is "feeling"
no force being applied upon it. In a free-fall situation, the accelerometer would report no
acceleration at all.
!
!
1
Peter Dalmaris
Lecture 12
Assembly
We will need:
The Arduino
An ADXL335 3-axis
accelerometer.
Sketch
!
int x, y, z;
void setup()
{
Serial.begin(9600); // sets the serial port to 9600
}
void loop()
{
x = analogRead(0); // read analog input pin 0
y = analogRead(1); // read analog input pin 1
z = analogRead(2); // read analog input pin 1
Serial.print("accelerations are x, y, z: ");
Serial.print(x, DEC); // print acceleration in the X axis
Serial.print(" "); // prints a space between the numbers
Serial.print(y, DEC); // print acceleration in the Y axis
Serial.print(" "); // prints a space between the numbers
Serial.println(z, DEC); // print acceleration in the Z axis
delay(100); // wait 100ms for next reading }
2
Peter Dalmaris
Lecture 12
!
We have not seen the analogRead() function before, but I am sure you can guess what it
does: it reads the voltage present at one of the analog pins. The accelerometer outputs
voltage to its three outputs relevant to the forces applied to it.
For example, at rest, the reading on the Z axis is around 410. If I push the device upwards, I
apply force along the Z axis which makes the test weight inside the accelerometer "feel"
heavier, and the voltage on the Z pin increases, causing the reading to increase (i.e. to
~470, in my experiment). If I push the device downwards, the opposite happens.
By taking and evaluating measurements in all three axis, you could make it possible for your
device to know the precise direction of its movement and the force applied to it.
Peter Dalmaris
Lecture 13
Lecture 13
Infrared line sensor
An infrared line sensor is a simple device made up of an infrared emitting LED and an
infrared sensitive photo-resistor.
You could use one of these sensors to build a robot that follows a dark line on the floor, or
your own heart rate monitor.
The principle of operation is very simple: The transmitter produces infrared light which
bounces of a surface and comes back to be captured by the photo-resistor. The more
infrared light is reflected back into the photo-resistor, the
higher the output of the sensor gets.
Assembly
Let's puts together this circuit and test out the motion
sensor.
We will need:
The Arduino
!
!
Peter Dalmaris
Lecture 13
Sketch
This one is very simple, just read the analog output at pin A0 and print it to the monitor:
Peter Dalmaris
Lecture 14
Lecture 14
Tilt and impact sensor
In many cases, knowing the exact force and direction applied to our gadgets is an overkill;
just knowing that a bottle has been tipped over is enough to know that the lid should close
automatically. For simple cases like that, a 3-axis accelerometer is an overkill. We could use
a simple sensor that can detect the shock of an impact or for being upside down.
Here is what a tilt sensor looks like from the outside (right - top)
!
!
Peter Dalmaris
Lecture 14
Assembly
!
!
Sketch
This is another very simple sketch. We'll just use the analog port to determine if the switch
inside the sensor is closed or open. You could just as easily have used a digital pin since
there are only two states to detect, open or close, which nicely translate to HIGH or LOW.
!
int out;
void setup()
{
Serial.begin(9600); // sets the serial port to 9600
}
void loop()
{
out = analogRead(0); // read analog input pin 0
Serial.println(out, DEC);
delay(100); // wait 100ms for next reading
}
!
2
Peter Dalmaris
Lecture 14
Try out by connecting the sensor to the Arduino and moving the sensor in dierent
directions. This is easier if you use some electrical tape to keep the sensor wires tidy. You
could even stick the sensor onto the Arduino, and move the whole assembly instead of only
the sensor. This will help in keeping the connections firm.
Peter Dalmaris
Lecture 15
Lecture 15:
Button
A button is a simple on-o switch. There are many kinds of buttons, distinguished by the
mechanism used to close or open a circuit, but essentially all buttons belong to one of two
families: those that keep the connection in either an open or a closed state, and those that
return to their original (default) state.
A keyboard key or a door bell button are both momentary buttons. Momentary buttons are
also known as "biased" because they have a tendency to return to their original position. A
light switch, on the other hand, stays at the position it was pushed to, so it is often called
"un-biased".
Peter Dalmaris
Lecture 15
Experiment
!
Once assembled, the project will look like this
(below):
Peter Dalmaris
Lecture 15
Sketch
The sketch is simply taking a reading from digital pin 2, where one of the pins of the button
is connected, and writing a value to digital pin 13, where the LED is connected.
I could have written this script to be even smaller, but I will leave that for you to do as an
exercise.
/*
Pushbutton sketch a switch connected to pin 2 lights the LED
on pin 13
*/
const int ledPin = 13;
const int inputPin = 2;
void setup() {
pinMode(ledPin, OUTPUT); //
//
//
//
//
pinMode(inputPin, INPUT);
}
void loop(){
int val = digitalRead(inputPin); // read input value
if (val == HIGH)
{
digitalWrite(ledPin,HIGH);
} else
{
digitalWrite(ledPin,LOW);
}
}
And that's how you can use a momentary button with the Arduino!
Peter Dalmaris
Lecture 16
Lecture 16
Potentiometer
Let's put this circuit together, and then we'll discuss how it works. In
particular, we'll see what is happening
inside the potentiometer.
Assembly
The Arduino
A potentiometer
An LED
!
!
1
Peter Dalmaris
Lecture 16
How it works
Potentiometer
So there you have it, a potentiometer is nothing more than a variable voltage divider.
Arduino's digital pins can output HIGH and LOW, as we have already seen. Some of these
digital pins, however, are special: although they can still only output HIGH and LOW, their
output can be modulated is a way that this output can behave as if it was analog.
Peter Dalmaris
Lecture 16
Sketch
int potentiometerPin = 0;
int ledPin = 11;
int potentiometerVal = 0;
void setup()
{
Serial.begin(9600); // setup serial
}
void loop()
{
potentiometerVal = analogRead(potentiometerPin);
//I use the map function because PWM pins can only accept
//values from 0 to 255. Analog pins can output values from
//0 to 1023. With the map function, the range 0-1023 is
//converted to appropriate values from 0 to 255.
int mappedVal = map(potentiometerVal,0,1023,0,255);
Serial.print(potentiometerVal);
Serial.print(" - ");
Serial.println(mappedVal);
analogWrite(ledPin,mappedVal);
delay(10);
}
There is nothing new here. We have seen the "map" function in an earlier lecture, so you
know that this function is used when we want to make a range of values fit within another
range of values. In this case, we read values in the range of 0 to 1023 from analog pin 0,
and we want to make this fit in the range 0 to 255, which is what the pulse width
modulation-capable pin 11 can output.
Other than that, we simply use the "analogRead" function to read a value from analog pin 0,
and the "analogWrite" function to create a PWM pulse on digital pin 11.
Simple!
Peter Dalmaris
Lecture 16
Here's an experiment: Take a small speaker and connect it to the breadboard so that it
replaces the LED. Fire up the circuit. Can you hear the sound coming out of it? Can you
notice how its pitch changes as you turn the knob?
Peter Dalmaris
Lecture 17
Lecture 17
Flex sensor
There's lots of other uses, like in biometrics, in fitness products, and in gaming devices.
Peter Dalmaris
Lecture 17
Assembly
The Arduino
!
Heres the completed circuit:
!
2
Peter Dalmaris
Lecture 17
Sketch
And, here's the script. All we need to do is to read the voltage at analog pin 0 and print it in
the monitor.
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(A0);
// Convert the analog reading (which goes from 0 - 1023)
//to a voltage (0 - 5V):
float voltage = sensorValue * (5.0 / 1023.0);
// print out the value you read:
Serial.println(voltage);
}
Upload it to your Arduino and open up the monitor. Bend the sensor back and forth. See
how the output fluctuates as the sensor's shape changes?
Peter Dalmaris
Lecture 18
Lecture 18
Membrane potentiometer
Assembly
The Arduino
A membrane potentiometer
3 Jumper wires
!
!
Peter Dalmaris
Lecture 18
The monitor output is essentially the same as with the potentiometer. The only dierence is
that the actual resistance of the voltage divider will be dierent between the two devices so
the actual reading will be dierent in analog pin 0.
Peter Dalmaris
Lecture 18
Sketch
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(A0);
// Convert the analog reading (which goes from 0 - 1023)
// to a voltage (0 - 5V):
float voltage = sensorValue * (5.0 / 1023.0);
// print out the value you read:
Serial.println(voltage);
}
!
The membrane
potentiometer,
however, is made by having a conductive
membrane (hence the name) that is
suspended over a resistor material. As one
presses the membrane, it comes in contact
with the resistor under it at dierent lengths
of the resistor, and closes a circuit, thereby
implementing the voltage divider.
Peter Dalmaris
Lecture 19
Lecture 19
Buzzer
Assembly
We will need:
The Arduino,
A buzzer,
7 jumper wires.
!
1
Peter Dalmaris
Lecture 19
!
!
Peter Dalmaris
Lecture 19
Sketch
The only dierence between this script and the one from Lecture 16 is the variable name for
the buzzer pin. ledPin is now buzzerPin.
int potentiometerPin = 0;
int buzzerPin = 11;
int potentiometerVal = 0;
void setup()
{
Serial.begin(9600); // setup serial
}
void loop()
{
potentiometerVal = analogRead(potentiometerPin);
//I use the map function because PWM pins can only accept
//values from 0 to 255. Analog pins can output values from
//0 to 1023. With the map function, the range 0-1023 is
//converted to appropriate values from 0 to 255.
int mappedVal = map(potentiometerVal,0,1023,0,255);
Serial.print(potentiometerVal);
Serial.print(" - ");
Serial.println(mappedVal);
analogWrite(buzzerPin,mappedVal);
delay(10);
}
And there you go, making sounds with the Arduino is super easy.
Peter Dalmaris
Lecture 20
Lecture 20
Direct current motor
Direct current motors represent the easiest way to add movement to your projects. But
easy does not mean less capable. A direct current or DC motor can be a very cost
eective and flexible solution for all sorts of projects: robots, cars, boats, toy helicopters,
home automation, and many more.
In this lecture I will show you how to use a DC motor through a series of demos. All involve
two geared DC motors (why use just one when we can use two?) and a motor break-out
circuit. Ill explain what these are in a minute. Heres what we are going to build in this
lecture:
First, well just hook the motors to the Arduino via the motor break-out board, and make
them spin in a single direction.
Second, well get the motors spinning forwards and backwards using a potentiometer as
a controller.
Third, well use an ultrasonic sensor to get the motors to spin faster or slower depending
on the distance between the sensor and an object in front of it.
Before we get into the demo, I want do a quick introduction to motors, and specifically to
discuss the kinds of motors people can use in their projects.
!
!
Peter Dalmaris
Lecture 20
Motors
The DC motor can spin very quickly, which often is not what we want in our applications.
For example, if you are building mechanism that opens the shutters of a window, you would
like the shutter to open slowly to avoid damaging it. A DC motor on its own would try to go
full speed immediately. So, in most cases people attach a gear box to the motor. The gear
box will convert high rotational spin from the shaft of the motor to lower rotational speed,
and often with a higher torque so that large loads can be moved.
In the examples in this lecture, I will show you what a gear box
looks like.
Peter Dalmaris
Lecture 20
fine control and small movements, a stepper motor is a good choice. For example, they are
used in 3-D printers, where milli-meter accuracy is needed.
Stepper motors tend to be much bulkier and expensive then DC motors. They contain
multiple coils and more complicated mechanical components, and that reflects in the price.
Your circuitry also needs to be more complicated because you need to provide several
signal lines of control to make it work.
!
!
!
Peter Dalmaris
Lecture 20
Lets build a simple circuit that controls 2 geared DC motors from a toy tank that I stole
from my kids (they originally stole it from me).
Even though you could connect a DC motor directly to the Arduino, that is not a good idea.
The Arduino can only provide very little current
to external devices, and only tiny motors
would be satisfied with that. In virtually all reallife applications, we need to provide external
power to the motor, and only signalling from
the Arduino.
You can get these devices fully assembled and ready to use on eBay for a couple of dollars.
Peter Dalmaris
Lecture 20
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
BEWARE! I was not able to find an appropriate part in the software I use to create the
circuit diagrams (Fritzing) for the L298N break-out board, so I am approximating it with this
basic black device containing 11 pins. From left to right, the pins on this mockup device
are:
1
Out1
2
Out2
3
Out3
4
Out4
5
In1
6
In2
7
In3
8
In4
9
12V+
10
Gnd
11
5V+
Try to match these pins against the marked connectors in your break-out board and you
should be ok.
Ill explain what the role of each pin is on the break-out board now.
Out1 and Out2 control the speed and direction of motor 1. Out3 and Out4 control motor 2.
As we are using DC motors, the higher the voltage dierential between the pins in those
pairs, the faster the motors will spin. If you want to reverse the motors, you must change
the polarity of these pins. This is something that the L298N will do for you with the
appropriate signal in the input pins.
Peter Dalmaris
Lecture 20
In1 and In3 control the direction of spin for motor 1 and motor 2 respectively.
[VIDEO]
!
!
Peter Dalmaris
Lecture 20
E1
M1
E2
M2
=
=
=
=
5;
4;
6;
7;
void setup()
{
pinMode(M1, OUTPUT);
pinMode(M2, OUTPUT);
}
void loop()
{
int value;
for(value = 0 ; value <= 255; value+=1)
{
digitalWrite(M1,HIGH);
digitalWrite(M2,HIGH);
analogWrite(E1, value);
//PWM Speed Control
analogWrite(E2, value);
//PWM Speed Control
delay(30);
}
}
We start by setting the digital pins for the motor 1 and motor 2 control. We need two pins to
control each motor. The M pins control the direction of rotation for the motor shafts, and
the E pins control the speed.
In the setup() function we set the mode for pins M1 and M2 to be output (so we can control
the direction of the spin).
In the loop() function, we have a for loop that cycles from 0 to 255, so it covers all the
possible PWM values that can be written to pins E1 and E2 (Arduino pins 5 and 6
respectively).
Every time that the block in this loop runs, we first set the direction of spin for both motors
by writing a HIGH value to pins M1 and M2 (Arduino pins 4 and 7 respectively), and then
we set the spin by using the PWM function analogWrite. The value we write to these pins
is controlled by the for loop.
Once you connect this circuit and run the loop, you will have your motors starting from zero
speed, speeding up gradually until they reach their maximum rotational speed, before they
suddenly stop to a halt and then repeating the same processes again, until power is
switched o.
Peter Dalmaris
Lecture 20
To change the direction of rotation, just change the value you write to either M1 or M2 pins
to LOW, and upload the sketch. You will see the corresponding motor shaft moving the
opposite way compared to the original sketch.
Easy!
!
!
!
!
!
Peter Dalmaris
Lecture 20
Heres the first sketch for Demo 2. This sketch allows the control of only the rotational
speed of the two motors:
E1
M1
E2
M2
=
=
=
=
5;
4;
6;
7;
void setup()
{
Serial.begin (9600);
pinMode(M1, OUTPUT);
pinMode(M2, OUTPUT);
}
void loop()
{
int potentiometerVal = analogRead(A0);
Serial.println(potentiometerVal);
move_motors(potentiometerVal);
}
I have highlighted the changes and additions from the sketch in Demo 1 in red.
In the loop() function, we take a reading from analog pin 0 where the middle pin of the
potentiometer is connected. We then call the function move_motors and pass the
potentiometer reading to it. Inside move_motors, we simply map the potentiometer reading
(which comes in the range of 0 to 1023) to a value between 0 and 255 (which is the range of
valid values for the PWM pins), set the direction of rotation for the two motors using the
digitalWrite functions, and set the speed using the PWM function analogWrite to be
mappedVal (which in turn is relevant to the value we set by turning the potentiometer).
Upload this sketch and turn the knob of the potentiometer back and forth. See how the
speed of the motors change accordingly?
Peter Dalmaris
Lecture 20
Theres one more thing to do, and that is to also be able to control the direction of rotation
of the motors, not just the speed. What Id like to do is to be able to accelerate the motor
towards one direction when I turn the knob of the potentiometer towards the left, and to the
opposite direction when I turn the knob towards the right. Schematically, this is what I
would like to achieve: The red circle represents the potentiometer knob, and the black
shows movement of the knob towards the left or the right.
!
!
!
!
!
!
!
!
!
!
Motors move
anti-clickwise
Motors move
clickwise
We are only going to make an adjustment to the sketch to make this happen, no need to
modify the circuit at all.
E1
M1
E2
M2
=
=
=
=
5;
4;
6;
7;
void setup()
{
Serial.begin (9600);
pinMode(M1, OUTPUT);
pinMode(M2, OUTPUT);
}
void loop()
{
int potentiometerVal = analogRead(A0);
Serial.println(potentiometerVal);
move_motors(potentiometerVal);
}
Peter Dalmaris
Lecture 20
{
if (potValue < 512)
{
int mappedVal = map(potValue,0,512,0,255);
//Going forward
digitalWrite(M1,HIGH);
digitalWrite(M2, HIGH);
analogWrite(E1, mappedVal);
//PWM Speed Control
analogWrite(E2, mappedVal);
//PWM Speed Control
delay(30);
} else
{
//Going backward
int mappedVal = map(potValue-512,0,512,0,255);
digitalWrite(M1,LOW);
digitalWrite(M2, LOW);
analogWrite(E1, mappedVal);
//PWM Speed Control
analogWrite(E2, mappedVal);
//PWM Speed Control
delay(30);
}
Block 1
Block 2
I have highlighted in red the part of the sketch that has changed from the script in demo 2.
In the move_motors(int pot_value) function, potValue, the value read from analog pin 0
where the potentiometer is connected, is tested using the if function. If potValue is less than
512, then the first block is executed, if not then the second block is executed.
In block 1 and 2, the motor control functionality from demo 2 is repeated with the dierence
that now we now need to recognise that the potentiometers range of outputted values is
divided to two parts. The first part, from 0 to 512, is used for moving the motors towards
one direction, while the second one, 513 to 1023, moves the motors towards the other
direction.
So, we need to adjust the code so that the new range of the potentiometer is taken into
account. In Block 1, the range from 0 to 512 is converted to the PWM range of 0 to 255. In
Block 2, potValue values are from 513 to 1023, still 512 possible values, so the range is
eectively the same as with Block 1, except that we need to make an adjustment to
potValue by subtracting 512 from it so that the value that the map function evaluates nicely
fits within this range.
The only other dierence between these two blocks is that in Block 1, a HIGH is written to
M1 and M2, while in Block 2 we write a LOW, therefore spinning the motors towards the
opposite direction.
If you are having any diculty understanding what is going on in Blocks 1 and 2,
experiment with the sketch. Try changing the map function values to something dierent.
For example, in Block 2, instead of map(potValue-512,0,512,0,255) try map(potValue,
11
Peter Dalmaris
Lecture 20
0,512,0,255) and upload the sketch. What happens to the rotational speed of the motors?
Perhaps you could try this too: map(potValue,512,1023,0,255). Upload and compare to the
previous two versions. Any dierence in behaviour? Can you explain any dierences or
similarities?
By now, you have learned how to control a motor through code, and through an external
controlling device like a potentiometer. The sketch would look much the same if you used a
simple joystick instead. You can see how from a humble beginning you can start branching
out and experimenting with dierent types of hardware, gradually reaching more interesting
and capable configurations.
Well do one last experiment for this lecture. We will connect an ultrasonic distance sensor
in the place of the potentiometer, and use that to control the rotational speed of the motors.
The idea is this: The distance sensor will tell the Arduino how close it is to an obstacle. If it
gets too close, it will start slowing down the motors, eectively slowing down our prototype
vehicle. Otherwise, it will forge ahead full speed.
!
!
!
!
!
!
!
!
!
!
!
!
!
!
12
Peter Dalmaris
Lecture 20
!
Have a look at Lecture 11 if you need a reminder on how to connect the ultrasonic distance
sensor. This sensor uses 4 wires, two for power (5V and Ground), one for the Trigger pin,
and one for Echo. Connect those wires as shown in the schematic, and upload the
following sketch:
E1
M1
E2
M2
=
=
=
=
5;
4;
6;
7;
#define trigPin 13
#define echoPin 12
void setup()
{
Serial.begin (9600);
pinMode(M1, OUTPUT);
pinMode(M2, OUTPUT);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}
void loop()
{
long duration, distance;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration/2) / 29.1;
move_motors(distance);
13
Peter Dalmaris
Serial.print(distance);
Serial.print(" cm - ");
Serial.println(mappedVal);
digitalWrite(M1,HIGH);
digitalWrite(M2, HIGH);
analogWrite(E1, 255-mappedVal);
analogWrite(E2, 255-mappedVal);
delay(30);
Lecture 20
}
}
Much of this code, at least the part in the loop() function, has been taken from the sketch in
Lecture 11. It generates the ping pulse that is emitted by the sensor, and then calculates
the distance of an object from the time it takes for the echo to return.
Once the distance is calculated, the move_motors function is called, and the distance is
passed as an integer parameter. Just like in Demo 2 Version 2, motors are moved in one of
two ways: if distance is 50cm or less, the motor speed is proportional to the distance. The
smaller the distance is, the smaller the speed. Otherwise, if the distance is over 50cm, then
motors will move at their maximum speed.
If these motors were part of a fully assembled vehicle, the eect of this sketch would be
that the vehicle would be capable of avoiding hitting an object by slowing down on
approach, until it came to a full stop.
Upload the sketch and play with it to get a sense of it in real life. Try to imagine how it
would behave in a real situation. Can you foresee any limitations? Can you imagine any
improvements?
Improvements
For example, in the current version of the sketch, the vehicle would come to a complete
stop only when the distance to a wall was 0cm. Is this sucient? Perhaps it should come to
complete stop a bit earlier, maybe at 5cm? Or perhaps, to be even more cautious, the
vehicle should backup slightly away from the obstacle in order to provide room for a turning
manoeuvre?
Can you make changes to the Demo 3 sketch and implement these scenarios (or whatever
other improvement you can think of)?
14
Peter Dalmaris
Lecture 23
Lecture 23
Servo motors
In Lecture 20, you learned about the DC motor: a versatile yet low-priced solution for
providing motion to your projects. A weakness (or characteristic) of the plain vanilla DC
motor is that it spins like crazy, and while it does we dont know where the shaft is or how
far it has traveled because there is no feedback provided! Once you apply power, it wants
to go as fast as it can for at long as it can!
This is great for applications were continuous movement is needed, like for propelling a
vehicle, but not when we need to be able
to generate precise movement.
!
In Lecture 20 there was a passing mention of the servo
motor. The servo motor is a spruced up DC motor
that includes circuitry for fine movement control and
feedback. In this Lecture, we will learn about how to
use this kind of motor. We will use both the build-in
servo motor library that comes standard with the Arduino IDE,
and a third party library that adds a bunch of very useful
features.
Peter Dalmaris
Lecture 23
But thats not where this decision ends. Notice the big round tube at the lower part of the
breadboard? That is a capacitor. Even though my servo motor is small, occasionally in may
draw more power than what the Arduino can provide, but only momentarily, like when it
starts a new move. To counteract that, I have plugged in a capacitor between the Ground
and +5V columns. A capacitor is a reservoir of electricity, a bit like a battery that can charge
and discharge very quickly. Plugged into the circuit like this, when the servo motors starts
and draws more power than what the Arduino can provide, the capacitor will start
discharging and providing the motor the additional power it needs. Just plug in a capacitor
that is 300F or more and youll be ok.
The motor itself has three wires coming out of it. Two are for power (+5V and Gnd), and one
for signal. Plug the signal wire to digital pin 9, and the other two to the +5V and Ground
columns on the breadboard.
We could try to control the servo motor through the Arduino digitalWrite functions but that
would require us figuring out the right values to write, and the timing for writing those
values. Thats too much work. We are lucky, though, because with the Arduino IDE we get
the Servo library, which contains functions that allow us to easily work with servo motors.
Well using this library first, and then Ill show you an alternative third-party library that
provides additional functionality.
Peter Dalmaris
Lecture 23
Demo 1a sketch
Heres the sketch:
#include <Servo.h>
Servo myservo;
int pos = 0;
void setup()
{
myservo.attach(9);
}
void loop()
{
for(pos = 0; pos < 180; pos += 1)
{
myservo.write(pos);
delay(15);
}
for(pos = 180; pos>=1; pos-=1)
{
myservo.write(pos);
delay(15);
}
}
//
//
//
//
Block 1
Block 2
This is one of the demo sketches that also come with the Arduino IDE. You can find it by
clicking on File > Examples > Servo > Sweep.
We first declare our intention to use a servo motor by including the Servo library (#include
<Servo.h>), and creating the variable myservo that we use as a handle to the Servo
object (Servo myservo;).
In the setup function, we tell the Arduino that the control wire from our servo motor is
attached to digital pin 9 (myservo.attach(9);).
The work is done in the loop() function, where we use the for loop to count from 0 to 180,
and another one to count backwards, from 180 to 0. This has the eect of the shaft of the
servo motor travelling 180 degrees to one side, and 180 degrees to the other side, 1 degree
at a time, constantly.
Inside each for block, we first write a value to the motor using myservo.write(d), where d
is a number representing the degree to which the shaft should turn. If we want to turn it by
15 degrees, we write myservo.write(15). Simple, right?
Peter Dalmaris
Lecture 23
In Block 1, we get the shaft to turn from 0 to 180 degrees, and in Block 2 to travel all the
way back to 0 degrees.
Finally, notice how we set a delay of 15 milliseconds inside each block, after a movement
has been written? We need this because it takes a bit of time for the motor to move, and we
want to make sure that any previous instruction has been completed before sending
through the next move instruction.
That was easy but not satisfying enough. I want to be able to control the servo motor my
self, instead of the Arduino being in charge. How about we try to connect a potentiometer
and use it as a controller for the motor?
#include <Servo.h>
Servo myservo;
void loop()
{
val = analogRead(potpin);
// reads the value of the potentiometer (0 to 1023)
val = map(val, 0, 1023, 0, 179); // scale it to use it with the servo (0 to 180)
myservo.write(val);
// sets servo position according to the scaled value
delay(15);
// waits for the servo to get there
}
4
Peter Dalmaris
Lecture 23
Just like in Demo 1a, we include the Servo library and set pin 9 as the servo pin. In the
loop() function, we constantly take readings from analog pin 0 (A0) where the potentiometer
is attached. Because the range of values read in A0 is not the same as the values we can
send to the servo, we use the map function to scale appropriately.
Finally, we use the myservo.write(val); function to send the scaled value to the
potentiometer.
Try it out!
!
!
!
!
!
5
Peter Dalmaris
Lecture 23
!
You need to rename this folder, so that its
now called VarSpeedServo (i.e. remove
the -master part.
Peter Dalmaris
Lecture 23
#include <VarSpeedServo.h>
VarSpeedServo myservo;
const int servoPin = 9; // the digital pin used for the servo
void setup() {
myservo.attach(servoPin); // attaches the servo on pin 9 to the servo object
myservo.write(0,255,true); // set the initial position of the servo, as fast as
// possible, wait until done
}
void loop() {
myservo.write(180,255,true);
myservo.write(0,30,true);
//
//
//
//
//
!
!
You can probably see the similarities and dierences between the original Sweep sketch
and this one. We include the library and create the myservo object reference. We set the
servo signal pin to 9.
In the loop() function, we attached the servo at pin 9 to the myservo object.
We then send the first instruction to the servo by using the myservo.write function. This
looks the same as in the original Sweep sketch, except that is takes three arguments, not
just one.
The position argument accepts an integer, denoting the degree wed like the servo to turn
to. For example, 15 degrees.
The speed argument controls how fast the movement should be. Top speed is 255, stopped
is 0, and anything is between is allowed.
Finally, the wait argument allows us to stop the program at that line and wait for the motor
to finish its movement. To wait, we make this argument true. If we want to just continue
Peter Dalmaris
Lecture 23
running the sketch and let the servo finish its movement on its own, we make this argument
false.
Lets now connect the potentiometer, like we did in Demo 1b, and load the VarSpeedServo
example sketch titled Knob into the Arduino IDE.
Here it is:
#include <VarSpeedServo.h>
VarSpeedServo myservo;
int val;
void setup() {
myservo.attach(servoPin);
}
void loop() {
val = analogRead(potPin);
// reads the value of the potentiometer (0 to 1023)
val = map(val, 0, 1023, 0, 180); // scale it to use it with the servo (0 to 180)
myservo.write(val);
// sets the servo position according to the scaled value
delay(15);
// waits a bit before the next value is read and written
}
Compare this against the sketch from Demo 1b. Isnt it practically identical? Notice how the
sketch above also makes use of the myservo.write(val); function, with just a single
argument? The VarSpeedServo library allows you to use the familiar functions from the
build-in Servo library, so that any existing sketches you may have lying around will still
work. You get the additional functionality, as we saw in the Sweep sketch, for free!
An exercise
Can you build a circuit that contains a servo motor and three buttons? When you press button 1, the
motor moves to 60 degrees. When you press button 2, it moves to 90 degrees. When you press
button 3, it moves to 180 degrees.
Peter Dalmaris
Lecture 24
Lecture 24
LCD character screen
Liquid crystal displays, LCD screens, are one of the most common ways for a gadget to
speak to us. They come in many dierent shapes, sizes, colours and prices. It is a common
and reliable technology. It is no surprise that the Arduino makes it super easy to connect to
them with a build-in library that provides several capabilities, and several modes of
operation.
In this first demo, Ill show you how to connect an LCD screen to your Arduino. There are
two ways to make this connection: parallel and serial.
With a parallel connection, we connect each of the screens data pins to an individual digital
pin on the Arduino. The LCD screen Ill be using is an 8-bit device, meaning that each
message from the Arduino is made up of 8 bits. This LCD screen has one pin for each bit,
Peter Dalmaris
Lecture 24
In this schematic, the first pin from the left of the LCD is number one, and the last on the
right is 16.
The green wires represent the 4 data lines. From the LCD screen, pins D4, D5, D6, D7,
connect to Arduino digital pins 5, 4, 3, 2 respectively.
LCD pin 1 goes to ground, and pin 2 go to +5V. These two supply power to the display.
The potentiometer is used to control the contrast. Plug the middle pin of the potentiometer
to LCD pin 3, and the other two to power and ground, as we have done in the past.
Peter Dalmaris
Lecture 24
#include <LiquidCrystal.h>
void setup() {
lcd.begin(16, 2); // set up the LCD's number of columns and rows:
lcd.print("hello, world!); // Print a message to the LCD.
}
void loop() {
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(0, 1);
lcd.print(millis()/1000); // print the number of seconds since reset:
}
We start by including the LiquidCrystal library to our sketch. This library comes build-in with
the Arduino IDE so we dont have to do anything to import it to our environment.
Then we declare and initialise the lcd object. The parameters that we pass to the initialiser
are the interface pins on the Arduino. The last four parameters are the data pins, and this is
how the Arduino knows that we will be using the four bit mode. The first two parameters are
the screen reset and enable pins. Have a look at the reference page for more details: http://
arduino.cc/en/Reference/LiquidCrystalConstructor
In setup(), we call the begin function and setup the screens number of columns and rows.
In this demo, I am using a screen with 16 columns in 2 rows. Change these numbers if you
are using a dierent sized screen.
Once we call the begin function, we can start sending content to the screen. So in the very
next line, we are printing the hello world! message, to greet the world!
In the loop() function, we make use of another function, setCursor(a,b). This function does
what you think it does: set the cursor where the next set of character will appear at a
particular column (parameter a) and row (parameter b).
And thats how you can use a parallel LCD character screen in 4-bit mode with your
Arduino!
!
!
3
Peter Dalmaris
Lecture 24
In this second demo, well add a DHT22 humidity and temperature sensor and use the LCD
screen instead of the monitor to show the readings.
!
!
!
!
!
!
!
!
!
!
Peter Dalmaris
Lecture 24
The sketch is essentially a merge of the sketch we saw in Lecture 8, and that of Demo 1 in
this lecture. Here it is:
#include <LiquidCrystal.h>
#include "DHT.h"
#define DHTPIN 7
// what pin we're connected to
#define DHTTYPE DHT22
// DHT 22 (AM2302)
DHT dht(DHTPIN, DHTTYPE);
void setup() {
lcd.begin(16, 2); // set up the LCD's number of columns and rows:
lcd.print("DHT test); // Print a message to the LCD.
dht.begin();
}
void loop() {
float h = dht.readHumidity();
float t = dht.readTemperature();
The only new function we use here are the lcd.clear() function, which clears the screen so
we can write on a clean canvas.
Peter Dalmaris
Lecture 24
The rest of this sketch is clear, I think. Setup the DHT sensor and the LCD screen, initialise
the objects for each sensor, start the devices, then take readings from the sensor and print
the acquired values to the screen.
An exercise
Can you build a circuit that contains a servo motor, an LCD screen, two potentiometers (one for the
screen and the other one for controlling the motor). As you turn the potentiometer and the servo
changes position accordingly, write the current angle on the screen. For example, if you have turned
the knob to 60 degrees, a message on the screen should show 60 degrees.
Peter Dalmaris
Lecture 25
Lecture 25
TFT LCD screen
!
!
A TFT computer screen can contain many millions of
these pixels. In small consumer electronics, like feature
phones, there could be several tens or hundreds of
thousands.
You can see that TFT LCD screens are far more versatile that LCD screens because there is
no restriction on the things you can display on them. While on a humble LCD screen you
can only display text, in a TFT screen you can draw anything at all.
Peter Dalmaris
Lecture 25
In this lecture I am use the first shield we play with in this course. A shield is a PCB on
which several components are mounted, with pin connectors that match exactly those of
the Arduinos headers. You plug the shield on top of the Arduino, and you are good to go.
No wiring and no soldering is required. This greatly minimises chances of making a mistake
and breaking something, and is a good way to experiment with more complicated devices,
like ethernet adaptors, SD card readers, motor controllers, etc.
To get started, you first need to download and install the library that best matches the
screen that you have. In my case, I purchase this one from ebay: http://r.ebay.com/rlUg4W.
After a little searching around on Google, I discovered that the best matching library comes
from its manufacturers website at http://www.elecfreaks.com/store/color-lcd-shieldp-462.html?zenid=d96eddbde9a0lmr9j7rkhrmmp2.
To make the library available to your Arduino project, and to get access to the example
sketches, follow the exact same procedure as you did back in the servo lecture:
2. Once expanded, ensure that the name of the folder matches exactly the name of the .h
file inside it (the name of the folder should not contain the .h extension). For this
library, the name of the folder should be ColorLCDShield. Case is important.
!
2
Peter Dalmaris
Lecture 25
!
!
3. Copy the folder in your Arduinos library folder (if not sure, look at your Arduino IDE
preferences).
You should now be able to see the ColorLCDScreen examples by clicking on File >
Examples > ColorLCDShield > Examples.
Next, plug the shield onto the Arduino. Because of the way the the headers are build into
the Arduino, and especially the distances between the four header segments, it is really
3
Peter Dalmaris
Lecture 25
impossible connecting the shield the wrong way without seriously damaging the shield by
bending its pins. Just play around with the shield and the Arduino to ensure you have
perfect pin alignment, and then apply some gentle force to make the two come together
securely.
Now that we have the shield installed, let upload one of the example sketches. Plug the
Arduino to your computer, go the the Arduino IDE, and click on File > Examples >
ColorLCDShield > Examples > ChronoLCD_Color.
Heres the sketch, I am showing you only the part that is relevant at the moment:
#include <ColorLCDShield.h>
void setup()
{
The important consideration at the moment is to figure out the type of screen controller
your screen uses. There are no external markings to help you out, and often the
manufacturer does not provide any documentation either. So most often this is a game of
trial and error. The controller will either be PHILLIPS or EPSON. Mine happens to be
PHILLIPS.
Go ahead and upload this sketch and find that it doesnt work, edit this line to EPSON to
see if it fixes the problem.
Peter Dalmaris
Lecture 25
Theres a lot happening in this sketch, so its better to leave it alone for now, and instead
have a look at something simple.
Leave everything connected, and load this sketch into the IDE:
#include <ColorLCDShield.h>
!
!
// background color
// red color constant
// green color constant
LCDShield lcd;
void setup()
{
pinMode(10, OUTPUT);
analogWrite(10, 1023); //PWM control blacklight
/* Initialize the LCD, set the contrast, clear the screen */
lcd.init(PHILLIPS);
lcd.contrast(40);
lcd.clear(BACKGROUND);
lcd.setStr("STARTING", 50, 0, C_COLOR, BACKGROUND);
delay(1000);
lcd.clear(BACKGROUND);
}
void loop()
{
char* Str4[ ] = {"arduino 1","arduino 2","arduino 3"};
for (int i=0;i<3;i++)
{
printString(Str4[i], 15 * i,0);
}
}
The sketch starts by including the LCD library. Then, we create (define) three constants. A
constant is simply a name that we give to a value, so that when we need to use it later, we
can just use it by referring to the name. Constant also help to make code more readable,
especially when the values we assigns to them are esoteric data like hexadecimal or
binary values.
In this sketch, for example, we define the background color like this:
Peter Dalmaris
Lecture 25
lcd.clear(BACKGROUND);
Here, we are calling the clear function of the lcd object, and passing the BACKGROUND
constant reference to it. We could have written this instead:
lcd.clear(BLACK);
and the result would be exactly the same. But consider this: the same code appears at
several places in this sketch. If at some point we decide to change the background color to
pink, we will need to find all references to the clear function and change its attribute. But
now, because we use a constant reference as an argument, we just go to the top of the
sketch, find the definition of this reference, and make the change from BLACK to PINK
there. Done!
LCDShield lcd;
We declare an object reference of type LCDShield. We can now use this reference, lcd, to
call all of the functions that the LCDShield object class oers.
Peter Dalmaris
Lecture 25
!
In the setup() function, we configure digital pin 10, which provides power to the screens
backlight.
pinMode(10, OUTPUT);
analogWrite(10, 1023);
We set it as output, and we turn its brightness to maximum by writing a 1023 value to it. If
you would like your screen to be dimmer, just reduce the value you write to pin 10.
Next, we tell the lcd object the kind of controller chip it is talking to:
lcd.init(PHILLIPS);
In my case, my screen uses a PHILLIPS controller. Remember, that the alternative is an
EPSON controller.
lcd.contrast(40);
Play with this parameter to see the eect it has on the contrast.
lcd.clear(BACKGROUND);
lcd.setStr("STARTING", 50, 0, C_COLOR, BACKGROUND);
The first one makes everything black, since at the start of the sketch we defined the
constant BACKGROUND to be black. And then, we write our first piece of string using the
setStr function. This one takes several parameters:
lcd.setStr([text to show], [start vertical pixel - y], [start horizontal pixel x], [text color], [background color]);
If you wanted to write something at the top-left corner of the screen, in red, with green
background, you would do it like this:
In the loop() function, we first define an array that contains three pieces of text:
Peter Dalmaris
Lecture 25
of characters (the char data type) are stored. A string of text is made up of lots of
characters, hence, char. We then give this pointer a name, Str4, and signify that this is
actually an array by using the square brackets next to the name: [].
Finally, we initialise this pointer variable to an array of strings by providing the 3 strings
inside curly brackets: {"arduino 1","arduino 2","arduino 3}.
We iterate through this array using the for function. For each item in the array, we call the
printString function, which is defined at the end of this sketch.
The printString function accepts three parameters: the string we want to write on the string,
the horizontal (x) start position and the vertical (y) start position:
Inside printString, there is a single call to the setStr function, just like we saw in the
setup() function earlier.
Peter Dalmaris
Lecture 25
In this second demo, Ill show you how to use the 5 buttons that are integrated into the
joystick that comes with the shield.
#include <ColorLCDShield.h>
!
!
!
LCDShield lcd;
int buttonPins[5] = {A0, A1, A2, A3, A4};
void setup()
{
/* Set up the button pins as inputs, set pull-up resistor */
for (int i=0; i<5; i++)
{
pinMode(buttonPins[i], INPUT);
digitalWrite(buttonPins[i], HIGH);
}
pinMode(10, OUTPUT);
analogWrite(10, 1023); //PWM control blacklight
/* Initialize the LCD, set the contrast, clear the screen */
lcd.init(PHILLIPS);
lcd.contrast(40);
lcd.clear(BACKGROUND);
lcd.setStr("STARTING", 50, 0, C_COLOR, BACKGROUND);
delay(1000);
lcd.clear(BACKGROUND);
}
void loop()
{
takeInput();
printString("Waiting...", 50,10);
}
void sayHello() {
char* Str4[ ] = {"arduino 1","arduino 2","arduino 3"};
Peter Dalmaris
Lecture 25
!
void printString(char* toPrint, int x, int y)
{
lcd.setStr(toPrint, x, y, M_COLOR, BACKGROUND);
}
void takeInput()
{
if(!digitalRead(buttonPins[0]))
{
lcd.clear(BACKGROUND);
printString("LAST INPUT: 0", 100, 0);
}
if(!digitalRead(buttonPins[1]))
{
lcd.clear(BACKGROUND);
printString("LAST INPUT: 1", 100, 0);
}
if(!digitalRead(buttonPins[2]))
{
lcd.clear(BACKGROUND);
printString("LAST INPUT: 2", 100, 0);
}
if(!digitalRead(buttonPins[3]))
{
lcd.clear(BACKGROUND);
printString("LAST INPUT: 3", 100, 0);
}
if(!digitalRead(buttonPins[4]))
{
lcd.clear(BACKGROUND);
printString("LAST INPUT: 4", 100, 0);
}
if(!digitalRead(buttonPins[5]))
{
lcd.clear(BACKGROUND);
printString("LAST INPUT: 5", 100, 0);
}
}
Lets look at the dierences between the sketches for Demo 1 and Demo 2.
!
!
10
Peter Dalmaris
Lecture 25
Here, we declare an array that will contain references to the five Arduino analog pins that
convey the status of the five buttons that the LCD shield provides. These five buttons are
integrated into a mini joystick soldered onto the PCB, next to the screen. We could refer to
these buttons using individually declared variables, like we have done in previous sketches.
But because we have several, it is more convenient to bundle them in a single array, and
then cycle through this array in order to set them up or to read their values using and
reusing the same code.
Here, we are cycling (looping) thew the contents of the array that contains the references to
the button pins, and first setting them as inputs, second turning on their pull up resistor.
This pull-up resistor (the opposite of the pull-down resister we saw earlier in this course),
keeps the voltage of the switch to high. When we close the switch by pressing it, the
voltage goes down, and the Arduino (guided by the code in the takeInput() function)
detects the event.
In the loop() function, we constantly call the takeInput() function and then calling the
printString function.
We have seen the printString function in Demo 1, so lets jump to takeInput() instead.
There, we individually examine the state of each button. When you push the joystick to the
side, one of the buttons creates a connection to Ground, so the digitalRead function
returns FALSE. Using the ! operator, we inverse this FALSE value and get TRUE instead.
We the if function, we examine this value, and if it turns out to be TRUE, the Arduino will
continue within the block and execute any instructions in it, like to clear the screen and
write some text in it.
So there you have it, you now know how to use the TFT LCD screen shield with the
Arduino. But there is more to learn. Best learn with an exercise.
Exercise
Have a look at the examples that come with the ColorLCDShield library. Can you figure out
how to draw a line, a rectangle and a circle?
11
Lecture 28
Keypad
Keypads represent an intuitive way for people to
provide input to gadgets and computers. Youll find
them in elevators, microwave ovens, TV remote
controls, phones, and countless other machines that
we interact with daily.
which means that all column and row wires from the keypad are connected to 8 of the
Arduinos digital pins.
Because this way of connecting the keypad is extremely wasteful of our precious digital
pins (the Arduino Uno only has 13), in demo 2 I will show you how to hack together a
solution that allows for a connection that uses a single signal pin.
Once the connections are done, the circuit depends solely on the software side to figure out
which button has been pressed and to display the right symbol in the monitor.
#include <Keypad.h>
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the symbols on the
char hexaKeys[ROWS][COLS] =
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[COLS] = {2, 3,
byte colPins[ROWS] = {6, 7,
The Keypad library provides a few convenience functions that are being used in the sketch,
so we include it in the first line. I find that this library is not absolutely necessary for our
purposes, so in the second demo I am not going to use it.
Next, we declare constants for the number of rows and columns my keypad has. These
constants are used later in the keypad object constructor. A constructor is a special
function that we use to initialise an object in a program. Ill discuss this in a bit more detail
further down.
We also need a map that assigns a symbol to each of the buttons of the keypad. We do this
by declaring a 2-dimensional array, in which each element is a character (an item of data of
type char). A 2-dimensional array is simply an array in which each element is another
array. In other words, think of it as an array within an array. To declare this array, we used
this syntax:
char hexaKeys[ROWS][COLS]
The first part is the data type of the data that the array holds in its cells, i.e. char. Next,
hexakeys is the name of the array. Finally, [ROWS][COLS] define the size for this arrays
two dimensions. Remember that at the very beginning of this sketch we defined the
constants ROWS and COLS? Well, heres the first place were these constants are being used.
To keep things simple, in the same line we also initialise the array with the data it will hold.
We do that by using curly brackets to delimit the arrays rows, and commas for the
columns.
rows by brackets
{
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
columns by commas
};
Your keypad may have a dierent key layout, so this array is the place were you define the
mappings of keys vs symbols.
Before we can use the Keypad library, we need to define the digital pins on the Arduino
where the keypads row and column pins are connected. To do this, we create two arrays
that hold single bytes in each cell, rowPins[COLS] and colPins[ROWS]. Within the brackets,
we insert the values we stored in the two constants at the start of the sketch. Bytes are
integer numbers that range from 0 to 256.
We could have used the int data type to define these arrays here, but that would have been
a waste of several bytes in Arduinos limited memory. Each int occupies 2 bytes, even
though we only need 1 to store the small numbers that represent the values reported at
digital pins we use to connect the keypad.
Finally, we can go ahead and initialise the keypad object that we will be used to get the key
presses. We give this object a name, customKeypad, and a type, Keypad. We then use a
constructor function which has the same name as the type of the object. This is not by
chance: In the Arduino language, which is standard C with added libraries, a constructor is
a function with the same name as the name of the class. We know that calling such function
will create an object for that class, store it in memory, and return a pointer (or handle) to it
so that we can use it. A class can have several constructors, each accepting a dierent set
of parameters. The keypad library only has a single constructor though, so this is not an
issue here (see documentation).
So, to initialise the keypad, we pass the key map array, rowPin array, colPin array, number
of rows and number of columns to the constructor.
Then, we can make use of the keypad object by calling its getKey function in a loop to
read any keys pressed. This is what happens in the loop() function, where the Arduino
repeatedly calls customKeypad.getKey(). When you press any key on your keypad, this
4
function will return something that is other than null (null is a data type that represents
nothing - which means that in C, nothing is something!).
In the next instruction, the function if evaluates the data that getKey returned, and if it is
not null it will evaluate it as true, causing the block within the curly brackets to be executed,
and the symbol that corresponds to the key pressed to be displayed in the IDE monitor.
So there you go, in just a few lines of code (but with many wires), you can connect and use
a keypad with your project! But can we make our hardware more ecient?
Yes we can!
Occupying most of the Arduinos digital pins just for the keypad is not an ecient use of our
hardware. Although Demo 1 was simple to put together and make it work, it isnt good
enough for practical use. In Demo 2a and 2b, well improve the design so that a single pin is
used on the Arduino to transmit button pressing information.
Well do this using two dierent scripts, but the same hardware design.
R4
R1
In the table below (left), R1 is the first resistance from the right in the schematic, R2 is the
next one to the left, and so on (counter-clock wise).
R1
1K
R2
2.2
R3
3.3
R4
220
R5
480
R6
680
R7
1.5K
A representation of the way that resistors were connected to the keypad matrix is shown
above (right). Imagine that you press one of the keys, and that current flows through the
circuit that was just created. Only two of the resistors will be included in this circuit.
Pressing any other key will engage two dierent resistors, in a unique combination. So, the
voltage produced at the analog input pin will be unique to each resistor combination
engaged by each button pressing.
Therefore, it is important to pick resistors with particular values so that we can guarantee
that there are no two keys that can produce the same voltage at the Arduinos analog input.
Next, lets have a look at the sketch that decodes these voltages and assigns them to a
symbol.
if(key!=-1)
Serial.println(keys[key]);
delay(10);
}
int getKeypad(){
int ret=-1;
if(analogRead(A0)==0)
key_lockout=false;
else if(!key_lockout){
delay(20);
Serial.println(analogRead(A0));
ret=15-(log((analogRead(A0)-183.9)/58.24)/0.1623)+0.5; //calculate the key symbol
//position based on a formula with magic numbers
key_lockout=true;
//Lock the key to avoid multiple key detection
}
return ret;
}
This script approaches the problem of detecting the key pressed by applying a calculation
(highlighted in red) to the voltage measured in analog pin 0. The result of the calculation is
an integer that represents an index to the keys string that contains the key symbols. So, if
you press key 1, then the voltage measured in analog pin 0 will generate a reading that
once applied to the formula, will generate an index value 0. In the keys string, the symbol at
index 0 is 1.
The numbers in this formula are magic in the sense that they were calculated to
specifically match the resistors that we use in the circuit. If any of the resistors are slightly
o their advertised value, than this formula will not be able to reliably detect which keys
were pressed. Indeed, in my experiment, this happened.
Go ahead and try it your self. Some of the keys will work, but some others will not.
The lesson to take away is that although this solution is elegant (a single calculation
encapsulates the key detection logic), we have too many dependencies to worry about, and
that those dependencies are out of our control to calibrate (unless you have the ability to
create and solve an array of complicated dierential equations).
So, instead lets go for a solution that is less elegant, but allows us to calibrate our circuit for
the resistors that we have in hand. I use resistors with advertised values as in the resistor
table from the previous section, but without the expectation that they are perfect. I will
show you how to deal with the variations and how to calibrate the circuit so that key
presses are identified correctly.
char* keypressed = 0;
int keyboardPin = 0;
int keyboardValue = 0;
void setup(){
Serial.begin(9600);
}
void loop(){
keyboardValue = analogRead(keyboardPin); // read the keyboard value (0 - 1023)
while (keyboardValue < 5){
//do nothing until a key is pressed
keyboardValue = analogRead(keyboardPin);
delay(50);
}
readkeyboard(); //get the value of key being pressed "keypressed" i.e. 0-9
}
Sketch, part 1
I have split the sketch in two parts. In part 1 we take a reading from analog pin 0 and if it is
valid (that is, it is not less than 5 in the pins 0 to 1023 range1), then it will call the
readkeyboard() function that will evaluate the reading and determine which key was
pressed.
As you will see in part 2, the lowest value we measure in analog pin 1 in my implementation was 242. This
means that instead of 5, we could set a value up to 241 in the test for valid keyboard input. None of this is set in
stone, however, so I can experiment for a while until I find values that are reliable and produce stable results.
1
Sketch, part 2
In the second part of the sketch, we look at the function that evaluates the input
measurement from analog pin 0, and determines which symbol matches the key that was
pressed.
The function starts with taking a fresh measurement from analog pin 0. Note that we could
have reused the original measurement that was taken while in the loop() function by passing
it as a parameter to the readkeyboard function. Maybe you can try out this modification on
your own once you get this version working properly.
The function then goes into a series of comparisons. It is trying to find a matching symbol
by evaluating the measurement against maximum and minimum values for each symbol.
These maximum and minimum values can be determined by trial and error. Heres how it
can work:
10
In the third line of the function, we print the measured value to the monitor. Press key 1 a
few times to see what values come out. Make a note of the maximum and minimum values.
In my implementation, the output was always within the range of 842 and 845 (not
inclusive). Yours might dier. So, take these values and use them to edit the if statement
for value 1 accordingly. What you will have is something like this:
This means that if keyboardValue is greater than 842 AND keyboardValue is less than 845,
then the key that was pressed represents symbol 1. The operator && is a boolean AND,
which means that both expressions around it must be true in order for the combined
expression to be true.
Repeat this process for every key in your keypad, then do some thorough testing to make
sure there is no overlap. An overlap would cause at least one value to be possible for more
than one key. If this happens, the Arduino would always return the last match as the
corresponding symbol.
Another thing to notice is that in my implementation, each of the keys has a tolerance of 2
or 3 units, out of 1023. This is very precise spacing between the keys, which means that we
potentially have room to decode a keyboard with more keys.
Exercise
In this lecture you learned not only about how to connect a keypad to your Arduino, but
also gained some skills in calibrating your project to match exactly your specific
configuration. You also discovered that there is a range of possible solutions to the same
problem, and that solutions dier in terms of elegance and practicality.
I think you are ready now for your most challenging exercise to date.
1. A keypad
2. An LCD screen
4. A DHT22
5. An Arduino
Build a project that combines these components, and works like this:
1. If the user presses key 1, the LCD screen displays the temperature.
11
2. If the user presses key 2, the LCD screen displays the humidity.
3. If the user presses key 3, the LCD screen displays the light intensity.
4. If the user presses key A, the LCD screen will say hello.
12
Peter Dalmaris
Lecture 33
Lecture 33
Ethernet
So far in this course, our Arduino has been capable of communicating only with its host
computer. And that communication was very limited: upload the sketch from the host
computer to the Arduino, and sent text messages from the Arduino to the computer via the
USB serial interface.
We would like to do much more with communications, and in this lecture I will show you
how to get your Arduino gadget to speak to the Internet!
There are several ways to make this work. In this lecture we will look at the Ethernet shield,
which represents the easiest (while still very capable) method. There is also Wifi, which is
technically very similar to Ethernet but wireless. I will leave wifi aside for another time.
Ethernet is the most common Local Area Network (LAN) technology around. You probably
have an Ethernet device in your home connecting your computers, printers, storage servers
etc together and to the Internet via a router.
I personally find the Internet of Things a fascinating concept, and people come up with all
sorts of applications to show what is possible to do this Universe. Heres some examples of
people have done: http://postscapes.com/internet-of-things-examples/
!
!
1
Peter Dalmaris
Lecture 33
In this demonstration, I will show you how to connect your Ethernet shield, and how to
make your Arduino part of your local
network. Specifically, I will show you how
to connect to the network by automatically
acquiring an IP address via DHCP, or how
to acquire an IP address manually. For
each connection, your Arduino will report
the connection attributes in console.
You need:
Arduino Uno
If you are using an Arduino Uno, then the Ethernet shield will fit perfectly on top of it. Note
that the Ethernet shield comes equipped with a mini SDcard slot. You can use this feature to make it possible
for your sketches to read data from files (like images,
CSS files etc), or for writing data onto files. For
example, you could store output from sensors in a log
file for later processing, of for uploading to an external
service periodically.
So, plug the shield onto the Arduino, no wiring required. This is what you end up with:
Peter Dalmaris
Lecture 33
!
Done with the hardware!
Lets have a look at the first Ethernet sketch. This sketch simply makes the Arduino a
member of the LAN and waits for someone to connect to its Telnet server. When a client
sends it a message (simple text), the server will simply send it back to the client (echo).
I will discuss several concepts related to how Ethernet LANs work rather than being
specific to the Arduino, so please bear with me if you are already familiar with them (or just
skip ahead to Demo 2).
#include <SPI.h>
#include <Ethernet.h>
void setup() {
Serial.begin(9600); // Open serial communications and wait for port to open:
while (!Serial) { // this check is only needed on the Leonardo:
; // wait for serial port to connect. Needed for Leonardo only
}
This sketch is one of the samples that come with the IDE. You can load it by going to
File > Examples > Ethernet > DhcpChatServer.
3
Peter Dalmaris
Lecture 33
At the top of the sketch we have the inclusions of the SPI and Ethernet libraries. The SPI
library provides the functions necessary for the Ethernet shield to communicate with the
Arduino using the SPI bus. The Serial Peripheral Interface bus is a full-duplex
void loop() {
EthernetClient client = server.available(); // wait for a new client:
if (client) {
// when the client sends the first byte, say hello:
if (!gotAMessage) {
Serial.println("We have a new client");
client.println("Hello, client!");
gotAMessage = true;
}
char thisChar = client.read(); // read the bytes incoming from the client
server.write(thisChar);
// echo the bytes back to the client
Serial.print(thisChar);
// echo the bytes to the server as well
}
1) A MAC (Media Access Control) address, which is the physical (hardware) address of the
network controller. This is a bit like the VIN number of a car, or the serial number
marked at the back of your TV. The Ethernet protocol uses this address to figure out
which device is sending a message and who the recipient is supposed to be. The
Arduino Shield also has such an address, and it should be printed somewhere in the
packaging or on an insert sheet of paper. Look up yours and copy the information in
header of the sketch:
Cant find your controllers MAC address? No problem. I have also lost mine. Chances
are that if you only have one Arduino Ethernet shield in your network, you will be just
fine by using the MAC address from this example. As long as there are no two
controllers with the same MAC address, the shield will work. If you suspect there is a
conflict, just change one of the fields of the mac[] byte array. For example, this should
also work (change in red):
Peter Dalmaris
Lecture 33
2) The IP (Internet Protocol) address is the address that any device connected to the
Internet needs to have. The IP address is also known as logical address because it
can be assigned to the device dynamically, unlike something like the MAC address
which need to be hardwired or at least statically defined (which is what you did in the
previous item). In most networks, including (most likely) your own home network, there
is a service called DHCP (Dynamic Host Configuration Protocol), which makes it
possible to just plug in an IP device and have it made part of the LAN by leasing it an IP
address (among other things). In your network, if you can just plug in your computer
and access the Internet, there are 99.999% chances that your network has a DHCP
server running. In the unlikely case that you dont, then configure your Ethernet shield
with a unique (for your network) IP address like this:
3) Next up is the gateway. The gateway variable holds the IP address of the device to
control access to the outside world, a.k.a. the Internet. If your Arduino needs to access
a web server or web service outside your LAN, it will need to know who to ask for
access, and that is the gateway. If there is a DHCP service running in your network,
than this is a piece of information that the DHCP will provide (I will explain how this
happens in a minute). If not, or if you just want to have a fallback, you can specify this
in this line:
Again, I had to edit this setting because in my network the gateway is at a dierent
address (change in red):
If you are on Windows, to find out your default gateway, try out this: http://
answers.yahoo.com/question/index?qid=20100221132909AA7jMQk
Peter Dalmaris
Lecture 33
4) The last required network configuration is the subnet mask. Because the possible IP
addresses (and hosts) in a single network are 2 in the power of 32, we find it much
easier to split this pool of addresses in small parcels, and deal with each parcel as a
separate network. The subnet mask is the bit of information we need in order to know
how our network has been split. In the example, the default subnet mask is:
Notice the two zeros at the end? This means this network allows 8 + 8 = 16 bits for the
host addresses, which means 2^16 hosts. That is 65,536 hosts. In my network, I have
lots of computers but not THAT many, so instead I am using this subnet mask:
This allows me to allocate 255 IP addresses in my network. Thats enough for most
home networks, and most likely what your network is using too.
So that how we setup out sketch so that our Arduino becomes part of the LAN.
EthernetServer server(23);
A server is an object that constantly listens for messages send to it by clients that connect
to a particular TCP port. The parameter we specified here, 23, represents one of 65,536
such ports. We could have chosen any one of those ports, however because we will be
using Telnet in minute, its best to keep things simple and use the default port for Ethernet.
In lecture 34, we will setup a web server to run on the Arduino, so the default port for that
will be 80.
Because we want the server to only do something when a message is received, we declare
a boolean variable to keep track of these events:
In the setup() function we first initialise the Serial connection to the IDE monitor, and then try
to setup the Ethernet controller. This instruction is very important:
Ethernet.begin(mac)
This tries to contact a DHCP server and lease an IP address, as well as to get information
about the gateway and subnet mask. If a DHCP server responds, then any values it
provides will be used to setup our Ethernet adapter. But, if the begin(mac) function fails (it
6
Peter Dalmaris
Lecture 33
will return a 0 if it does), then the sketch will initialise the Ethernet adaptor by using the
default values we provided at the start of the sketch.
Once this process is complete, the sketch will print out the allocated IP address. The
function
Ethernet.localIP();
returns the IP address as an array of bytes, and in the loop that follows the sketch converts
each field in that array to its equivalent decimal:
Serial.print(ip[thisByte], DEC);
Finally, at the end of the setup() function, the Sketch starts the Telnet server:
server.begin();
Now the server is active and just waiting for a message to arrive. We test for incoming
messages in the loop() function.
First, we check that a client is attempting to connect by calling the available function of
the server object, and if there is, we assign it to a new client object of type EthernetClient:
Otherwise, read the transmitted character and copy it to both the serial port, and to the
server object so that it is displayed to the user via the Telnet console.
To try out this connection, start up a console (command line) and type this1:
telnet 192.168.111.61
which works for my Arduino ethernet shield, and you will probably need to adjust for
your settings. You should see something like this:
If you are using Windows, especially Windows 7, read this article for a guide on how to enable Telnet client on
your computer: http://social.technet.microsoft.com/wiki/contents/articles/910.enabling-telnet-client-inwindows-7.aspx
1
Peter Dalmaris
Lecture 33
When I start typing random characters, these are echoed back to my console (to the right,
with the black background):
You can see that every character I type is copied both to the monitor using the
Serial.print(thisChar) function as well as the Telnet console using the
server.write(thisChar) function. But on the console side, every character I type appears
twice: once for my original key press, and the second for the response from the server.
Dont worry about the text garbage that appears in the monitor. Well worry about this later.
For now, congratulations, you made your first Ethernet connection through your Arduino!
You need:
Arduino Uno
Photo-resistor
LED
1K resistor
Peter Dalmaris
Lecture 33
jumper wires
Lets see what this we are trying to achieve with this circuit before looking at the sketch.
void loop() {
EthernetClient client = server.available(); // wait for a new client
commandString = "";
if (client) {
// when the client sends the first byte, say hello
if (!alreadyConnected) {
client.flush(); // clear out the input buffer
commandString = ""; //clear the commandString variable
server.println("--> Please type your command and hit Return...");
alreadyConnected = true;
}
while (client.available()) {
char newChar = client.read();
// read the bytes incoming from the client
if (newChar == 0x0D) // If a 0x0D is received, a Carriage Return,
// then evaluate the command
{
server.print("Received this command: ");
server.println(commandString);
processCommand(commandString);
} else
{
Serial.println(newChar);
commandString += newChar;
}
}
}
9
Peter Dalmaris
Lecture 33
When a user connects via Telnets, they can type commands for the Arduino to execute. In
this version of the sketch there are three commands, but you can add as many as you
want.
Type photo, and the Arduino will respond with a reading from the photo-resistor. If you
type ledon, it will turn the LED on, and if you type ledo it will turn the LED o. Anything
will give you the instructions:
void instructions()
{
server.println("I don't understand");
server.println("Please use one of these commands:");
server.println("* photo, to get a reading from the photoresistor");
server.println("* ledon, to turn on the LED");
server.println("* ledoff, to turn off the LED");
}
10
Peter Dalmaris
Lecture 33
The first part of the sketch is identical to the one in Demo 1, except for the declaration of
the ledPin integer, which contains the pin number where the LED is connected, and the
string commandString which will hold the last command we send to the Arduino.
In the loop() function, we wait for a client to connect, just like in Demo 1. Once we have a
connection, we clear the commandString variable to get it ready to store a new command.
If a client is not already connected, i.e. a new one just got connected, the clean the input
buer to make sure theres no garbage from previous connections, print out a message to
the client to let them know the Arduino is ready to receive a command, and change the
alreadyConnected variable to true.
What happens next is the most interesting part of this sketch. Here it is:
In processCommand (next page), the sketch checks to see if the command given matches any
of the three valid commands (photo, ledon, ledo). It does that by using a string
function:
command.indexOf(photo")
The indexOf function checks to see if the string photo exists in the string stored in the
command variable. If it does, it will return a positive integer number, but if it doesnt it will
return -1. If you want to expand the repertoire of commands for your Telnet server, just add
new blocks that test for the new commands using the indexOf function.
Once a command is found, we want the sketch to execute whatever code is in its block and
then continue listening for new commands instead of trying to match. That is why we
include the return statement at the end of every block: we want to stop the command
evaluation there.
If none of the commands available match the command we issued, the Arduino will call the
instructions function which will print out a set of instructions.
Simple!
11
Peter Dalmaris
Lecture 33
So what did you learn in the lecture? Quite a lot! You got through the fundamentals of
connecting a host to an Ethernet LAN and to the Internet, and you learned about getting the
Arduino connected to your LAN. But most important, you learned about controlling simple
components connected to your Arduino using Telnet. It only takes one more step to
connect to your Arduino using Telnet from the Internet, which will allow you to control your
LED, your motors, or whatever else is connected to it from anywhere in the world. Thats
the Internet of Things!
You can do this by making a configuration change to your home router and enable port
forwarding. Wikipedia has a good article to help you understand what this is, and this web
site contains an extensive list of modem/routers with specific instructions for each model.
Hopefully yours will be in this list. In a nutshell, port forward allows a user from the Internet
to access a server running inside a LAN without exposing this server to the Internet.
Exercise
I hope you are feeling excited now! How about you try out these exercises to stretch your
skills a little more
1. Add a DHT22 to your Demo 2 setup, and extend the repertoire of commands of the
Telnet server so that it also returns the temperature and humidity.
2. Add an LCD screen which displays the IP address of your Arduino when it starts up and
connects to the network. Once connected, the LCD screen displays the commands it
receives from the Telnet client.
12
Peter Dalmaris
Lecture 35
Lecture 35
A simple reporting web server
The Web is an open and well understood technology for disseminating information.
Equipped with an Ethernet shield, your Arduino can host a simple web server to which
clients can connect and interact with.
In the simplest of cases, an Arduino web server will be used to report readings from its
sensors to whomever is asking. In this lecture, well look into the scenario of using a web
server running on the Arduino that reports values from its sensors. We will do this reporting
in two dierent modes: First, for humans to view using HTML, and second for machines to
read where the data reported will be formatted as simple comma separated values.
Before we get started with the demos, lets go through a really quick discussion on running
a web server on the Arduino.
!
Principals of Arduino web servers
!
The Arduino is a remarkable piece of hardware, but it isnt build to be a full blown web
server. It has a single tiny processor with very limited memory when compared to build-forpurpose web servers. And this processing power is really optimised for dealing with
sensors and input/output components, not with the kind of processing that a web server is
required to do.
Another limitation is the programming language. The Arduino language is standard C with
added libraries, like the Ethernet library we saw in lecture 33. It is a bare-minimal language
that is fairly close to the hardware so that it can be compiled eciently for the AtMega
micro-controllers, and as a result it doesnt have the kind of programming sugar that highlevel languages like Ruby and PHP have. This sugar makes it a lot more easier for
programmers to build elaborate web applications with relative ease when using, for
example, Ruby, as opposed to C.
For example, take images. A jpeg image you might find in a simple web site may be 15 or
20Kbytes. The Arduino Uno only has room for 32KBytes in its RAM, so the image will not fit
and we will need to store it in an SD card or on another web server and make a reference to
it from the Arduino sketch.
What about SSL encryption? This is a standard feature for most non-trivial web sites these
days, but the Arduino simply does not have the computational power required to support
SSL.
These reasons, in a nutshell, require that any server we host on an Arduino, be it a web
server, a telnet server, or anything else, has to be minimal.
Peter Dalmaris
Lecture 35
!
Minimal, because we dont have much memory to spare, because programming anything
more complicate will take a lot of programming eorts, and because we want to maintain
CPU resources for sensing and control, rather than reporting.
However, with all that said, it is possible to interface the Arduino with a more capable
computer and ooad to that computer the more elaborate features we would like to enrich
our Arduino-power web site with.
With this kind of arrangement, you use the Arduino for sensing and control. You also have a
very simple web server running on it, like the one well see in this lecture. But instead of
opening it up to the whole world, you allow only a single client to connect to it. The green
device in the middle of this schematic is a Raspberry Pi, a single board computer that costs
around $45. Yes, this is an actual computer which can run Linux, and that means that you
can have a full-blown web server on it build with a high-level language. You can build your
application on the Raspberry Pi (or similar) so that clients from the Internet will connect to it,
and use it as a proxy for your Arduino. This architecture makes it possible and easy to have
an Arduino-powered web site with SSL, graphics, CSS, and all the bells and whistles to
which we are accustomed.
Showing you exactly how to do this, is a topic for another lecture though. Just know that it
is possible for the time being.
With these points in mind, lets go ahead and have a look at an Arduino bare-minimal web
server.
In this Demo, Ill show you how to setup a basic reporting web server on your Arduino. Well
look at the simple web server from one of the examples that come with the IDE. In Demo 2
well make a few changes to it so that it reports data from a humidity/temperature sensor,
and a photo-resistor.
Peter Dalmaris
Lecture 35
To assemble the hardware, just plug the Ethernet shield on the Arduino. The sketch we will
play with takes measurements from the analog pins and shows them in a web page. You
can connect whatever analog sensors you have handy, but that is not necessary. In the
absence of actual sensors providing readings, the values on the analog pins will be random.
To load this sketch, go to File > Example > Ethernet > WebServer.
#include <SPI.h>
#include <Ethernet.h>
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
!
!
This block of the sketch is familiar: it is identical to the definition and setup parts of the sketch in
Lecture 33. It sets up the Ethernet connection and a server. The only dierence is that in this sketch,
the server will be listening for connections on TCP port 80 (highlighted in red).
The next block is a lot more interesting. It is there where the parsing logic of a web server is
implemented. Have a quick read but dont spent too much time on it for now. There is something
else I should show you first before the parser can make sense.
!
!
!
!
!
!
!
Peter Dalmaris
Lecture 35
void loop() {
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
boolean currentLineIsBlank = true;
// an http request ends with a blank line
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed
//after completion of the response
client.println("Refresh: 5"); // refresh the page automatically
// every 5 sec
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
// output the value of each analog input pin
for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
int sensorReading = analogRead(analogChannel);
client.print("analog input ");
client.print(analogChannel);
client.print(" is ");
client.print(sensorReading);
client.println("<br />");
}
client.println("</html>");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disonnected");
}
}
Peter Dalmaris
Lecture 35
Web pages travel from web server to web client on top of HTTP: the Hyper Text Transfer
Protocol. This protocol has certain rules which web servers and clients must abide by if
they are to work together. A web server or client parser is a computer program that knows
how to understand HTTP. When your web browsers send a request to a web server, it
makes a HTTP request.
Ill use Requestbin, requestb.in, a website that shows me the bare content of a HTTP
request, a kind of X-ray vision for the Web. Visit this site, and you will see this:
Click on the green button, this will create a RequestBin for you, which is a URL to which
you can send an HTTP request.
Peter Dalmaris
Lecture 35
!
In the Bin URL box, you have exactly that, a URL to which you can send HTTP requests.
In a new window or tab of your web browser (keep the page above handy), copy/paste the
URL in the Bin URL box, and hit Enter.
!
!
This just means that RequestBin received your HTTP request. Now go back to the tab or
window that contains the Bin URL box and refresh it. You will see something like this:
Peter Dalmaris
Lecture 35
!
What you see here are the raw contents of your HTTP request. In the red box, I have
highlighted the most important part of this request for our present discussion: It is the first
line that your web browser trasmitted to the server. It starts with a verb, in this case GET,
and a path for the resource that was requested, in this case pebptxpe.
There are other bits of information provided here, like in the headers section.
All this is text that the Arduino will have to parse in order to correctly respond to a clients
request.
Our Arduino sample web server sketch is only programmed to identify the end of a client
HTTP request, and then to send out its HTTP response.
Peter Dalmaris
Lecture 35
According to the HTTP protocol, a HTTP GET request must end with a blank line. In the box
below, you can see the complete HTTP GET request, and the last blank line.
GET / HTTP/1.1
Host: 192.168.111.177
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://192.168.111.177/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,el;q=0.6
blank line
This means: if the current line is blank and the current character is a new line, then the
expression is true, so the sketch will go ahead and print its response.
The boolean currentLineIsBlank is there to keep track of new lines: when a new line
character is found (\n), then currentLineIsBlank becomes true. When anything else is
found, it becomes false. This is happening in this code block:
if (c == '\n') {
currentLineIsBlank = true;
} else if (c != '\r') {
currentLineIsBlank = false;
}
So, the Arduino, will know that the HTTP request is finished if it finds two new lines (\n)
are found, one right after the other.
You may need to read these last coupe of pages a few times before you continue, but
before doing that, consider this: we did all this just so that we can detect when a HTTP
request has finished so that the server can respond. We havent done any serious parsing
yet!!!
Peter Dalmaris
Lecture 35
This request is more like a trigger, it asks the server to send back whatever it wants to send.
For the server to send a response back, it needs to be formatted in accordance with the
HTTP rules. A response needs to start with a response header, followed by the response
body.
client.println("Content-Type: text/html");
client.println();
In the first line, the server sends a 200 HTTP code, which tells the client that it understood
its request and there is no problem to worry about. Always nice to hear that!
Next line informs the client of the type of content that will be following in the body of the
response. In this case, it will be HTML text.
Next, the server tells the client that once it sends what it has to send, it will close the
connection. If the client needs something else, it will have to initiate a new connection via a
new HTTP request.
Next line contains a non-standard instruction. Refresh tells the browser to send a new
GET request to the server in order to get an updated page. Most browsers are able to
understand this instruction, even though it is not a standard HTTP instruction.
Finally, the server prints a new line. Dont forget that! In order for the client to know that the
HTTP response header has completed its transmission, there has to be a blank line!
The body of the response follows, and the Arduino will construct the HTML for it line-byline. Heres the code for this:
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.print(analogChannel);
client.print(" is ");
client.print(sensorReading);
client.println("<br />");
client.println("</html>");
break;
Peter Dalmaris
Lecture 35
The first bit of text printed to the client stream is the HTML declaration. This is not actually
an HTML tag, but an instruction to the browser informing it about the version of HTML used
in the page that follows.
The actual HTML follows. You can see that there is a for loop that samples the analog
ports, and the prints out their captured values to the client stream.
Finally, there is a break statement. This breaks the while loop that started with the while
(client.connected()) instruction earlier. The sketch is about to close the connection by
calling the stop() function of the client object, but before doing so it waits for 1 millisecond
for the client to finish receiving the response.
Upload the sketch and fire up your browser. Visit your Arduinos web site, and this is what
you should see:
I have not connected any sensors to the analog ports, so the values you see here are
random.
With this detailed discussion on getting a web server running on the Arduino for simple
reporting, lets go ahead and try out a simple modification from this basic example sketch.
Lets connect a humidity and temperature sensor, and a photo-resistor, and report the
values they report through our web server using HTML and CSV. The HTML will be useful
for humans, while the CSV is more machine-friendly.
10
Peter Dalmaris
Lecture 35
In this demo, I would like to use my trusty DHT22 and photo-resistor sensors. I would like
to be able to use my web browser and get readings from the sensors like this:
If a machine makes the same request, I would like it to receive this response instead:
11
Peter Dalmaris
Lecture 35
12
Peter Dalmaris
Lecture 35
!
What is new in the sketch compared to the one in demo 1 is highlighted in red. The new
sketch just takes readings from the DHT sensor and from the analog pin 0 and prints them
out in the client stream.
We removed any printouts of HTML tags, and add commas to delimit the values.
Although it is out of scope for this lecture, it is worth mentioning that for transmitting simple
values like the numbers in this example, the CSV format is a very ecient of going about
sending data from one machine to the other.
Conclusion
You covered a lot of ground in this lecture. You learned about how HTTP requests are
structured, and how the Arduino can host a very simple reporting server. There were a lot of
details involved, but what I think would be most useful to remember is the fact that a web
server is essentially a parser of text which interprets HTTP requests and responses.
Although the examples in this lecture did not do much parsing other than figuring out where
a HTTP request ends so that a responds can be sent, what you learn will prove very useful
in the next lecture. In that lecture, you will learn about creating a server that parses a
request so that it can find out the specific parameters of what is it that it is being requested
to do. You will learn, for example, how to get the server (i.e. the Arduino) to turn on the LED
at pin 9. I call this kind of server controlling, because it allows you to control the Arduino
via the web, not just to get readings of its status.
!
13
Peter Dalmaris
Lecture 35
Exercises
Try to add a couple of sensors of your choice to the HTML version of the Demo 2 sketch.
Adjust the sketch so that the values are printed nicely to the client. If you have CSS skills,
perhaps improve the looks of the output by incorporating some styling to the page that the
Arduino generates.
14
Peter Dalmaris
Lecture 37
Lecture 38
A simple controlling Arduino web server
In Lecture 35, you learned about the basics of parsing HTTP requests and you implemented
a simple web server. I referred to that web server as reporting because it was build to only
report the values of pins on the Arduino but did not oer any way of changing their state.
In this lecture, I will show you how to setup a web server on the Arduino that does just that.
A user will be able to connect to the Arduino using their web browser and turn LEDs on and
o. You could replace the LEDs for other devices, like motors, without having to introduce
significant changes to the sketch well see here. We will actually do this in the next lecture
on web-based motor control.
Just like in lecture 35, we first need to look at the HTTP request parsing issues that we will
need to deal with before implementing the controlling web server sketch. Because the web
browser will be sending information with instructions to the Arduino, the Arduinos web
server HTTP parser will have a lot more work to do.
Lets go through the demo, and start by looking at how HTTP deals with data going from
the client to the server.
Sending data to a web server can be done in two ways: GET and POST.
Simply speaking, in a GET request, the data that you want to send, travels to the server as
part of the URL. When you go to your browsers URL field, type in a URL like google.com
and hit the Enter key, you are triggering an HTTP GET request.
So, here is an example. If I want to sent a message to my Arduino asking it to turn the LED
in pin 8 on, I could put this in the URL:
http://192.168.111.177/?pinD8=1
In this example, you can recognise the first part of the URL, in black. It is simply the
protocol followed by the IP address of the web host, which is my Arduino. After the slash,
there is a green question mark ?. I am using this character as a marker for the location in
the URL where the instructions I want to sent begin. The parser running on my web server
on the Arduino will be looking out for this special character and once it finds it, it will start
looking with great interest for whatever follows.
The part in red is the actual instruction. I have chosen to keep things very simple, and use a
protocol that provides me some flexibility for future changes of the parser as my
requirements change. In the current iteration, my protocol says that a command should
1
Peter Dalmaris
Lecture 37
start with the three characters pin, followed by an integer, followed by an equal sign =,
followed by another integer. Once the parser in the web server encounters the ?, it will
then start looking for the pin instruction. Whatever character comes next will signify the
type of pin we want to manipulate. After that is an integer which signifies the pin number
that we want to manipulate. And finally, after the = character, any integer that the parser
finds signify the value that we want to write to this pin.
In the example above, I am asking the web server to write value 1 to digital (D) pin 8. If I
wanted to write value 32 to analog pin 2, according to my protocol, I would issue this URL:
http://192.168.111.177/?pinA2=32
Simple, right? The current version of the sketch that is published on Github only supports
the first example, but later on we will discuss the parser itself and you will see that
changing it to accommodate the second example is very easy.
Now, lets have a look at the raw information that travels to the Arduinos web server. This
will help us figure out exactly what is it that the parser will need to work with. To look at the
raw contents of the GET request, Ill use Chromes Inspect Element feature. We first had a
look at this really nice tool in Lecture 33.
Peter Dalmaris
Lecture 37
To access the source of the GET request, go to any page, right click anywhere on the page,
and select Inspect Element. Then, click on Network. Click on a link on the page to trigger a
GET request, and observe how several new rows are added to the box in the right side of
the Inspect Element pane. Each one represent an HTTP request. Click on any of them to
get the requests details. The Request Headers section displays a nicely formatted view of
the information that the GET request transmitted to the server. This view is nice, but we
want to know EXACTLY was is it that the web server received, so click on view parsed to
switch to the raw view of the headers.
What you see now is what the server sees. In the case of my example, notice that the first
line (in the blue box) contains the word GET, which is the HTTP verb for this kind of
request, followed by /?led8=1. This matches the second part of the URL I typed in the
browser, right?
Well, yes, this is the simplest way to transmit data with instructions to the server. The parse
can ignore anything else below the first line (unless it is interested to know, for example,
who is sending the request, what kind of browser is used, etc.).
An alternative to post, is to use the POST HTTP method. This method transmits the user
data to the server in the body of the message instead of the URL. This is nice in case you
dont want your user to be able to see exactly what is being transmitted. It is also preferable
if you have lots of data to transmit, since the number of characters you can fit in the URL is
limited. Lets have a look at a POST request and spot the dierences between it and a GET
request.
To switch my example from using the GET method to using the POST method, I change the
form HTML element method attribute to POST:
!
3
Peter Dalmaris
Lecture 37
!
First and foremost, in the request header and in the URL, my data is nowhere to be seen.
However, looking at the Form Data, which is the segment of the POST request that exists in
the body of the request, I can see the data I submitted. Everything else is the same.
The conclusion of all this is that this little dierence between GET and POST requests
means that parsing a POST request is significantly harder than parsing a GET request, and
will take much longer to complete on the Arduino. While in a GET request, the information
we are sending is available in the very first line and right after the HTTP verb, for a POST
request it is tacked way down in the document. The Arduino and the parser will have to
search for this information by first scanning every single character that precedes it. This fact
alone means that there will be a significant impact on performance, not to mention the
significant programmer unhappiness (to say the least) that is unavoidable when you try to
write and debug code like this.
Bottom line: Avoid POST requests unless you really have to, and even then consider
alternatives!!!
!
!
4
Peter Dalmaris
Lecture 37
!
!
!
!
!
!
!
!
!
The sketch that well use
implements this web page
(right).
Peter Dalmaris
Lecture 37
Sketch
This sketch is larger than those we have seen so far. Because of this, I have added several
functions that help with the added complexity by packaging functionality in individual
functions. For example, there is special function that creates the header of the servers
response, another one that generates the HTTP form part of the web page that the server
returns, and one that implements the parser, to name a few.
#include <SPI.h>
#include <Ethernet.h>
byte
byte
String
mac[]
ip[]
message
EthernetServer server(80);
String
get_request
boolean
reading
void setup()
{
Serial.begin(9600);
Ethernet.begin(mac, ip);
server.begin();
Serial.println("ready");
}
Peter Dalmaris
Lecture 37
!
void print_form(EthernetClient &client)
{
client.println("<h2>Click buttons to turn pin 8 on or off</h2>");
client.print("<form action='/' method='GET'><p><input type='hidden' name='led8'");
client.println(" value='0'><input type='submit' value='Off'/></form>");
client.print("<form action='/' method='GET'><p><input type='hidden' name='led8'");
client.print(" value='1'><input type='submit' value='On'/></form>");
}
void end_page(EthernetClient &client)
{
client.print("</body>");
client.print("</html>");
}
!
the loop function is shown in the next page
!
!
!
!
!
7
Peter Dalmaris
Lecture 37
void loop() {
EthernetClient
client = server.available();
String return_message;
if (client) {
Serial.println("new client");
boolean sentContent
= false;
get_request
= "";
boolean
currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char
c = client.read();
if(reading && c == ' ')
{ reading = false;
parseGetRequest(get_request);
break;
}
if(c == '?'){
reading = true; //found the ?, begin reading the info
if(reading){
get_request += c;
}
}
if (!sentContent){
construct_page(client, return_message);
sentContent = true;
}
delay(1);
client.stop();
Serial.println("client disonnected");
}
}
Peter Dalmaris
Lecture 37
Sketch disassembly
A lot is happening here, and much of this sketch you have not seen before.
The first few lines are familiar as thats where the EthernetServer object is being setup with a
MAC and IP address.
I am declaring a String called get_request in which the sketch will store the actual
instructions send by the GET request. For example, if my browser sends out this:
http://192.168.111.177/?pinD8=1
then the value that will be stored in the get_request variable will be pinD8=1. The work
for extracting this string from the HTTP request and storing it in this variable is done in the
loop() function as well see in a minute.
After the get_request declaration, I am setting up a boolean variable called reading. This
variable is true when the loop() function detect the ? symbol in the HTTP request. The ?
symbol, as you may remember from earlier, is used as a market that signifies the beginning
of the part of the URL that contains the instructions that the user is sending to the server.
Next are the definitions and code of several functions, starting with construct_page. This
function makes calls to other functions that implement the basic components of the
response web page (i.e. the page that the web server returns to the browser):
The part of the page that contains a confirmation message for the user, implemented by
the print_confirmation function,
The ending tags of the HTML page, implemented by the end_page function.
Have a look at these functions and you should see recognisable HTML. The print_header
function is slightly dierent because it also contains the HTTP response header, with the
first two lines carrying the HTTP declaration and content type, then a blank line. The HTML
document follows.
Also have a good look at the print_form function. I have created one form per button
because when a button is clicked on I only want a single value-pair to be sent to the server.
Also notice that the form method attribute is set to GET, not POST.
You can notice that each of these functions receive one or two parameters. The
construct_page function received two parameters:
Peter Dalmaris
Lecture 37
The first one is called client, is of type EthernetClient. The & means that client is
actually a pointer to the memory location where the object of type EthernetClient is
stored. If we dont use this & modifier, the Arduino will actually create a copy of this
object and pass it to the function. Because memory is at a premium, we dont want to
make copies of objects for which we can simply pass around a pointer, so that is what is
happening here and anywhere else you see the & modifier.
The second one is another pointer, this time pointing to a memory location where a String
object is stored. The String rmessage contains the confirmation message that will be
shown to the user.
Its starts as we have seen in the previous two Ethernet lectures, by acquiring a client. It
also declares a String variable that will be used later to store a message that will be
returned to the user.
Once it acquires a client, it will declare and initialise the sentContent boolean variable. This
variable is used so that for every client only a single response is sent. For a new client, the
sentContent variable is set to false, to indicate that a response has not yet been sent.
Once, at some point in time later, a response is sent, the this variable will be set to true.
Next, the get_request String is set to empty, getting it ready for the new instruction.
There is also another boolean declared and initialised to true. Its currentLineIsBlank, and
is used to remember a blank line when one is found. This is needed because according to
the HTTP protocol, a GET request ends with a blank line, which also ends with a new line
(\n).
Do a quick scan a few lines further down, and you will find this code:
10
Peter Dalmaris
Lecture 37
In the while loop, the Arduino assembles the instructions send by the web browser.
The client.connected() statement is true for as long as the client is sending data to the
server or there is still data for the server to read even if the client has disconnected. This
function, therefore, ensures that the server reads everything the client sends. With this
statement in a while loop, we ensure that the Arduino will keep reading data until everything
has been read.
The client.available() statement, returns the total available (not yet read) number of bytes
that the client has sent to the server. Because we are not interested in the actual number of
bytes but rather the fact that there are still bytes to be read by the server, we put this
statement in an if block.
The while and if functions make up the framework of the web server. Inside this
framework, the server will scan for instructions.
It starts by grabbing the next byte from the byte stream send by the client and storing it in a
local char variable:
char
c = client.read();
In the first check, it looks for the case where the Arduino is reading an instruction (reading
variable is true) AND the current character c is one white space . If this happens, then the
String variable get_request has captured the instruction sent by the client, and the sketch
will call the parseGetRequest function to parse it. It will then break the while block
because it is not interested in anything else that the client has sent. Why did I choose to
stop the parsing of the request here?
Remember that the first line of the GET request looks like this:
Next test that happens is the one that checks for the ? character that signifies the
beginning of the string that carries the client instruction. As soon as the Arduino hits this
character it sets reading to true.
11
Peter Dalmaris
Lecture 37
Next, it tests that the Arduino is currently reading an instruction. If this is true, it will append
the current character to the get_request String variable. The operator += is the append
operator, and adds whatever is given in the right side of the expression to the existing
contents of the variable.
The last three tests in this block we have already discussed so I will not repeat here.
In summary, the while-if loop goes through the data that the client sends to the server and
grabs the only thing our server is interested in, the instruction component of the URL. Once
it does that, the while loop breaks.
If the response has not yet been sent, then the construct_page function will be called to
do so. A 1 millisecond delay is inserted to give enough time to the client to complete
receiving the response, and then the connection is closed.
The only function left to discuss is the parseGetRequest function. Its job is to receive
something like led8=1 and figure out what it means.
We have already discussed the pattern used here: the string led followed by a letter,
followed by an integer, followed by the = sign, followed by an integer.
int
led_index = str.indexOf(led): The indexOf function searches for the
string led inside the string str. If it finds it, the first position of the match is stored in
the led_index integer variable.
int
led_pin
= str[led_index + 3] - 0: Add three to the led_index (since the
string led contains three characters) and return the character at this position after
subtracting 0. What is happening here is ASCII arithmetic. A string like led8 is made
up of chars. According to the ASCII system (which the Arduino observes), character 8
corresponds to decimal number 561. However, what I want this expression to return is the
actual number 8, not the ASCII equivalent of character 8. So, I adjust the formula so that
it subtracts 48 from it, which is the ASCII value for character 0. And I have 56-48 = 8. If
the client had sent led6 instead, that this expression would have evaluated 54-48 = 6.
int
led_val
= str[led_index + 5] - 0: Now add 5 to the led_index so that
the value after the = sign is returned. Again, we need to adjust for the ASCII values, so
the expression subtracts the ASCII value for 0 from the character at index led_index + 5.
In the next 5 lines, the function constructs the response that will be sent to the user as part
of the HTML page.
12
Peter Dalmaris
Lecture 37
And finally, action is taken by setting the LED on or o by calling the executeInstruction
function.
Conclusion
If all this has caused you a headache, I understand. You have created a web server, and
even though it is capable of understanding only simple GET requests, this is not a trivial
matter.
But I encourage you to stick with it, go through the sketch a few times on your own, change
things and learn from whatever you break. This sketch also has a lot of room for
improvement.
Feel free to post a questions if you need help, and suggestions for improvement if you have
any.
Exercises
1. Upload the sketch to your Arduino, and use your browser to access it. Click one of the
buttons and make sure that the LED in pin 8 is working. Notice that according to the
Demo schematic, there is an LED in pin 7, but there is no button for it. Hmm First,
make sure that you can turn it on and o by editing the URL directly. Change this:
http://192.168.111.177/?led8=0 to this http://192.168.111.177/?led7=0. I have
highlighted the dierence in red. Can you edit the sketch so that the web page contains
an on button and an o button for the LED in pin 7?
2. For the more adventurous in you, can you edit the sketch, and specifically the
parseGetRequest and executeInstruction functions so that you can also control the
analog pins? think about the change(s) that this will require in the string pattern that the
parser will need to understand.
3. Modify the sketch so that the server will be able to both receive an instruction (like in
the default sketch) but also to report the values from a couple of sensors of your
choice. Imagine that you are building a home automation system. You want to be able
to close a window shutter by turning on a DC motor, and to confirm that the light
intensity inside the room is reduced by reporting the value of an attached photoresistor.
13
Peter Dalmaris
Lecture 37
14
Peter Dalmaris
Lecture 40
Lecture 39
Motor control through a web server
In the previous lecture, you learned about controlling the state of a digital pin through
sending GET requests with your web browser.
What if you had to control two pins at once, and if you had to send over values other than
1s and 0s?
In this lecture, Ill show you how to do something like that by demonstrating how you can
control a DC motor through your web browser.
You may remember from lecture 20, that to properly control a DC motor you need two bits
of information: speed and direction. So, in this demo, we will look at how we can send
speed and direction information to the Arduino from the browser encoded in a GET request.
This makes for slightly higher parsing complexity on the Arduino, but nothing that we cant
handle by building on our current knowledge.
Instead of using a
potentiometer knob to control
the speed and direction of the motors, well use a web user interface composed of radio
buttons (see image in next page).
Each radio button, when clicked, sends direction and speed values to the Arduino via HTTP.
The web server running on the Arduino parses the HTTP request and isolates those values
so that it can the requested control signals to the motors.
!
1
Peter Dalmaris
Lecture 40
In this image, also notice the structure of the URL. You may remember that in the previous
lecture, the URL was composes of two parts: the host address and the key-value pair
containing the data that the user sent to the Arduino web server. These two were separated
by the ? character (a delimiter we chose).
In this example, the same holds true. The dierence now is that we have two key-value
pairs after the ? delimiter, instead of one. And these two value pairs are delimited by the
& character.
192.168.111.177/?speed5=122&direction4=0
Remember that the L298N motor control break-out board uses two pins to control a motor:
one for direction and one for speed. In the colourful example above (hopefully you are not
color blind!), in orange I mark the delimiters, purple mark the keywords, green are the pins,
and blue are the values. The web server will need to use the following logic in order to
extract the necessary information from this string of characters:
1. Look for the location of the ? character. This indicates that the instruction key-value
pairs follow.
3. Read the character immediately after the keyword speed. Convert this character to an
integer value.
Peter Dalmaris
Lecture 40
5. Read the character(s) after the = and before the & characters. Convert these
characters to an integer.
7. Read the character immediately after the keyword direction. Convert this character to
an integer.
8. Read the character after the = and before the end of the string. Convert this character
to an integer.
The code needed to implement this pseudo-code is slightly more complicated then the
one use in the previous lecture (where there was a single key-value pair to deal with), but I
promise it is not scary at all. Lets have a look at it next.
Sketch
Heres the sketch. Highlighted in red is the part that deals with analysing the two key-value
pairs. There are also changes in the function that generates the HTML, and the function that
executes the received instructions. Everything else is the same (I am including the whole
sketch for completeness).
#include <SPI.h>
#include <Ethernet.h>
byte
mac[]
byte
ip[]
String
message
will be shown to the user
EthernetServer server(80);
String
get_request;
boolean
reading
received
void setup()
{
Serial.begin(9600);
Ethernet.begin(mac, ip);
server.begin();
Serial.println("ready");
}
Peter Dalmaris
Lecture 40
void loop() {
EthernetClient
client = server.available();
char return_message[30];
if (client) {
Serial.println("new client");
boolean sentContent
= false;
get_request
= "";
boolean
currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char
c = client.read();
if(reading && c == ' ')
{ reading = false;
parseGetRequest(get_request);
break; }
if(c == '?'){
reading = true; //found the ?, begin reading the info
}
if(reading){
get_request += c; }
if (reading && c=='\n')
{ break;}
if (c == '\n' && currentLineIsBlank) { break; }
if (c == '\n') { currentLineIsBlank = true; }
else if (c != '\r') { currentLineIsBlank = false; }
}
}
if (!sentContent){
construct_page(client);//, return_message);
sentContent = true;
}
delay(1);
client.stop();
Serial.println("client disconnected");
}
}
Peter Dalmaris
Lecture 40
Peter Dalmaris
Lecture 40
In the printform function, the HTML form is constructed. I am using radio buttons instead
of regular buttons enhanced with an onClick Javascript event so that the form is
submitted when the user clicks on any of the radio buttons. Each radio input tag has a
name that is made up of a keyword (either speed or direction), and the number of the
pin it is meant to control. It also has a value attribute that contains a pre-determined value
that we want send to the Arduino when the radio is clicked.
The parseGetRequest function is rewritten. The parameter is a pointer (&) to the memory
location where the char array that contains the instruction is stored. The first thing that
happens here is to find the index position in that array where the key-value pair delimiter is
Peter Dalmaris
Lecture 40
stored (&)1, and keeps that value handy in the delimeter_index integer variable. To do
this, we use the indexOf function that is available to strings.
Then, in the same way, it looks for the location of the string speed in the char array str
and stores that value in the speed_index variable. The motor pin is 5 characters to the right
of the speed index, so we add 5 to the speed index to extract that value from the char
array. This gives us the motor pin value, and it gets stored in the motor_number variable. Up
to now, all this is identical to what we did in the previous lecture.
The next bit is slightly more challenging, because the value we need to extract, the motor
speed value, may be made up of 1, 2, or 3 characters. So, we dont know before hand the
beginning and end of the segment in the char array that contains this value. What we do
know, are the location of the first character (right after the = character), and the location of
the last character (right before the & character - delimiter). Here, I could have used the
substring function that is available to strings, which requires two parameters, the start and
end index of the substring that I want to extract. However, in my IDE version 1.0.5, this
function seems to have a bug in its implementation and I was not able to get it to work
reliably. So, I had to resort to a more basic (lower-level) solution, which involves looping
through the char array, picking one character at a time between the two index limits (after
= and before &), and storing each char in a temporary char array of size 4.
This array, speed_value_array, has a size 4 because the max number of chars I expect to
store in it is 3, and I need an extra char to the null character (\0). The null character
signifies the end of the string stored in the array, and is a C-language requirement.
Remember here, that you only need to provide enough space for this character, you dont
actually have to set the last cell of the array to null as this is done automatically at run time.
Also remember that in C (and all C-style languages that I know about), arrays are 0indexed. This means that the first cell index is 0, not 1. For this reason, the extracted value
is stored in speed_value_array like this:
speed_value_array[i - 8] = str[i];
Notice the i-8? Because the speed_index is always 1 (unless you change the order by
which the key-value pairs are encoded in the URL - dont do that yet!), I need to subtract 8
in order to store the first character extracted from the char array to index position 0 in
speed_value_array. If you wanted to make this code able to deal with the case of the
speed_index order being dierent to first, then you could write this:
it is only a coincidence that the C-language memory reference operator & is the same as the HTTP key-value
pair delimeter &.
1
Peter Dalmaris
Lecture 40
Last important point of discussion here is the atoi function: what we have now is a string
stored in an array of chars that contains the speed value. We need to convert this to an
integer, and the atoi is a C-language function that does just that. You give it a pointer to
an array of chars, and it gives you back an integer.
A similar method is used to extract the direction pin number and direction value from the
2nd key-value pair.
We know have all the necessary information, and the sketch goes ahead to apply the
instruction to the motor by calling the executeInstruction function and passing the
extracted values as parameters.
Feel free to read this a few more times in combination with the video. It will become clear
with a bit of experimentation. Feel free to ask questions if you get stuck anywhere.
Conclusion
In this lecture you learned about sending two key-value pairs that contain information for
updating the state of a connected device, a motor in our case. From here onwards,
complexity can, of course increase, but it can be managed with a bit of planning.
Exercises
I have two exercises to recommend here. The first one I rate as easy because you can
deal with it without having to expand on what you learned here.
The seconds one is harder because you have to go beyond what you learned here. You can
use the principles regarding designing a flexible URL structure, but after that you are on
your own.
Heres te exercises:
1. Adjust the web form segment of the sketch so that you can also control a second
motor. Imagine you had a toy car with two motors: one motor for moving forward and
backward, and one for left and right. Can you create a new version of the sketch so that
you can control the car from the web browser?
2. Create a sketch so that the parser can deal with 3 key-value pairs. For example,
imagine a URL like this: GET /?var1=10&var2=20&var3=30. How can you adapt the
parser we saw in the demo to extract all the information from this URL?
Peter Dalmaris
Lecture 40
Lecture 40
Logging on the web
You now know how to create a web server in order to monitor activity on your Arduino. This
is quite useful for real-time monitoring, where you want to know what is happening right
now with your sensors. But what if you wish to keep historical records of your sensor
readings so you can inspect an analyse later?
In that case you will need to somehow record these readings. Broadly speaking, there are
two ways to do this: you can record readings on-board the Arduino, by using an SD card
writer. Or, you can use an online service to which your Arduino can send its readings
periodically. From there, you can use your web browser to monitor in real time, plot
historical values on a chart, or download the data to your computer.
There are a few really nice services that allow you to do that, and in this lecture we will look
at Nimbits. Nimbits is a cloud service that allows you to log and retrieve data from all kinds
of devices, from Arduinos and Raspberry Pis to web server. Anything that generates data
needing logging can use Nimbits. Did I mention that Nimbits is open-source and free to use
by the community?
Another popular service is Xively, formerly known as Pachube. Xively provides a variety of
services, of which logging is just one. It has a dierent business model to Nimbits, and
while it allows free use by developers, it does have limits that an active Arduino hacker
could reach in no time.
In the second part of this lecture we will also look at logging your sensor data to Twitter.
Twitter is often useful not so much as a logging service (this would be a bit awkward), but
for its social broadcasting and direct communication capabilities. If you would like to
expose environmental data to the whole world to access, then Twitter is for you. Or, if you
want to receive a direct message as an alert when a sensor value exceeds a set limit, again,
you can use Twitter.
Peter Dalmaris
Lecture 40
Demo 1: Logging on
Nimbits
!
!
!
!
!
!
As a logging service, Nimbits provides several benefits:
2. Its free, supported by ads, with an option to purchase an account in exchange for an
ad-free experience.
3. You can download the software and host your own Nimbits server should your project
requirements need a privately hosted logging service. This is an important futureproofing feature that significantly safeguards your time investment to this technology.
4. Every new record is timestamped, which means that your Arduino does not need to
maintain a clock.
5. It utilises a simple HTTP (REST) interface, which means that your Arduino can talk to
Nimbits without any special libraries.
!
continues next page
Peter Dalmaris
Lecture 40
Click on the Login link. Nimbits uses Google for authentication and account creation, so if
you already have a Google account, you can go straight ahead and use it to logon.
Now you have an account, lets create a new Data Point to which you will be sending your
data, and a new Read/Write key to use for authenticating your Arduinos access to this data
point.
Peter Dalmaris
Lecture 40
Heres the starting point for the process that will follow: the Admin Console.
1. The user interface can be a bit quirky. When you add a new element, it must be
attached to an existing element in the hierarchy. Since this is the first element/data
point to add, click first on your email address to select it, and then right click to get a
list of properties. Then, select New Data Point:
Peter Dalmaris
Lecture 40
3. Create one more data point in the same way. The sketch we will look at later will be
posting data to two data points.
4. Your Data Point is now ready to receive data once your setup your read/write key. You
can also edit its properties if you want to change its privacy settings, alert trigger levels, etc.
Highly recommended spending a bit of time to play with these.
!
Lets create a new read/write key and assign it to the newly created Data Point.
1. Right click on your newly created Data Point and click on New Read/Write Key:
2. Type some random text in the Key field, and leave to the default settings as they are:
Peter Dalmaris
Lecture 40
!
Keep the Key handy because you will need to use it in your Arduino sketch.
You are done! If you double click on the new Data Point, you will get a pop-up window with
your Data Point chart, which for now is empty.
Lets proceed with the Arduino side now. The sketch we will use is a slightly edited sketch
written by StewieT.
Peter Dalmaris
Lecture 40
#include <SPI.h>
#include <Ethernet.h>
#include <PString.h>
// from http://arduiniana.org/libraries/pstring/
// allows 'printing' to a string buffer
// larger than required for this example, resize to suit
char buffer[400];
your application
char content[200];
// larger than required for this example, resize to suit
your application
PString str(buffer, sizeof(buffer));
PString cont(content, sizeof(content));
int ana_A ;
int ana_B ;
!
!
EthernetClient nimbitsServiceClient;
//***************************************
!
!
void setup() {
Serial.begin(9600);
// serial debug
Ethernet.begin(mac, ip, nameserver, gateway, subnet);
ethernet system
Serial.println("up and running....");
Serial.print("POST interval is ");
Serial.print(postingInterval/1000);
Serial.println(" seconds");
}
// initialise the
// =========================================================================
!
7
Peter Dalmaris
Lecture 40
// =========================================================================
// very simple main loop. Just constantly reads two ADC channels and then if its time to
log again, do_weblog() sends in the latest values
// of A8 and A9
void loop()
{
ana_A = analogRead(8);
// read some voltage and save it
ana_B = analogRead(9);
// read some voltage and save it
do_weblog();
}
// =========================================================================
/*
This is where it all happens.
If the posting interval is reached then a new POST is done with the latest data
If not time yet, we simply exit
*/
void do_weblog() {
// if you're not connected, and 'postinginterval' secs have passed since
// your last connection, then connect again and send data:
if(!nimbitsServiceClient.connected() && (millis() - lastConnectionTime >
postingInterval)) {
str.begin();
// reset the into-string pointer. This is the 'final' composite
string being assembled.
cont.begin();
// and for the actual content ie payload string
!
!
sendData();
delay(1);
nimbitsServiceClient.stop();
nimbitsServiceClient.flush();
}
}
!
!
Peter Dalmaris
Lecture 40
void sendData() {
// Create the 'content' string to send.
cont.print("email=");
cont.print(mailaddr);
cont.print("&key=");
cont.print(key);
// next get the data and store it in the 'cont' string
cont.print("&p1=Test+DP2&v1="); // test123 is the name of a data point you created
cont.print(ana_A,DEC);
// latest analog value. Replace this with whatever
// you are logging
// now get the length of the assembled content string.
// email addr, access key, sensor data (two analog vales in this case)
int contlen = (cont.length());
// now try and connect to the web-site
Serial.println("connecting...");
if (nimbitsServiceClient.connect("cloud.nimbits.com", 80))
{
// the format of the POST section below seems to be fairly critical.
str.print("POST /service/batch HTTP/1.1\r\n");
str.print("Host: cloud.nimbits.com\r\n");
str.print("Connection: close\r\n");
str.print("Cache-Control: max-age=0\r\n");
str.print("Content-Length: ");
str.print(contlen,DEC);
str.print("\r\n");
str.print("Content-Type: application/x-www-form-urlencoded\r\n");
str.print("\r\n"); // this empty line is REQUIRED
str.print(cont);
// the actual content string 'cont' (access details, data
points)
str.print("\r\n"); // and a terminating newline
// the total string (post headers and content) is now sent to the ethernet connection in
one hit
nimbitsServiceClient.print(str); // ethernet send to nimbits
Serial.println();
Serial.print(str);
Serial.println();
// for debug
Serial.println();
// for debug
}
else {
// if you couldn't make a connection:
Serial.println();
Serial.println("Connection failed");
Serial.println("disconnecting.");
Serial.println();
}
// note the time that the connection was made
lastConnectionTime = millis();
}
Peter Dalmaris
Lecture 40
Sketch discussion
This code looks long and convoluted, but its not as bad as it seems. Apart from the SPI
and Ethernet libraries needed for the actual communication, the only dependency is the
PString class which help a lot with the construction of strings. To read about the details, go
to the source at http://arduiniana.org/libraries/pstring/. By using the PString class, in this
sketch we can put together a string of characters that contain the complete HTTP request.
This request, which uses the POST method, contains the headers and the data that we
want to write to the Nimbits datapoint. Once the string has been assembled, the sketch
makes a single connection to the remote service (Nimbits) and completes the request.
The method of installing this class is the same as what we have done so far with third-party
Arduino-specific libraries. PString is a class that is implemented in C++. The Arduino IDE
can compile code written in C++ just like it can do with C code. To make the code that
implements this class available to your sketch, you need to copy the folder that contains
the classs files (PString.cpp and PString.h) in the folder that contains the Arduino thirdparty libraries. Then, rename the folder that contains the PString files to PString.
With PString, you can construct a string by appending partial strings into a buer. In this
sketch, we use two buers, one for the POST HTTP header, and one for the body of the
request which contains the data. Then these two parts are added together to create the full
content of the HTTP request. It looks like this:
Request buffer
(buffer)
Content buffer
(content)
Here is the part of the code that sets up the buers and the PString objects that operate on
them:
char buffer[400];
char content[200];
This code declares two arrays of chars, and initialises them with 400 and 200 cells
respectively. These sizes are large enough to contain the complete HTTP request (for the
buer array) and the data part (for the content array). Then, it points the str PString object
10
Peter Dalmaris
Lecture 40
to manipulate the buer buer, and the cont PString object to manipulate the content
buer.
postingInterval, also an unsigned long integer, which contains the frequency (in
milliseconds) by which we want the sketch to communicate with Nimbits. By default, we
set this variable to 120000, which means that data will be send to Nimbits every 120
seconds.
mailaddr, an array of char which contains the email address you used to create your
Nimbits account. This is the first element used in the authentication process.
key, also an array of char, which contains the access key. This is the second element
used in the authentication process
In the setup() function, all the code should be recognisable, so lets move to loop().
In loop(), we take to measurements from analog ports 8 and 9. Add whatever sampling
code you want in here, being digital or analog. Once you have the values you want to post,
call the do_weblog() function to attempt a post to Nimbits.
In do_weblog(), the sketch checks to determine that it is time to post a new request. It just
subtracts the time now (by calling millis()) from the time that the last post was made
(stored in the lastConnectionTime variable). If that is greater that postingInterval, then it is
time to post. This check happens with this statement:
If it is time to POST new data, the sketch resets the two PString objects by calling their
begin() function. Then, it calls the sendData() function where the bulk of the work is done.
In sendData(), the sketch first constructs the body of the HTTP request by calling cont.print.
Each call to the print function of the cont object appends any string passed to the content
buer. The first two key-value pairs added to the buer contain the account email address
and the data point authentication key. These are used as the credentials for authentication.
11
Peter Dalmaris
Lecture 40
Any subsequent key-value pairs will contain the data you want to post. The pattern is the
px and vx represent a point and a value, where x is an integer starting from 1 and
incrementing depending on how many data points you want to access.
You need two key-value pairs for each data point. In this example sketch, 2 data points are
being written to, so the content string looks like this (omitting the authentication part for
clarity):
p1=Test&v1=100&p2=Test2&v2=200
This means that the data point name for p1 is Test, and its value is 100. And the data point
name for p2 is Test2, and its value is 200.
Once all the key-value pairs are in the buer, the sketch moves on to the headers. It first
attempts to connect to Nimbits (since theres no point continuing if a connection is not
possible), and then constructs the header fields line by line by calling the print function of
the str PString object.
For the content-length field, the sketch needs to calculate the actual size in bytes of the
request body. This is done by calling the length() function of the cont PString object.
After the empty line of the header is printed (which identifies the end of the header), the
sketch prints the contents of the cont buer (which contains the body data). Add a
terminating line after that, the the string the makes up the HTTP request is complete.
To finally transmit it to Nimbits, the sketch calls the print function on the
nimbitsServiceClient EthernetClient object.
Now, you should be able to double click on the datapoint row in your Nimbits console and
see the value that was just posted plotted in the chart.
After a few data point have been recorded, you will have a plotted chart similar to this:
!
Exercise
Now that you know how to post sensor values to Nimbits, go ahead and make some use of
it. Add a DHT sensor to your circuit, extract humidity and temperature values from it and
post them to your Nimbits data points. Record values for a few days and see how they
fluctuate over time. Add a barometric pressure sensor and a photo-resistor and record all
four values for a week.
Nimbits has a facility that allows you to export your data for local processing. This facility
can be invoked by clicking on Schedule a data dump option available from the right-click
menu of each data point. Get the data and plot the all together in a spreadsheet. Can you
see a correlation between the four measurements? For example, can you confirm that
humidity in your area is higher at night than it is during the day?
12
Peter Dalmaris
Lecture 41
Lecture 42
Social logging to Twitter
In lecture 40, we played with logging sensor data to Nimbits, a cloud service dedicated to
the task. Nimbits will collect the data from your sensors and plot them in a chart, or you can
download them in a data dump and process them on your computer.
What if youd like to post your data for anyone to see on the Internet? Well, just like people
can post their holiday adventures on Facebook, gadgets can post their worldly
observations on Twitter!
In this lecture, Ill show you a very easy way to do just this.
Twitter is a private messaging service, owned and controlled by a corporation, Twitter Inc..
It is one of the most popular micro-blogging services in the world, if not the most popular.
To post a message to Twitter, you need an account and a Twitter client. Because Twitter has
an interest in protecting its infrastructure and users from un-authorised use, it requires that
any tweet is authorised. This makes the tweet legitimate.
Twitter, like many other web sites and services, uses an open-source authorisation protocol
called OAuth. This protocol allows a third-party application, like a tweeting Arduino board,
to gain access to Twitters application programming interface (API). So it is quite possible
for you to include code in your Arduino that implements OAuth as a client and use Twitters
API to tweet sensor data.
But there is a situation here to consider: If Twitter decides to make changes to the way it
authorises tweets, you will have to update the code on the Arduino. If the Arduino is on your
desk, that is no big deal, but if you have deployed an Arduino-based product and can no
longer access it, then you have a problem. Although changes like this are very infrequent,
they are possible, and because you have no control at all over Twitters API changes, there
is a small, but existing, possibility that this will be a problem one day.
The alternative is to use an authorisation proxy service. This is also my favourite solution to
this problem. Your Arduino will send a tweet to the proxy, with authorisation credentials that
the proxy supports, and from there the proxy will relay the message to Twitter. As long as
you have control over the proxy, you will be safe from changes to Twitters API because any
changes you will have to make are in the single proxy only.
In the demo for this Lecture, I will show you how to setup an open source proxy called
Arduino-tweet, authorise this proxy for posting tweets to Twitter, and getting the Arduino to
send tweets to the proxy. This architecture is depicted graphically like this:
Peter Dalmaris
Lecture 41
Arduinotweet
proxy
!
!
!
!
!
The Arduino-tweet proxy
The proxy component is an open-source piece of software that you can download from
Github (https://github.com/NeoCat/ArduinoTweetLib-server) and install on your own server.
But for convenience, you can just use a public, free and managed instance running at
http://arduino-tweet.appspot.com. You dont even need to create an account, just to get an
authentication token. This is what well do next.
I assume that you already have a Twitter account, or that you have created one specifically
for your Arduino to use. Thats probably best, because at least in the beginning your
Arduino will be posting a lot of annoying test tweets which you do not want in your ocial
timeline.
Go to arduino-tweet.appspot.com. The home page shows the three things that need to be
done:
Peter Dalmaris
Lecture 41
Click on the link for step one. This is direct you to Twitter, where you will need to authorise
the proxy to post on Twitter on your (or your Arduinos) behalf. This page also contains a list
of things that the proxy will be able to do with your Twitter account.
Click on the Authorize app button. This will register the proxy with authority to post
tweets, and generate a token that your Arduino will use to authenticate itself to the proxy.
Copy it and store it somewhere safe, since you will need to use it in your Arduino sketch:
Keep this secret, because any client with this token will be able to post messages to Twitter
via the proxy.
Go back to arduino-tweet.appspot.com and click on the step 2 link. You will now see
instructions on how to download and import the required library to your Arduino IDE:
Peter Dalmaris
Lecture 41
Click on the link in the first bullet point. Once the download is complete, extract the
contents of the ZIP file, and copy the folder to your Arduino IDE libraries folder. Rename the
folder that contains the Twitter library to Twitter. You should have something like this in
your Arduino IDE libraries folder:
Restart your IDE, and make sure that the new library shows up. You know that the library
has been imported successfully if you can see the example sketches (File > Examples
> Twitter):
!
4
Peter Dalmaris
Lecture 41
Finally, the next step is to try out the example sketch. Click on the link in Step 3, and the
sketch will show up.
To try this out, change the IP address to one that matches your network (blue arrow), and
copy/paste the proxy token in the twitter constructor (red arrow). You can get this sketch by
clicking on File > Examples > Twitter > Sample Post. Here it is for completeness:
Peter Dalmaris
Lecture 41
#include <SPI.h>!
#include <Ethernet.h>!
#include <Twitter.h>!
void setup()!
{!
delay(1000);!
Ethernet.begin(mac, ip);!
Serial.begin(9600);!
Serial.println("connecting ...");!
if (twitter.post(msg)) {!
int status = twitter.wait();!
if (status == 200) {!
Serial.println("OK.");!
} else {!
Serial.print("failed : code ");!
Serial.println(status);!
}!
} else {!
Serial.println("connection failed.");!
}!
}!
void loop()!
{!
}
If this goes well, your Arduino will introduce itself to the world with a greeting: Hello, World!
Im Arduino!!
Peter Dalmaris
Lecture 41
Custom tweets
Now that we know that the Arduino can tweet via the proxy, lets customise the message
that is posted. Lets make it so that it reports a value from one of the analog inputs. I would
like the Arduino to tweet regularly, say once every hour, so I must move the code
responsible for tweeting from the setup() function to the loop() function. Have a look at
this code:
#include
#include
#include
#include
<SPI.h>!
<Ethernet.h>!
<Twitter.h>!
<PString.h>
void setup()!
{!
Ethernet.begin(mac, ip);!
Serial.begin(9600);!
}!
void loop()!
{!
if((millis() - lastConnectionTime > postingInterval)) {!
tweet_pstring.begin();!
int ana_A = analogRead(8);!
Serial.println("connecting ...");!
tweet_pstring.print("Light intensity:");!
tweet_pstring.print(ana_A);!
if (twitter.post(tweet_pstring)) {!
int status = twitter.wait(&Serial);!
if (status == 200) {!
Serial.println("OK.");!
} else {!
Serial.print("failed : code ");!
Serial.println(status);!
}!
} else {!
Serial.println("connection failed.");!
}!
lastConnectionTime = millis();!
}!
}
Peter Dalmaris
Lecture 41
The Ethernet part of the sketch is familiar. Just like in Lecture 40, I am using the PString
library to help me assemble text strings.
After adjusting the IP address, and setting your proxy token, the sketch defines an array of
chars with 200 character cells in it. Twitter only allows 140 characters, so 200 is a bit of an
overkill. Finally, it declares and initialises the tweet_pstring object that will be used to
construct the tweet post string.
Inside the loop() function, we check that one hour has passed since the last tweet using
almost the same code we used in Lecture 40. You want to keep a low posting frequency
because both the proxy and Twitter have limits on how many posts they can process per
minute. If it is time to tweet, we take a measurement from analog pin 8 and store it in a local
integer variable. Then we construct the tweet using the tweet_pstring object
(tweet_pstring.print).
twitter.post(tweet_pstring)!
!This attempts to post the string to which tweet_pstring is pointing to. This command will
return a boolean, and if that boolean is true, then the sketch will go inside the if block and
check for a response from Twitter. This happens with this command:
!The wait function of the twitter object can receive one parameter, which in this case is a
reference to the Serial port, the one that outputs text to the IDE monitor. This way, we can
see the actual text that Twitter sends back to the Arduino. If you dont need to see this text,
you can just call twitter.wait();
The wait() function eventually returns a status code. If that is 200, that we know that the
post was successful.
Before finishing the loop, the sketch gets the correct time in milliseconds since the Arduino
was booted with the millis() function and stores it in a variable so that it can later check if
it is time to post again.
Exercise
You can see that with a proxy managing the communication with Twitter, posting tweets
with the Arduino could not be any simpler. With the code for both the Arduino library and
the proxy being open source, you have peace of mind that your tweeting infrastructure can
be built on your own hardware and be in your total control if you ever need to.
As an exercise, make the necessary changes to the custom sketch so that your Arduino
tweets data from multiple sensors. Please message me at @futurshocked with your
Arduinos Twitter handle so I can check out your tweets!
Peter Dalmaris
Lecture 45
SD card storage
Local file storage
The Arduino has a tiny amount of memory in which a sketch can store data, or where the
sketch itself is being kept.
The ATmega micro-controllers in the Arduino contain 3 types of memory: SRAM, flash, and
EEPROM.
Static RAM (static random-access memory) is volatile, and is where your sketches store
values that belong to variables from things like sensor readings. Volatile memory is cheap
and fast, but as soon as power is lost, it is erased. Therefore, it is only used as a temporary
place to store data.
In an expression like int ledPin = 13, you are storing the number 13 in static RAM. In
the Arduino Uno where the ATmega 328 is used, there are only two thousand bytes
available in the static RAM. It sounds like its a lot, but it isnt. Your computer probably has a
million times more RAM available to the programs that run on it.
Flash memory is non-volatile, so when power is turned o, its contents remain safe. Flash is
where your sketches and the Arduino bootloader (a special program that helps the Arduino
start executing the sketch when power is applied, or with uploading a new sketch) are
stored. The ATmega 328 has 32k bytes of this memory. You dont normally use this memory
to store data, but you can if you want to (this is a topic for another lecture, but for the very
curious of you, have a look at the PROGMEM keyword).
Finally, EEPROM (Electrically Erasable Programmable Read-Only Memory), like flash, is also
non-volatile. It is a good place to store data permanently that should not be erased in case
a new sketch is uploaded. Things like serial numbers, ids and the like can be stored here.
The ATmega 328 has only 1k byte of EEPROM available, so it is definitely not mass storage.
On the Arduino, on-board storage is very limited. For applications like data logging, or
running a web server with multiple pages and images, the build-in memory is not enough,
so we turn to SD cards for help.
SD cards
SD cards have matured over the years. Their sizes have expanded
to many giga-bytes, and their prices have dropped to a few cents
per gigabyte. Compared to build-in memory, SD cards oer a really
good mass storage solution. On the Arduino, an SD card can be
used with the appropriate hardware extension and with an easy to
use library that comes with the IDE. Your Arduino Ethernet shield
comes with a micro-SD card slot, but you can also get them as a
separate breakout board.
Peter Dalmaris
Lecture 45
The SPI (Serial Peripheral Interface) bus is a way of connecting multiple slave devices to a
master. Unlike I2C, which we played with back in lecture 9 where we learned about the
BMP08 barometric sensor, the SPI bus provides full duplex (two-way) communication
between master and slave using two wires. The I2C interface supports half-duplex, so that
data can flow only one way at a time. There is also a wire that provides a clock signal to the
slave so that both devices are synchronised, and a fourth wire that is used for selecting one
of the many slave devices that may be available.
In summary, this table shows the pins used by the SPI bus, which we will be using to
connect the breakout board to the Arduino:
Pin
Stands for
Purpose
CS or SS
MOSI
SCLK
Shared/Serial Clock
MISO
The SPI bus oers itself as a topic for a full lecture, so I will not go into more details now.
For now, simply keep in mind the functions of each of the SPI bus pins.
Peter Dalmaris
Lecture 45
3. We will upload a sketch that prints out information about the card and confirm that it
works.
Connection
Prepare an SD card
The Arduino SD card library works with SD cards that contains FAT16 or FAT32 partitions.
Most SD cards will work out of the box. However, if you are having any issues that prevent
you from using it with the Arduino, it is a good idea to format it. On a Mac, you can do this
by using the Disk Utility, and on Windows by right-clicking on the SD Card icon and clicking
on the Format option.
Once your SD card is formatted and ready, insert it into the SD card slot and plug the
Arduino to your computer. Lets have a look at the first sketch.
Peter Dalmaris
Lecture 45
Sketch
Lets try something simple first: Get information about our SD card.
The sketch well look at makes use of un-documented classes in the SD library. Typically a
vendor does not provide documentation for certain features because they either had no
time to write it up, or because they dont want users to know about them. In this case, this
library has been out for some time now, so it is more likely that Arduino (the company) does
not want you to use certain features as they are probably subject to change. But since they
are used in some of the examples that come with the IDE, we can try them out and see
what they do!
Open the CardInfo sketch by clicking on File > Examples > SD > CardInfo. Heres
what you get (slightly edited from the original):
#include <SD.h>
Sd2Card card;
SdVolume volume;
SdFile root;
const int chipSelect = 10;
void setup()
{
Serial.begin(9600);
!
!
!
Serial.print("\nInitializing SD card...");
pinMode(10, OUTPUT);
// change this to 53 on a mega
if (!card.init(SPI_HALF_SPEED, chipSelect)) {
Serial.println("initialization failed. Things to check:");
Serial.println("* is a card is inserted?");
Serial.println("* Is your wiring correct?");
Serial.println("* did you change the chipSelect pin to match your shield or
module?");
return;
} else {
Serial.println("Wiring is correct and a card is present.");
}
Peter Dalmaris
Lecture 45
!
continues from previous page
volumesize = volume.blocksPerCluster();
volumesize *= volume.clusterCount();
volumesize *= 512;
Serial.print("Volume size (bytes): ");
Serial.println(volumesize);
Serial.print("Volume size (Kbytes): ");
volumesize /= 1024;
Serial.println(volumesize);
Serial.print("Volume size (Mbytes): ");
volumesize /= 1024;
Serial.println(volumesize);
Serial.println("\nFiles found on the card (name, date and size in bytes): ");
root.openRoot(volume);
// list all files in the card with date and size
root.ls(LS_R | LS_DATE | LS_SIZE);
}
void loop(void) {
}
!
!
!
!
!
5
Peter Dalmaris
Lecture 45
When you run this sketch on your Arduino, you will see something like this, assuming that
your SD card is properly formatted:
Peter Dalmaris
Lecture 45
Demo 2
Peter Dalmaris
Lecture 45
You can also write bytes or arrays of bytes by using the write(data) or write(buer, length)
function. This way of writing to the card may have a better performance and it is something
worth while remembering if you are building a higher-speed data logger.
Peter Dalmaris
Lecture 45
!
Remove the SD card from the Arduino and insert it in your
computers card reader.
Peter Dalmaris
Lecture 45
Demo 3
In this last demo, well look at how we can browse directories and files stored on your SD
card. This is useful in order to create and maintain a hierarchical file system in which files
are stored. Even though it is unusual for an Arduino sketch to be managing too many files,
basic file management can be setup by creating folders and putting files in them, instead of
placing everything in the root (which is still a folder).
Looking at the SD class documentation, you see functions like mkdir() and rmdir() which
create or remove a directory, and exists() which checks for the existence of a file or
directory.
This example sketch comes with the IDE and browses the file system on the SD card and
prints out the directories and their contents.
The sketch in this Demo contains a recursive structure, which simply means a function
that calls itself. It sounds fancy, but it is merely a convenient way to code operations that
repeat themselves.
In the sketch that follows, I have provided annotations for only those parts that we havent
already seen in Demos 1 and 2.
!
!
!
!
!
!
!
!
!
!
!
!
!
10
Peter Dalmaris
#include <SD.h>
File root;
Lecture 45
void loop()
{ }
!
!
11
Peter Dalmaris
Lecture 45
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
Directory
Directory
Size
(bytes)
An exercise
You now know how to write text data to your SD card!
But what about reading? I did not show you how to read text from the card because Id like you to
work this function out your self. Have a look at the documentation page for the read function, and a
sample sketch that shows you how to read text from a text file.
12
Peter Dalmaris
Lecture 47
It is true that the Arduino has a build-in sense for time. It knows how much time has passed
since it started executing a program. You can use the millis() function to get a number that
represents this time in milliseconds. A similar function is micros(), and it does the same
thing as millis() except that it reports the elapsed time in microseconds instead of
milliseconds. There are also the time-related functions delay() and delaymicroseconds()
which add a delay in your program, the fist in milliseconds and the second in
microseconds.
Still, your sketch cannot ask the Arduino for the time. You could use an Internet time server
and poll it occasionally for the time, but this method requires Internet connectivity, a
dependency that maybe an overkill for some projects.
A real-time clock break out board is a PCB that contains a time-keeping integrated circuit.
Its like the watch you wear on your wrist. You glance at it to get the time. The Arduino will
be able to ask the real-time clock for the time too.
A simple application for a real-time clock is for time-stamping log entries recorded on an
SD card. We will look at this problem in this lecture. You can also consider applications
where certain events must be scheduled, like turning an illuminated sign on and o, or
taking sensor readings at predetermined times.
Peter Dalmaris
Lecture 47
EEPROM Clock
!
2
Peter Dalmaris
Lecture 47
Download it, copy the library folder to the Arduino libraries folder, and restart the IDE. Then,
load the sketch from File > RTClib > ds1307.
#include <Wire.h>
#include "RTClib.h"
Import the Wire library so that we can use the IC2 bus.
RTC_DS1307 rtc;
void setup () {
Initialise the IC2 bus.
Serial.begin(9600);
Wire.begin();
Initialise the real time clock device.
rtc.begin();
if (! rtc.isrunning()) {
This will get the time from your
Serial.println("RTC is NOT running!");
computer during compilation
and automatically set the time.
rtc.adjust(DateTime(2014,01,16,14,45,00));
// following line sets the RTC to the date & time this sketch was compiled
//RTC.adjust(DateTime(__DATE__, __TIME__));
} This will adjust the time with the
help of the C-language DateTime
Check to determine is the device is on. Since the
}
object initialiser.
void loop () {
DateTime now = rtc.now();
Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Get the time now, and store it in the now variable,
Serial.print('/');
which is of time DateTime.
Serial.print(now.day(), DEC);
Serial.print(' ');
Print time attributes to the Serial port.
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
Serial.print(" since midnight 1/1/1970 = ");
Get the number of seconds elapsed since 1/1/1970,
Serial.print(now.unixtime());
when Unix time begins.
Serial.print("s = ");
Serial.print(now.unixtime() / 86400L);
and convert that to days, since 1 day
Serial.println("d");
contains 86400 seconds.
Peter Dalmaris
Lecture 47
!
!
Peter Dalmaris
Lecture 47
In the next page, I provide the merged sketch, and comments to the interesting segments.
Peter Dalmaris
Lecture 47
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 rtc;
const int chipSelect = 10;
rtc.adjust(DateTime(__DATE__, __TIME__));
Serial.print("Initializing SD card...");
pinMode(10, OUTPUT);
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
return;
}
Serial.println("card initialized.");
}
void loop(){
Get the sensor readings.
DateTime now = rtc.now();
String dataString = "";
for (int analogPin = 0; analogPin < 2; analogPin++) {
int sensor = analogRead(analogPin);
dataString += String(sensor);
if (analogPin < 1) {
dataString += ",";
}
}
File dataFile = SD.open("datalog.txt", FILE_WRITE);
if (dataFile) {
Peter Dalmaris
Lecture 47
An exercise
With the right hardware, keeping time is easy. Try out this exercise:
Create an LCD clock. Use a real time clock like the one in this lecture, combine it with an LCD
screen, and display the date and time on it.
!
!
For the adventurous, heres another exercise: Extend the LCD clock so that it has these features:
1.
2.
3.
4.
A toggle switch
A buzzer
Only keeps track of time (dont worry about the date for now)
The toggle switch will set the mode of the clock to Time Set or Alarm set. When the toggle switch is
on, then turning the potentiometer will change the time. When the switch is o, then turning the
potentiometer will change the alarm time.
Assuming the clock keeps time in 24 hour format, make your gadget so that when the alarm time is
reached, a sound will be generated by the buzzer for 10 seconds.
Peter Dalmaris
Lecture 50
Lecture 50
Wifi
In Lecture 33 and 34, you learned about the Arduino Ethernet shield, and connected your
Arduino to the Internet. In this lecture, well again connect the Arduino to the Internet, but
well do that using Wifi, and go completely wireless!
There are a lot of shields and breakout boards that provide Wifi functionality, with varying
prices. Shields that provide 802.11n capability can sell for over $100 with bells and whistles
like external antennas and on-board SD card modules.
I personally go for breakout boards whenever possible, because usually they oer a lower
price point, a modular and smaller package, and a single function per board which makes it
easier to learn and integrate into to my projects.
In the last one I will show you an adapted version of Demo 2 from lecture 38, where we had
a web server running on the Arduino, showing us a simple user interface through which we
could turn an LED on and o.
Peter Dalmaris
Lecture 50
!
Adafruit maintains a Github repository with the latest
version of the library and examples. Go ahead and get it,
then copy the library into your Arduino IDEs libraries
folder as we have done before. Remember to restart the
IDE once the new library has been installed.
The CC3000 Wifi module, made by Texas Instruments, uses the SPI communications
interface to talk to the Arduino. On the PCB, notice the familiar pin markings CLK, MISO,
MOSI, and CS. We saw the exact same pins on the SD card module in lecture 45. Theres
two additional pins on the CC3000: IRQ and VBAT_EN (the actual marking is VBEN on the
PCB).
In the SD card module, the Arduino is responsible for initiating communication. The Arduino
will ask for a read or write, and the SD card module will execute it. The SD card module
never initiates communication. With the wifi module, however, it is just as likely for the
module to initiate communication as it is for the Arduino. The IRQ pin (for Interrupt
ReQuest) is used by the Wifi module to grab the attention of the Arduino when it has data
to sent. The Arduino Uno has a special pin, digital pin 3, which can detect an interrupt
request. When an interrupt is requested, the Arduino will stop whatever it is doing at that
moment and start executing a special function (part of the CC3000 library) that will deal
Peter Dalmaris
Lecture 50
with the interrupt. When the function is done dealing with the interrupt, the Arduino
continues doing whatever it was doing before the interrupt.
The VBAT_EN is used to start the module properly. Think of it as a reset switch.
Arduino
CC3000
13 SCK
12 MISO
11 MOSI
10 CS
5 VBEN
3 IRQ
GND GND
5V VIN
!
!
!
!
!
!
!
Double-check the connections, and the plug the Arduino to your computer via the USB
port. A surface-mounted green LED will light up on the wifi board, thats means youre good
to go!
Peter Dalmaris
Lecture 50
Sketch
Lets look at the sketch now. The one well use for this demo is one of the many samples
that come with the CC3000 library. Fire up the IDE and load the example: File > Examples
> Adafruit_CC3000_Library > buildtest
Here is the sketch (edited to make it fit), I am highlighting the interesting parts:
#include
#include
#include
#include
#include
<Adafruit_CC3000.h>
<ccspi.h>
<SPI.h>
<string.h>
"utility/debug.h"
Import the Adafruit library so that we can use the Wifi module.
Contains SPI helper functions for the CC3000.
Import that standard SPI and String libraries.
Implements the useful foreach() and sign() functions, used in this
sketch.
#define ADAFRUIT_CC3000_IRQ
3
#define ADAFRUIT_CC3000_VBAT 5
Set pins for IRQ (fixed) and VBAT, CS (can change).
#define ADAFRUIT_CC3000_CS
10
// Use hardware SPI. On an UNO, SCK = 13, MISO = 12, and MOSI = 11
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS,
ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT, SPI_CLOCK_DIVIDER);
Peter Dalmaris
#ifndef CC3000_TINY_DRIVER
listSSIDResults();
#endif
Lecture 50
You can load a small-footprint driver for the Wifi module. The tiny
driver will only load the essential API functions and save a lot of
RAM. See http://processors.wiki.ti.com/index.php/
Tiny_Driver_Support. Enable tiny mode by adding this definition in
your code: #define CC3000_TINY_DRIVER.
Peter Dalmaris
Lecture 50
void displayDriverMode(void){
#ifdef CC3000_TINY_DRIVER
Serial.println(F("CC3000 is configure in 'Tiny' mode"));
#else
Print out various driver parameters. The F()
function enforces storage of these static
Serial.print(F("RX Buffer : "));
(unchanging) Strings in flash memory instead
Serial.print(CC3000_RX_BUFFER_SIZE);
of SRAM, therefore preserving SRAM for the
Serial.println(F(" bytes"));
dynamic segments of the sketch. Reminder:
Serial.print(F("TX Buffer : "));
flash memory is where the sketch is actually
stored when you upload it.
Serial.print(CC3000_TX_BUFFER_SIZE);
Serial.println(F(" bytes"));
#endif
End of the pre-processor conditional group.
}
Retrieve the firmware version running in the
uint16_t checkFirmwareVersion(void)
device, and store the major and minor versions
{
in the memory locations provided as
parameters.
uint8_t major, minor;
uint16_t version;
#ifndef CC3000_TINY_DRIVER
if(!cc3000.getFirmwareVersion(&major, &minor)) {
Serial.println(F("Unable to retrieve the firmware version!\r\n"));
version = 0;
}
else
{
Serial.print(F("Firmware V. : "));
Serial.print(major); Serial.print(F(".")); Serial.println(minor);
version = major; version <<= 8; version |= minor;
}
#endif
return version;
}
!
!
!
!
6
Peter Dalmaris
Lecture 50
void displayMACAddress(void){
uint8_t macAddress[6];
if(!cc3000.getMacAddress(macAddress))
{
Serial.println(F("Unable to retrieve MAC Address!\r\n"));
}
Get the devices MAC address. It contains 6
else
bytes, and those bytes are stored in the
{
macAddress array of bytes.
Serial.print(F("MAC Address : "));
cc3000.printHex((byte*)&macAddress, 6);
Print the MAC address in hexadecimal
notation, as is common.
}
}
bool displayConnectionDetails(void){
uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;
if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
{
Serial.println(F("Unable to retrieve the IP Address!\r\n"));
return false;
Get the Wifi connection parameters. The
parameters will be stored in the memory
}
locations passed as parameters to the
else
function.
{
Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
Serial.println();
return true;
}
}
Print out the Wifi connection parameters.
!
!
!
!
!
!
!
7
Peter Dalmaris
Lecture 50
void listSSIDResults(void)
Search for Wifi networks. The 8-byte integer
{
index will contain the total number of networks
found.
uint8_t valid, rssi, sec, index;
char ssidname[33];
index = cc3000.startSSIDscan();
Serial.print(F("Networks found: ")); Serial.println(index);
Serial.println(F("================================================"));
while (index) {
index--;
valid = cc3000.getNextSSID(&rssi, &sec, ssidname);
Serial.print(F("SSID Name
: ")); Serial.print(ssidname);
Serial.println();
Serial.print(F("RSSI
: "));
Serial.println(rssi);
Serial.print(F("Security Mode: "));
Print out the parameters for the networks
Serial.println(sec);
found, starting with the last one.
Serial.println();
}
Serial.println(F(================================================"));
cc3000.stopSSIDscan();
Stop scanning.
}
!
Here is an example
output of this sketch:
Peter Dalmaris
Lecture 50
Great, the Wifi module works, so lets go ahead and do something useful with it.
Automation and remote control fits well in that category. You may remember back in
Lecture 38 that you learned how to control an LED by setting up a simple web server on the
Arduino. You used your web browser to access this web server and click on buttons to turn
the LED on and o. We will adapt that sketch to use the Wifi module instead of the Ethernet
shield in Demo 3, but in this demo well try something dierent: polling-based control.
In summary, we will use instructions contained in a text file, hosted by a web server
somewhere on the Internet. The Arduino will use the Wifi module to get this file, read its
contents, and turn an LED on and o accordingly. Schematically, this is what is going to
happen:
!
!
!
!
!
!
There is a blank
line after the
instruction line.
!
!
!
Peter Dalmaris
Lecture 50
Theres an LED connected to digital Pin 8 via a protective 1k resistor. The Arduino will use
the Wifi module to request a copy of the file titled cc3000.txt from the web server that is
hosting it. In my demo, I am using an Amazon S3 bucket, but you can use any service at all
as long as you can access it with a HTTP URL like http://myserver.com/cc3000.txt. You
may remember that the instruction contained in the first line of this file is identical to the one
that we learned about how to parse back in lecture 38. We are not learning again how to
parse this instruction, we just store it on the web instead of on the Arduino!
The nice thing about this architecture is that is is scalable. Once you stop thinking of
cc3000.txt as a text file but instead as a web resource, then you have many options to
manipulate it. You can get the Arduino to poll a URL that is controlled by a web application
which creates the LED instruction based on some user entry, a schedule, environmental
conditions somewhere else etc.
I also mentioned in the introduction of this lecture that polling, as opposed to a web server
running on the Arduino, has the additional benefit of not having to worry about local
network restrictions, especially firewalls and NAT. You Arduino will be able to get its
instructions by accessing a public URL, and you will be able to control your Arduino by
manipulating the resource at that URL.
#include <Adafruit_CC3000.h>
#include <ccspi.h>
#include <SPI.h>
No change compared to Demo 1, except
#include <string.h>
#include "utility/debug.h"
Import the Watchdog library. See further down
#include <avr/wdt.h>
for details.
#define ADAFRUIT_CC3000_IRQ
3
#define ADAFRUIT_CC3000_VBAT 5
#define ADAFRUIT_CC3000_CS
10
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ,
ADAFRUIT_CC3000_VBAT, SPI_CLOCK_DIVIDER);
Set the host and path to the file that contains
boolean
reading
= false;
the LED instruction. Also set a timeout
String
get_request = "";
constant. We expect the server to respond
within 3 seconds.
#define WLAN_SSID
"mynetworkid"
#define WLAN_PASS
"mynetworkpassword"
#define WLAN_SECURITY
WLAN_SEC_WPA2
#define IDLE_TIMEOUT_MS 3000
#define WEBSITE
"arduinosbs.com.s3.amazonaws.com"
#define WEBPAGE
"/cc3000.txt"
uint32_t ip; uint32_t
t; int port = 80;
No change compared to Demo 1
int connectTimeout = 5000;
Adafruit_CC3000_Client www;
Counter keeps track of how many times the
int repeat_counter = 0;
file has been polled.
10
Peter Dalmaris
Lecture 50
void setup(void){
Serial.begin(115200);
Serial.println(F("Hello, CC3000!\n"));
Serial.print(F("Free RAM: ")); Serial.println(getFreeRam(), DEC);
Serial.println(F("\nInitializing..."));
if (!cc3000.begin()) {
Serial.println(F("Couldn't begin()! Check your wiring?"));
while(1); }
Broken down the functionality from the setup
connect_wifi();
function in Demo 1 to individual functions in
get_dhcp();
order to improve readability.
lookup_ip();
}
void loop(void){
repeat_counter++;
Serial.print(F("Free RAM: ")); Serial.println(getFreeRam(), DEC);
Serial.print(F("Repeat counter: ")); Serial.println(repeat_counter);
Serial.print(F("starting connection to "));
Serial.println(ip);
Enable watch dog timer (WDT). See detailed
wdt_enable(WDTO_8S);
discussion at the end of this sketch.
connect_tcp();
Call the connect_tcp() function. This function
wdt_disable();
will attempt to connect to the remote web
server.
Serial.println(F("Connecting"));
if (www.connected()) {
Disable watch dog timer (WDT). It is not
Serial.println(F("Connected"));
likely that anything else may cause the sketch
to freeze.
make_get_request();
Serial.println(F("Request sent"));
Connection to the remote web server was
} else {
successful.
Serial.println(F("Connection failed"));
return; }
Call the make_get_request function which will
construct and apply the HTTP GET request.
Serial.println(F("-------------------------------------"));
unsigned long lastRead = millis();
while (www.connected() && (millis() - lastRead < IDLE_TIMEOUT_MS)) {
boolean currentLineIsBlank = true;
get_request
= "";
char c = www.read();
A response from the server is expected within
Serial.print(c);
3 seconds. The IDLE_TIMEOUT_MS constant
if(reading && c == '\n')
was set at the start of the sketch.
{ reading = false;
parseGetRequest(get_request);
break;
The server has started sending its response,
lets start parsing it.
}
Grab a character
from the response.
If we are currently reading, and this char is a new line, then reading is
finished, so lets parse the instruction. Nothing else to do here.
11
Peter Dalmaris
if(reading){
get_request += c; }
Lecture 50
}
server.
www.close();
Serial.println(F("-------------------------------------"));
delay(10000); }
void parseGetRequest(String &str) {
Serial.print(F("Parsing this string:"));
Serial.println(str);
int
led_index = str.indexOf("led");
int
led_pin
= str[led_index + 3] - '0';
int
led_val
= str[led_index + 5] - '0';
executeInstruction(led_pin, led_val); }
void executeInstruction(int pin, int val){
Serial.println(F("Executing instruction"));
pinMode(pin, OUTPUT);
digitalWrite(pin, val);
Serial.println(F(Done!"));}
bool displayConnectionDetails(void){
uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;
if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv)){
Serial.println(F("Unable to retrieve the IP Address!\r\n"));
return false; } else {
Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
Serial.println();
return true;
}
}
12
Peter Dalmaris
Lecture 50
void make_get_request(){
Get request is assembled by printing to the
www.fastrprint(F("GET "));
remote server represented by the www object.
www.fastrprint(WEBPAGE);
www.fastrprint(F(" HTTP/1.1\r\n"));
www.fastrprint(F("Host: ")); www.fastrprint(WEBSITE); www.fastrprint(F("\r
\n"));
www.fastrprint(F("\r\n"));
www.println(); }
void connect_tcp(){
t = millis();
do {
www = cc3000.connectTCP(ip, port); }
while((!www.connected()) && ((millis() }
t) < connectTimeout));
void connect_wifi(){
if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
Serial.println(F("Failed!"));
while(1); }
Serial.println(F(Connected!"));
}
void get_dhcp(){
Serial.println(F("Request DHCP"));
while (!cc3000.checkDHCP())
{
delay(100); }
while (! displayConnectionDetails()) {
delay(1000);
}
}
void lookup_ip(){
ip = 0;
Serial.print(WEBSITE); Serial.print(F(" -> "));
while (ip == 0) {
if (! cc3000.getHostByName(WEBSITE, &ip)) {
Serial.println(F("Couldn't resolve!"));
}
delay(500);
}
cc3000.printIPdotsRev(ip);
}
13
Peter Dalmaris
Lecture 50
The Arduino, just like a normal computer, occasionally may hung or, in other words,
become unresponsive. This can be a result of hardware issues, like faulty sensors or
communications devices, or bugs in a sketch. As I was playing around with the CC3000
Wifi module while preparing this demo, I noticed that the Arduino would hung after a few
(around 20) loops. Each loop represents a poll of the control text file from the web server. I
discovered that the most likely cause of this behaviour was memory fragmentation. Memory
fragmentation occurs when data in RAM is erased and saved in a way that over some
period of time even though there is a lot of memory available, it is only available in small
chunks; this makes it almost usefulness unless you only want to store a byte or so at a
time. Have a look at this screenshot from the Microsoft Windows Disk Defragmenter utility:
http://i.stack.imgur.com/00nX7.jpg
The hard disk that is visualised in this map contains free space (shown in white), but that
space is not contiguous, so you can only store small files in it. A normal computer has the
resources to defrag, that is to re-arange the contents of the memory (or hard disk) in order
to create larger and more useful contiguous segments of free memory.
Working on this demo, I realised that the connectTCP function would, over time, fragment
the SRAM until the free space available could not be used, and then the ATMega would
freeze.
On the Arduino, there is no practical way to achieve defragmentation other than reseting the
micro-controller. To make this work, I used a feature build-in to ATMegas called Advanced
Watchdog Timer (http://www.atmel.com/Images/doc2551.pdf). This feature makes it
possible to set a timer so that if by the time the timer reaches a set limit the watchdog has
14
Peter Dalmaris
Lecture 50
not detected any activity ((heart beat) it assumes that the micro-controller has become
unresponsive. The watchdog will then reset the micro-controller, and the sketch will start
executing with a clear slate - and a de-fragmented memory.
1. I identified the segment of the sketch that seems to be causing fragmentation and/or is
aected by it. I used lots of Serial.print statements to figure out where the sketch
hangs. In the Demo 2 sketch, this was the call to the connect_tcp() function.
2. Just before the oending call, add the call to wdt_enable(WDTO_8S). This enables the
WatchDog Timer and sets a timer for 8 seconds. This time I think was enough for
nothing to happen. In other words if 8 seconds pass with no activity, reset the device.
3. After the oending call, add the call to wdt_disable(). This disables the WatchDog Timer.
If anything outside this block causes my sketch to hung, I want to know about, so I
dont want the WDT
to reset the device
and mask such
behaviour.
Output
Execute this sketch, and
you will get something like
this:
Peter Dalmaris
Lecture 50
For this last demo, we will modify the sketch from Lecture 38 Demo 2 so that instead of
working with an Ethernet shield, it will work with the CC3000 Wifi breakout.
We will use the exact same circuit as in Demo 2. Lets go straight to the sketch and see
whats dierent
Sketch
This sketch is a hybrid between the sketch in Demo 2 and that of Lecture 38 Demo 2.
Comments are embedded.
#include <Adafruit_CC3000.h>
#include <ccspi.h>
#include <SPI.h>
#include <string.h>
#include "utility/debug.h"
#include <stdlib.h>
#define ADAFRUIT_CC3000_IRQ
3
#define ADAFRUIT_CC3000_VBAT 5
#define ADAFRUIT_CC3000_CS
10
define WLAN_SSID
"yournetworkssid"
define WLAN_PASS
yournetworkpassword"
#define WLAN_SECURITY
WLAN_SEC_WPA2
#define LISTEN_PORT
80
16
Peter Dalmaris
Lecture 50
void setup() {
Serial.begin(115200);
Start the device.
Serial.println(F("\nInitializing..."));
if (!cc3000.begin()) {
Serial.println(F("Couldn't begin()! Check your wiring?"));
while(1); }
if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
Serial.println(F("Failed!"));
while(1); }
Connect to the Wifi access point.
Serial.println(F("Connected!"));
Serial.println(F("Request DHCP"));
Get network parameters from the
while (!cc3000.checkDHCP()) {
DHCP server.
delay(100); // ToDo: Insert a DHCP timeout!
}
Show network parameters leased
while (! displayConnectionDetails()) {
from the DHCP server.
delay(1000); }
ledServer.begin();
Serial.println(F("Listening for connections..."));
}
Start the web server
void loop() {
Adafruit_CC3000_ClientRef client = ledServer.available();
String return_message;
if (client) {
The contents of the loop() function are
boolean currentLineIsBlank = true;
virtually identical to that from Lecture
38, Demo 2, with the exception of the
get_request
= "";
client object. Here we use the
boolean sentContent
= false;
Adafruit_CC_3000_ClientRef class
while (client.available()) {
instead of EthernetClient.
char c = client.read();
if(reading && c == ' ')
{ reading = false;
return_message = parseGetRequest(get_request);
break;
}
if(c == '?'){
reading = true;
}
if(reading){
get_request += c;
}
if (reading && c=='\n')
{
break;
}
if (c == '\n' && currentLineIsBlank) {
break;
}
}
!
17
Peter Dalmaris
if (!sentContent){
construct_page(client, return_message);
sentContent = true;
}
delay(5);
client.close();
Serial.println("client disconnected");
result = "";
Lecture 50
}
}
void construct_page(Adafruit_CC3000_ClientRef &client, String &rmessage)
{
print_header(client);
print_form(client);
print_confirmation(rmessage, client);
end_page(client); }
void print_header(Adafruit_CC3000_ClientRef &client)
{
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println();
client.print("<html><head><title>");
18
Peter Dalmaris
Lecture 50
bool displayConnectionDetails(void){
uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;
if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
{
Serial.println(F("Unable to retrieve the IP Address!\r\n"));
return false; }
else {
Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
Serial.println();
return true;
}
}
19
Peter Dalmaris
Lecture 50
Using the sketch from Demo 2 in Lecture 38 as a building block, we now have the same
capability to control the state of an LED using a web browser but wirelessly. Running this
sketch generates this output in the monitor:
!
!
!
!
!
!
!
!
!
!
!
!
!
Use a web browser and go to your Arduinos IP address, you should see the same user
interface as in Lecture 38, Demo 2:
!
!
!
!
!
!
!
!
20
Peter Dalmaris
Lecture 50
An exercise
In this lecture, we covered a lot of ground in regards to using the Adafruit CC3000 wifi
module to control our Arduino wirelessly. The module we used still has its supporting
library under development, so I expect that its features and stability will improve over time.
The module comes in two types, one with a build-in ceramic antenna, and one with a
connector for an external antenna. An external antenna can be used for projects where
range is important, like when you want to control your quad-copter outdoors.
As an exercise for this lecture, how about you try to extend the Demo 2 and Demo 3
examples with the ability to control more devices? For example, for Demo 2, add a couple
of lines in the instructions file so that you can control additional LED, or motors (you may
want to review Lecture 39 for this). Because polling takes place every few minutes, you
would use the Demo 2 sample in a project like home automation and control, where realtime is not necessary.
21
Peter Dalmaris
Lecture 54
Lecture 54
Single Wire LCD screen
In Lecture 24, you learned how to display text in a LCD screen. Although this was a simple
way to show useful information to the user, the sheer number of wires required to make the
LCD screen work makes this solution far from elegant.
In this lecture, I will show you a much improved solution to the same problem, one that
involves a single data wire (plus power).
The dierence is stark. Have a look at the before (left) and after (right) images for the
exact same result.
To achieve this reduction in total number of wires we have to switch the type of interface we
use to connect the screen to the Arduino. Natively, the screen uses a parallel interface,
where each of the 8 bits that make up a character encoding uses up a wire. You may
remember that in Lecture 24, use used a 4-bit parallel mode instead of the full 8-bits in
order to save 4 wires. Still, even 4 wires are too many for transferring data. We also needed
wires for power, and for the screen backlit.
To improve the design, well use an adaptor that allows us to connect the parallel LCD
screen to the Arduino using the I2C serial bus. We have used I2C before, but here is a quick
recap:
Can be shared amongst multiple I2C devices, which means that you can connect multiple
compatible devices to your Arduino without increasing the wire count.
Peter Dalmaris
Lecture 54
On the adaptor, check the markings of the 4 pins. Connect the GND to GND on the
Arduino, VCC to 5V. Then, connect SDA (DAta) to analog pin 4 on the Arduino Uno, and
DCL (CLock) to analog pin 5. You are not done with the wirings!
You now need to install an LCD I2C library that will replace the original LiquidCrystal library
that comes with the IDE. There are several options that you could use, but the one that I
found easy to use and tested for this lecture is the LiquidCrystal_I2C available here.
Download the ZIP archive from this page and install it in your IDEs Libraries folder. Dont
forget to restart the IDE for the install process to complete!
!
2
Peter Dalmaris
Lecture 54
Sketch
1: Device address
2: LCD screen enable (En) pin
3: LCD screen read/write (R/W) pin
4: LCD screen reset (Rs) pin
5: LCD screen data 0 pin
6: LCD screen data 1 pin
7: LCD screen data 2 pin
8: LCD screen data 3 pin
9: LCD screen backlight pin
10: LCD screen backlight polarity (POSITIVE or
NEGATIVE)
Because I2C is a shared serial bus, we need a way for each connected device to be able to
detect which of the messages that are flowing through the common wires is meant to be
read by them. On the flip side, the Arduino (or any master device) needs to be able to
determine which of the connected devices transmitted a message.
This is achieved by setting an address for each connected device. This address needs to
be unique within the group of the devices that are sharing the bus. Most devices, like the
LCD adaptor in this demo, come with an I2C address preset from factory that, in my
experience, does not conflict with addresses from other devices as long as they are not of
the same type. For example, if you connect an LCD screen adaptor and a real-time clock to
the same I2C bus, chances are that there will be no conflict with their preset addresses and
they will work out of the box.
But lets say you wanted to attach two LCD screens to the same I2C bus, using two
separate, but of the same type, LCD adaptors. In this case, you will need to change the
address of one of the two adaptors. Most I2C devices provide a way to do this, usually by
changing the configuration of jumpers or soldering address pins.
Peter Dalmaris
Lecture 54
In the second demo, well combine 2 devices on the same I2C bus. Well use the LCD
adaptor from Demo 1, and the real-time clock from Lecture 48.
4
3
5,6
1
6. Using the same columns as in step 5, connect the SDA wire to Arduino Uno analog pin
4 and the SCL wire to the Arduino Uno analog pin 5.
7. Connect the power wires from the LCD adaptor and the real-time clock to the
breadboards top power power strip.
By default, the DS1307 is listening to I2C address 0x68 while the LCD adaptor to address
0x27 so there is no conflict.
Peter Dalmaris
Lecture 54
Sketch
#include <Wire.h>
#include "RTClib.h"
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
RTC_DS1307 rtc;
Same as in Demo 1
void setup () {
Add the real-time clock library
Serial.begin(9600);
Start the I2C bus.
Wire.begin();
lcd.begin(16,2);
Start the LCD screen with 16 columns, 2 rows, and
lcd.backlight();
turn backlight on.
rtc.begin();
the real-time clock module, and set the time
rtc.adjust(DateTime(__DATE__, __TIME__)); Start
void loop () {
DateTime now = rtc.now();
Serial.print(now.year(), DEC); Serial.print('/');
Serial.print(now.month(), DEC); Serial.print('/');
Serial.print(now.day(), DEC); Serial.print(' ');
Serial.print(now.hour(), DEC); Serial.print(':');
Serial.print(now.minute(), DEC); Serial.print(':');
Serial.print(now.second(), DEC); Serial.println();
lcd.setCursor(0, 0);
lcd.print(now.year(), DEC); lcd.print('/');
if (now.month()<10)
{ lcd.print("0"); }
lcd.print(now.month(), DEC); lcd.print('/');
if (now.day()<10)
{ lcd.print("0"); }
lcd.print(now.day(), DEC);
lcd.setCursor(0, 1);
if (now.hour()<10)
{ lcd.print("0"); }
lcd.print(now.hour(), DEC); lcd.print(':');
if (now.minute()<10)
{ lcd.print("0"); }
lcd.print(now.minute(), DEC); lcd.print(':');
if (now.second()<10)
{ lcd.print("0"); }
lcd.print(now.second(), DEC);
delay(100);
}
Peter Dalmaris
Lecture 54
You now know how to connect an LCD screen to your Arduino using a single data wire, and
how to combine multiple devices on a single I2C bus.
In this lecture, we used a real-time clock with an LCD parallel to serial adaptor. As an
exercise, try to add one or two additional IC2 devices. Heres some recommendations:
* Add a BMP085 temperature and barometric sensor. We saw this device in lecture 9.
Display the temperature and humidity in the LCD screen.
* Add an SD card module. We saw this device in lecture 45. Store BMP085 readings in a
log file.
* A total of 127 can be attached to an I2C bus. Here is a sketch that can detect and report
all connected devices, try it out: http://playground.arduino.cc/Main/I2cScanner
* Although we have not yet discussed adding external EEPROM modules to your Arduino,
understanding how they work easy based on your existing knowledge. You can consider
the AT24C256 Serial EEPROM device, an inexpensive way to add memory to your
project. It uses the I2C interface, and is easy to use with the appropriate library.
Peter Dalmaris
Interrupts
Interrupts
!
Up to now in our examples, we have been using the loop() function to periodically poll
things like sensors and buttons for their values. For example, in the push button lecture we
used this sketch to check on whether the button was pressed or not and light up an LED if
it was (only showing the loop() function and the code that is relevant here):
void loop()
{
int val = digitalRead(inputPin); // read input value
Turn an LED on if the inputPin is HIGH
}
The Arduino checks (digitalRead) the voltage level in the inputPin and then does some
processing depending on the value that was read. The digitalRead will execute regardless
of whether the button was actually pressed or not. If we execute this code on an
Atmega328 which typically runs at 8MhZ will roughly poll the inputPin 8 million times per
second, and every one of these measurements will come out false since most of the time
the button is not pressed. This is a great waste of resources.
But it can get worse. If the Arduino needs to do other work within the loop, like check on
other sensors or communicate with a web service, it is possible that when the user presses
on the button, the Arduino will never notice because it had not yet reached the digitalRead
instruction at the time of the button press. The Arduino was busy doing something else.
This problem can be solved with an Atmega 328 feature called Hardware Interrupt. This
lecture will show you how to use the hardware interrupt. In the this lecture, I will also show
you how to use a relevant feature called the Time Interrupt.
Hardware Interrupt
A hardware interrupt provides the ability to tie a special pin with a function in your sketch
that will be executed with priority when the state of the pin changes in a particular way.
You can configure the pin and the kind of signal you want to generate an interrupt request,
and tie this to a small interrupt request function. This function is meant to handle the
interrupt. I will give you an example in a minute.
Each Atmega micro-controller has a specific set of pins that can be used as interrupt pins.
For the Atmega328p (which you find in the Arduino Uno) these are digital pins 2 and 3. The
Arduino Mega, which is based on the Atmega2560, provides interrupts in pins 2, 3, 18, 19,
20, and 21.
Peter Dalmaris
Interrupts
This table contains the interrupt numbers and associated pins for the more popular
Arduinos:
int.0
int.1
int.2
int.3
int.4
int.5
Uno,
Ethernet
Mega2560
21
20
19
18
Leonardo
Lets have a look at our first hardware interrupt example and discuss some of the
implementation details.
Demo 1
In this first demo, well take the circuit from Lecture 16 and only modify the sketch it so that
the LED is lit as a response to an interrupt generated by the button, instead of by polling the
state of the button a few million times per second.
A 5mm LED
Peter Dalmaris
Interrupts
void loop()
{
}
Nothing
happening
here!
void buttonPressed()
{
if (digitalRead(ledPin))
digitalWrite(ledPin,LOW);
else
digitalWrite(ledPin,HIGH);
}
In this small sketch, notice how the loop() function is empty. We are going to handle the
button presses in a separate function, buttonPressed(). The name of the function is not
important, you can choose an valid identifier.
In the setup() function, we create the interrupt by calling the attachInterrupt function. This
function requires three parameters:
1. The interrupt ID. This is an integer that corresponds to the ping where we attach the
interrupt device. The IDs for some of the popular Arduinos are shown in Table 1. In the
sketch, we use ID 0, which corresponds to digital pin 2.
2. The interrupt handler function name. This function contains a small amount of code,
just enough to handle the interrupt and then allow the Arduino to continue with
whatever it was doing before the interrupt.
3. Interrupt mode, which is the kind of event that should trigger an interrupt. With this
parameter you are telling the Arduino the type of electrical event it should be monitoring
which would be perceived as an interrupt. In the example sketch, the literal FALLING
tells the Arduino to look out for voltage that goes from HIGH to LOW, and when it
detects that to interpret it as an interrupt. Table 2 (below) contains a list of valid
interrupt modes.
Peter Dalmaris
Interrupts
Description
LOW
CHANGE
RISING
FALLING
Trigger the interrupt when the pin goes from HIGH to LOW
HIGH
Trigger the interrupt whenever the pin is HIGH (only available in the
Arduino Due)
In the sketchs present form, the LED will light up when you press and release the button,
then light down when you press and release again. Try this small variation: change the line
attachInterrupt(0,buttonPressed,FALLING);
to
attachInterrupt(0,buttonPressed,CHANGE);
then compile and upload the sketch. Press the button a few times and observe the
behaviour of the LED. What do you see? (Hint: The LED now lights-up when you press the
button, and remains lit until you release the button).
Hardware interrupts have certain constraints that you need to keep in mind when you
design your app. Here they are in a nutshell.
Hardware interrupts are designed to capture simple events from the outside world. As a
result, parameters like those passed to functions make no sense.
2. The interrupt handler function cannot return any data (a way around this problem is
suggested in Demo 2), and their return type should always be void.
Same thinking goes here. The interrupt originated from the outside world, something like a
button. It makes no sense to return a value to a button. However, there is a use case where
we need the interrupt handler to update the value of a variable in our sketch, like a counter.
This is possible, and Ill show you how in Demo 2.
While the interrupt request handler is running, all interrupts on the micro-controller are
disabled. If you have attached a second button to the second interruptible pin on the
Arduino Uno and you press it while the micro-controller is in the handler of the first button,
4
Peter Dalmaris
Interrupts
then the second button press will be ignored. In fact, anything that uses interrupts will not
work while an interrupt request is being serviced. This includes things like the delay() and
millis() functions, and the loop() function also will be stuck to the line it was at when the
interrupt happened.
4. It is possible to disable hardware interrupts anywhere in your sketch. To do this, use the
interrupts() and noInterrupts() functions. The former one will enable interrupts, while the
second one will disable them. You may want to do something like this in cases where you
are timing an event and you want your measurement to be accurate, when you are writing
serial data to an external device like an SD card or similar. Your code could look a bit like
this:
void loop()
Turn off hardware interrupts.
{
noInterrupts();
Heres the code that should not be interrupted.
// critical, time-sensitive code here
interrupts();
Turn on hardware interrupts.
// other code here
}
The critical code that should not be interrupted can simply be code that modifies a variable
that may also be modified from inside a interrupt handler function. More about such
variables is discussed in Demo 2.
Peter Dalmaris
Interrupts
In the second demo, well add a volatile variable which the code in the interrupt request
function will be able to access and update.
This variable will keep track of the number of times that we have pressed on the button.
void setup() {
pinMode(ledPin, OUTPUT);
attachInterrupt(0,buttonPressed,FALLING);
Serial.begin(9600);
}
void loop()
{
}
void buttonPressed()
{
counter++;
Serial.println(counter, DEC);
if (digitalRead(ledPin))
digitalWrite(ledPin,LOW);
else
digitalWrite(ledPin,HIGH);
}
In this sketch, the only dierence is the addition of the volatile byte variable counter and
the increment operator on this counter in the interrupt handler function. We also print the
value of this counter every time it gets updated.
So what exactly is the purpose of the volatile keyword? If the keyword is not used, then
the compiler will generate machine code that optimises the use of this variable by caching it
in a CPU register whenever possible. For example, if the counter variable is used inside a
loop, then it is possible that instead of storing the value of the variable in RAM, it will be
stored in one of the available registers. A CPU register is several times faster than the RAM,
and therefore this caching represents a gain in performance. By indicating to the compiler
that counter is volatile, the compiler mark it so that it will never be cached in a register.
Instead, it will be always stored and fetched from the RAM, meaning that it will be always
Peter Dalmaris
Interrupts
Before finishing the Demo, consider the case where you would like to print (or just access)
the value stored in the counter variable inside the loop function. You can add the code for
this inside the loop function like this:
loop(){
Serial.println(counter);
}
The problem with this is that the println instruction may try to access the counter variable at
the precise moment when an interrupt request is generated by the button. In this case, the
value that is printed is not possible to determine. to guard against such situation, we
enclose the instruction that accesses the volatile variable within a noInterrupts() and
interrupts() block, like this:
loop(){
noInterupts();
Serial.println(counter);
interrupts();
}
With this guard in place, a button press during the time that the Serial.println instruction is
being executed will be ignored. The chances of this happening while an actual button press
are few, as long as you keep the processing that is happening within the noInterupts() and
interrupts() block to a minimum.
Peter Dalmaris
Interrupts
A timer interrupt is an interrupt that the Atmega generates using an internal timer, rather
than an external event.
You could create, for example, a sketch that checks on a sensor every 5 seconds without
using the delay function. A timer interrupt can be easily set with the help of the TimerOne
library and can be use to create timers from 1ms to 8,388,480 or around 8.4 seconds.
In this demo, well make an LED blink by setting a timer to trigger an interrupt once every
second. The sketch follows, with embedded comments:
#include <TimerOne.h>
void loop(){ }
void toggleLED()
{
digitalWrite(ledPin, ledState);
ledState = !ledState;
}
First, include the TimerOne library in the sketch. The one I used is a newer and enhanced
fork of the original TimerOne, and you can download it from https://github.com/
PaulStoregen/TimerOne.
In the setup() function, we initialize the timer with a value that represents the period duration
in microseconds. So, 1 second is equal to 1 million microseconds. Then, we attach the
timer interrupt to a function that will handle it, by name.
In the interrupt handler, we set the LED state, which is updated every time the function is
called. The instruction ledState = !ledState takes whatever value is currently stored in
ledState, flips it, and stores the new value to itself.
!
!
8
Peter Dalmaris
Interrupts
The TimerOne library also contains several other useful functions that you can explore. One
of them provides that ability to create very accurate PWM output. Normally, using the
analogWrite function, you can create PWM output on a scale from 0 to 255.
Calling this:
analogWrite(127);
will create a PWM waveform with a duty cycle of 49.80% (since 255 -> 100%).
Calling this:
analogWrite(128);
(notice that we just went from 127 to 128), will create a PWM waveform with duty cycle
of 50.19%.
If you are trying to generate more accurate waveforms, you can do it using the TimeOne
library like this:
Timer1.pwm(pin, duty);
where pin is a digital pin that the library supports, and duty is an integer from 0 to
1024.
In the Arduino Uno, we can use pins 9 and 10. See the librarys documentation for
information on pin support in other models.
So, this:
Timer1.pwm(9,300);
would have a duty cycle of 29.29% (since 1024 -> 100%), and
Timer1.pwm(9,301);
would have a duty cycle of 29.39%. You can see how with TimeOne you have much finer
control over the waveform that your PWM pin is able to produce.
Peter Dalmaris
Interrupts
Connect the LED to digital pin 9, and try out this sketch:
#include <TimerOne.h>
int ledPin = 9;
void setup()
{
Timer1.initialize(1000);
Timer1.pwm(ledPin,0);
pinMode(ledPin, OUTPUT);
}
void loop(){
Timer1.setPwmDuty(ledPin,
delay(1000);
Timer1.setPwmDuty(ledPin,
delay(1000);
Timer1.setPwmDuty(ledPin,
delay(1000);
Timer1.setPwmDuty(ledPin,
delay(1000);
}
250);
500);
800);
1024);
You should see that the LED lights up very gradually and finely, the eect of the small
increase in the PWM duty cycle will every loop cycle.
Conclusion
In this lecture you learned about hardware and timer interrupts, how to handle them, where
they might be useful, and some of their limitations.
Often, interrupts are the ideal solution to getting the Arduino to respond to events that are
infrequent without unnecessary waste of resources. But think carefully before you make use
of them, they can often be dicult to debug, especially when you have implemented more
than trivial functionality.
10
Peter Dalmaris
Shift Registers
Shift registers
The Arduino only has a relatively small number of digital outputs. While there are enough of
them to get us through the examples in this course, in most real life projects they will be not
enough.
Take, for example, the the case of a gadget that contains a character LCD screen, a couple
of status LED, a couple of buttons, and a couple of sensors and a Wifi breakout. You will
need 2 pins for the screen (using the serial to parallel adaptor), 2 for the LEDs, 2 for the
buttons, at least 2 for the sensors, and 6 for the Wifi breakout. That more or less exhausts
the available ports.
Sooner or later you will need a way to multiply the available inputs and outputs so that you
can connect a larger variety of peripherals.
In this lecture, Ill show you how to use shift registers to multiply the available digital
outputs. In a later lecture I will also introduce you to I2C-driven port expanders. These
technologies make it possible to design ever larger and realistic gadgets using a single low
cost micro-controller.
Before getting into it, I should highlight that more available ports do not automatically
guarantee that you will be able to create larger gadgets. Once the input/output port
availability is dealt with, the next potential show stopper is memory. At 32Kbytes of flash
memory, it becomes dicult to create sketches that match the ever growing hardware they
are supposed to drive. So, also in a later lecture I will talk about ways to manage and
optimise memory use.
Peter Dalmaris
Shift Registers
Think of a shift register as a single-byte memory. Each of the bits in this memory is
connected to the outside world via a pin on the package of the chip that contains the
memory. You use a single data pin from the Arduino to write each bit, one at a time.
Because each bit is written individually, and all bits can be read all at once, we say that a
shift-register is a device that supports serial in and parallel out.
To support the serial transfer of bits from the Arduino to the shift register chip, we need a
second pin that provides a clock signal. Every time the clock ticks, one bit is transmitted
from the Arduino to the shift register. Once the new bit is received by the shift register, all
existing bits are shifter by one bit to make room for the new one. This shifting process is
what gave the name shift register to this device. Heres a graphical way to look at this
process:
Lets say that wed like to store the byte 11001101 in the shift register represented by this
table. At start, the shift register has an undeterminable state, so Ill represent this with a
question mark.
This is Step 0:
In Step 1, well take the least significant bit (first from the right - 11001101), wait for the
clock to tick, and shift it in the register. We could have chosen to start with the most
significant bit just the same.
This is Step 1:
Notice the grey box at the end of the register. This is an additional overflow bit memory
that stores the bit that was shifted into it from the bit on its left. We will be using this
overflow bit in Demo 2 when we create a circuit that uses two shift registers connected in
series.
In Step 2, well take the next least significant bit (second from the right - 11001101), wait for
the clock to tick, and shift it in the register. Notice that the original first most significant bit
(1) is greyed out to show that it has already been transmitted to the register. We now also
have the first bit that shifts out of the overflow bit and is lost for all eternity. Im only
including it here outside the register to emphasise the outcome of the shift function.
This is Step 2:
In Step 3, the same process continues: clock ticks, next bit in 11001101 is sent to the
shift register, and all existing bit in the register are shifted one position to the right to make
room for the new bit.
This is Step 3:
??
Peter Dalmaris
Shift Registers
!
!
???
????
?????
??????
???????
!
!
!
!
!
!
!
!
And finally, the complete byte has been shifted into the shift register. You can sent single
bits to the register, its not necessary to do so for a whole byte (though most libraries make
the latter easier than the former). For example, lets say that you wanted to sent a 0 to the
register, which already contains the values from the previous experiment. You would end up
with these new contents in the register:
!
!
????????
The original least significant bit from the first experiment has now been shifted into the
overflow bit cell to make room for the new value at the left end of the register.
Another way to think about the way that this register operates is FIFO: First bit in is First
bit out.
I hope you have a solid understanding of this process now, it will make it easy to
understand what is happening with Demo 1 which follows. If you feel unclear about
something, read this section again or ask a question in the forum.
3
Peter Dalmaris
Shift Registers
Normally, to drive an LED you need to connect it to one of the Arduinos outputs. If you
need to drive two LEDs, then you need 2 outputs. Etc. Etc.
In this demo, well drive LEDs by using a single data wire, with the help of a shift register.
Shift register chips are very cheap and made by many dierent companies. A typical one is
the 74HC595N. This device provides 8-bit storage, plus one for the overflow. The particular
one I used is made by NXP, and you can find its datasheet here. The first thing you want to
look at in the datasheet is the pin out diagram so that you can see the functionality of each
pin. I have grabbed this from the datasheet. I define the pin functions in the table next to
the diagram.
Q1-Q7
8 GND
Ground
9 Q7S
10 MR
Master reset. If this pin is grounded,
inverted the chip is reset. Well keep it
connected to 5V.
11 SHCP
12 STCP
13 OE
Enable or disable output pins. We will
inverted keep it always connected to GND so
that outputs are always enabled.
14 DS
15 Q0
16 Vcc
Power in (5V)
Peter Dalmaris
Shift Registers
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
Well use 220 or close resistors for each LED. Start with the 74HC595N chip on one side
of the breadboard, then connect the LEDs and their resistors. Then, do the wiring between
the LEDs and the D0-D7 pins on the chip.
Peter Dalmaris
Shift Registers
Connect the 5V breadboard rail to Pins 16 and 10 on the chip, and ground to Pin 8 on the
chip. Chip pins 9 and 13 are not connected to anything at the moment.
If everything is connected properly, then once you apply power for the first time, the LEDs
will light up at a random pattern. They will show whatever is stored in the shift register. Lets
have a look at the sketch and see how we can change the pattern.
void setup() {
pinMode(latchPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
Serial.begin(9600);
randomSeed(analogRead(0));
}
void loop() {
byte randNumber1 = random(255);
writeLeds(randNumber1);
delay(100);
}
!
!
!
Plug in your Arduino and upload the sketch. You should see the LEDs blinking in random
patterns, like this (next page):
!
!
!
6
Peter Dalmaris
Shift Registers
!
!
!
Perhaps now you can see how a shift register can be used to allow your sketch to control
an arbitrary number of external devices. Instead of LEDs, you could be controlling relays or
transistors as switches for higher loads (that is, devices that require a lot of power to run). It
is even possible to use shift registers as an input, whereby the shift register reads all inputs
in parallel, and then shifts them out in a serial manner to a reading device (like the Arduino).
An example of such a parallel-in serial-out register is the SN74ALS164A (link will open its
data sheet).
Another consideration is power. The 74HC595N can provide less than 20mA for each
output pin, and in our circuit it draws this power from the Arduinos power supply. If the
load placed on it exceeds a certain limit, the circuit will either not work or it will be damaged
if it is forced to exceed its specifications. In such situations, you can consider providing a
separate power supply for the 74HC595N and for its loads (like the LEDs). You can do this
with a simple transistor amplifier circuit, or by segmenting the driver circuit (the 74HC595N
and the Arduino) from its load with relays.
Peter Dalmaris
Shift Registers
In demo 2, well add a second 74HC595N to our circuit and drive an impressive 16-LED
array, all lighting up in random patterns, while still only using a single data wire on the
Arduino (plus latch and clock). Heres the circuit, it really is simpler than what it looks:
The circuit from Demo 1 is on the left of the breadboard. The new part on the right is made
of the same arrangement of LEDs and resistors, plus the second 74HC595N. Because
theres so many LEDs drawing a few mA of power each at random times, there will be a
strain on the Arduinos power supply. To smooth out spikes in consumption, I have added
an electrolytic capacitor directly on the power rails of the breadboards. The one I used in
my circuit is 220F. Be careful of the polarity, it is clearly marked on the package of the
capacitor. If you remember, we used a capacitor in the exact same way in the servo motor
lecture.
Lets concentrate on the second 74HC59N (Chip 2) for a moment. The first 74HC59N is
Chip 1. Connect it like this:
Chip 2 Pin 14 to Chip 1 Pin 9 Q7S (this establishes the daisy chaining of the two chips)
Peter Dalmaris
Shift Registers
Connect the 5V breadboard rail to Pins 16 and 10 on Chip 2, and ground to Pin 8 on Chip 2.
Chip 2 pins 9 and 13 are not connected to anything.
This is the sketch. Its almost identical to the sketch in demo 1. The only dierences are
highlighted below.
!
The only change to the sketch is the addition of a second random byte, and a second call
to the writeLeds function. The first call will transmit the first random byte, and the second
call will transmit the second random byte.
This is what is happening: as the bits stored in the first 74HC59N are shifted out by the bits
of the second byte, one by one they are pushed into the overflow bit cell. This is Pin 9 Q7S
on Chip 1. As Chip 2 has its Pin 14 (Data pin) connected to Chip 1 Pin 9, it will pick the
overflow bit and start shifting it in its own cells. Since Chip 1 and 2 share the same latch
and clock signals, they will be synchronised and work together as a single 16-bit shift
register. You can daisy chain as many 74HC59N units you like like this, the concept is the
same.
Peter Dalmaris
Shift Registers
Conclusion
Shift registers operate on a very simple principle, however from experience I know it is a topic that
tends to confuse new makers. Hang in there, read again, experiment with the circuits, and ask if you
run into trouble. Once you understand the basics, you will be able to use them without much eort.
!
!
As an exercise, try this: modify the sketch for Demo 1 so that you can type in a number from 0 to 255
and have the binary representation of that number show in on the LEDs. You will need to figure out
how to enter text in the console (I have shown you how to do this in a previous lecture).
You can also try this: modify the sketch from Demo 1 so that the number of LEDs lit depends on the
value coming out of a potentiometer. When the potentiometer is turn all the way to one direction, you
will have no LEDs lit, when its all the way to the other direction you will have all of the LEDs lit, and
accordingly for all other positions.
10
Peter Dalmaris
EEPROM
EEPROM
The Atmega microcontroller, as we have seen already, contains 3 types of memory: Theres
flash (where the sketch is stored), SRAM (where temporary data are stored), and EEPROM.
SRAM (Static RAM) can store data for as long as power is provided. Turn o the power, and
everything is the SRAM is lost.
Both flash and EEPROM memories persist after the power is cut, so you could use either
one for long term data storage, a bit like a hard disk works. But heres the dierence: flash
memory can only be changed when a new program is uploaded to the Arduino. People
often refer to this process as flashing. Once flashing is completed and the newly
uploaded sketch is in place, you cannot change anything in the flash memory.
Thats where EEPROM comes in. EEPROM stands for Electrically Erasable Programmable
Read-Only Memory, and you can use it to store data that must be preserved even after
power is lost. You could use EEPROM to store configuration values for your sketch that the
user can modify, passwords, sensor logs and things of that sort.
In this lecture, you will learn how to use internal and external EEPROM. As a bonus, I will
show you how to create a small library so that you can simplify your sketch organisation
and reuse utility code.
The Atmega328 contains 1KByte of internal EEPROM. That means that you can store and
persist 1024 words, where each word is made up of 8 bits in your Arduino Uno. For this
demo, we will not use any external hardware, just plug your Arduino to your computer and
youre good to go.
In this first demo, well use two sketches to write and read a pin number for a basic security
system. The writing sketch will write the default pin in a specific internal EEPROM memory
location, and the reading sketch will simply go to that location and read back the pin.
Keeping things simple, we are not going to worry about LCD screens, a keypad for the pin
entry, or encryption for storing the pin securely.
The pin well write and read is an integer. On the Atmega, integers consume two bytes, and
we will need to take this into account in our sketch since EEPROMs are byte-addressable.
This means that each byte has its own address, and we cant simple store an integer like
we do with the RAM, where the compiler knows how to deal with integer (and other datatype) addressing. With the EEPROM, while using the basic facilities that come with the IDE,
we have to split an integer to two bytes, store each byte individually, and then again
separately retrieve the two bytes and concatenate them so that we end up with the original
integer. Bear with me for now, in Demo 2 well simplify this process a lot.
!
1
Peter Dalmaris
EEPROM
Heres the writing sketch (Demo1_writer), lets execute it and then go through it to explain
how it works.
#include <EEPROM.h>
int addr = 0;
void setup()
{
Heres the Pin, its an integer that needs to bytes
Serial.begin(9600);
of storage.
int myPin=1234;
highByte is a build-in function that
EEPROM.write(addr, highByte(myPin));
extracts the left-most byte (high-order) of
EEPROM.write(addr+1, lowByte(myPin));
an integer, or the second-lowest byte of
Serial.print("My pin recorded:");
a multi-byte data type like a double
Serial.print(myPin);
(which occupies four bytes).
Serial.println(".");
Similarly to highByte, lowByte is a build}
in function that extracts the right-most
byte (low-order) of an integer, or the any
multi-byte data type.
void loop()
{ }
If we only wanted to store a byte, we would be able to just use EEPROM.write, pass an
address (from 0 to 1023), and the byte, and that would be all.
Unfortunately, the basic EEPROM library that comes with the IDE and we use in this sketch,
can only understand bytes, not integers or other multi-byte data types, so we have to take
care of some basic binary arithmetic.
The pin we selected, 1234, converted in binary format, looks like this:
high byte
00000100
11010010
low byte
The byte on the right is the low byte, and the byte on the left is the high byte. We need to
store each one individually, so we call EEPROM.write, twice:
1. EEPROM.write(addr, highByte(myPin));
2. EEPROM.write(addr+1, lowByte(myPin));
First, take the high byte of the pin and store it in address 0, then take the low byte of the pin
and store it in address 0+1 = 1.
Thats all, the pin is now stored in the internal EEPROM. You would do the exact same thing
if you wanted to store a user setting. For example, if you had built an alarm clock, the user
could enter an alarm time, and you would store the time components in the EEPROM to
safe guard against power loss.
Peter Dalmaris
EEPROM
In this sketch (Demo1_reader), we read the two bytes we stored earlier from the EEPROM,
and then we concatenate them so that we can recreate the original pin.
!
#include <EEPROM.h>
int addr = 0;
Declare the byte variables that will eventually
void setup()
contain the two bytes of the original pin.
{
Serial.begin(9600);
Retrieve the bytes from addresses 0 and
byte retrievePinHigh;
1, and store them in RAM.
byte retrievePinLow;
retrievePinHigh = EEPROM.read(addr);
Do some binary arithmetic (<< bit shift
retrievePinLow = EEPROM.read(addr+1);
left) to assemble the pin from its two
Serial.println(retrievePinHigh);
parts.
Serial.println(retrievePinLow);
Serial.print("My pin is:");
Serial.print((retrievePinHigh << 8) + retrievePinLow);
Serial.println(".");
}
void loop()
{ }
!
Fetching the two bytes from the EEPROM is straight-forward. We use EEPROM.read, pass
the address of the byte we want to fetch, and store what is retrieved in a variable in RAM.
But how do we re-assemble the pin correctly? For that, we use the bitshift left operator.
There is also bitshift right.
Bitshift operators to exactly what their name says, they take a byte and move its bits left or
right by as many positions as we request.
For example, take this byte, and lets bitshift it to the left by 3 positions:
original
shifted
11001101
11001101000
Notice how all this operator did was to add three zeros to the right side of the byte, and
push everything to the left. We now have a binary number with 11 bits. Thats 8 original bits,
and 3 inserted bits.
Peter Dalmaris
EEPROM
If you do the opposite, i.e. bitshift to the right by 3 positions, you will end up with this
number:
11001
Notice how the bitshift right operator pushed the byte to the right, and this resulted to the
first three bits disappearing into the binary ether, and ending up with a 5 bit number.
High byte
High byte shifted to
the left by 8 bits.
Low byte
Shifted high byte and
low byte added.
00000100
00000100 00000000
11010010
00000100 11010010
!
And there you have it, your pin has been retrieved!
!
!
Peter Dalmaris
EEPROM
Dealing with raw bytes may seem daunting at first, but with a bit of practice thinking in
bytes becomes almost second nature. Still, I find that often such low-level implementation
details slow us down. You really want to build a prototype quick, not to be caught up in
binary arithmetic, even when its as simple as what we saw in Demo 1. Personally, I find it
more ecient for my own productivity to work at a high-level (logical) rather than having to
worry about the hardware too much. If your gadget qualifies the prototyping phase, you can
always go back for a round of optimisations. So, in this demo, Ill show you a way to
abstract the way that data is written to and read from the EEPROM in a bid to speed up
your personal productivity.
We will make use of the EEPROMex library, or EEPROM Extended. This library extends the
one that comes with the IDE so that it knows about all the common datatypes that we are
likely to use in Arduino sketches, not just the byte.
With EEPROMex, if you want to store or fetch an integer or a double, you just call the
appropriate method and the job is done. No need to think about bits, bytes, and bitshifting.
This abstraction and flexibility comes at the cost of flash memory - adding the library adds
a couple of kilobytes to your project sketch.
Get the library from its Github repository, install it in the Libraries folder, and restart the IDE.
The library is a drop-in replacement of the default EEPROM library, meaning that you can
just replace one for the other, and your sketch will still work. The extended library adds
functions for longs, ints, floats and doubles. You can also manipulate single bits (great for
better managing storage resources), arrays, strings, C structs, and it even provides an
update function that checks if a new value already exists in the EERPOM before saving it
again, thus extending its life span.
#include <EEPROMex.h>
int addr = 0;
void setup()
{
Serial.begin(9600);
int myPin=1234;
EEPROM.writeInt(addr, myPin);
Serial.print("My pin recorded:");
Serial.print(myPin);
Serial.println(".");
}
Peter Dalmaris
EEPROM
void loop()
{
}
Once the original library has been replaced with EEPROMex.h, all we need to do is to use
the appropriate function depending on the data type we are working with. In the example,
EEPROM.writeInt will write the integer myPin to address addr.
#include <EEPROMex.h>
int addr = 0;
Used EEPROM.writeInt to write and integer? then
void setup()
use EEPROM.readInt to read it back from the
{
EEPROM.
Serial.begin(9600);
int retrievePin;
retrievePin = EEPROM.readInt(addr);
Serial.println(retrievePin);
Serial.print("My pin is:");
Serial.print(retrievePin);
Serial.println(".");
}
void loop()
{
}
You only need to use the corresponding read function for whichever write function you used
to write a value.
Peter Dalmaris
EEPROM
You now know how to use the internal EEPROM, but since its only 1KByte in size, you may
wonder what you can do in order to store more data. You may remember from the SD card
lecture, that SD cards is a good way to store lots of data persistently. However the SD card
module is a bit pricey (you can get one for $2-$10 depending on its bells and whistles), and
then you still need to add the cost and the size of the SD card itself (over $5), and the
complexity of using the card itself.
An external EEPROM is a good alternative. You can get a 256KByte external EEPROM for
around $1.5, store a lot more data in it as compared to the internal capacity, and you only
need a tiny amount of space on your printed circuit board. So lets check this out.
!
!
!
!
!
!
!
Arduino
EEPROM module
5V
VCC
GND
GND
A4
SDA
A5
SCL
Peter Dalmaris
EEPROM
To communicate with an I2C device, we need to know its I2C address. I had no idea what
my EEPROMs address was, so I used an I2C device scanner sketch to find it out.
I have the sketch in the courses Github account. It isnt too important to discuss how the
scanner works, well cover that in a lecture dedicated to I2C. But for now it is enough to say
that the scanner loops through the 127 possible I2C addresses, send a message to each
one, and if the message is acknowledged then we assume that a device is connected and
listening at that address.
With my I2C EEPROM device only connected, I uploaded the scanner sketch, and this
output came out:
!
!
!
!
!
!
!
!
!
So, we now know that the EEPROM device is listening at address 50. Well use this
information in the sketches that follow.
To demonstrate how to use the external EEPROM, well follow the pattern from Demos 1
and 2: there will be a sketch that writes a pin in the ROM, and one that reads it back.
Because the external EEPROM communicates via I2C, we will need to use the I2C protocol
to communicate with it. Well combine the two into one sketch, where the writing will take
place in the setup() function, and the reading in the loop() function.
Because I dont want to deal with the low-level implementation details of the I2C interface
and its EEPROM protocol for the chip I am using (the 24C256), I decided to use some code
that adds a layer of abstraction. This means that instead of having to worry about the actual
I2C commands required to store or retrieve an integer or a byte, I can just call a function
like i2c_eeprom_write_byte with some parameters and be done with it.
Such code exists in the Arduino Playground, but unfortunately it is not available as a library.
Instead of just including it into your project, you have to copy and paste the code into your
Peter Dalmaris
EEPROM
sketch. This adds baulk, makes it harder to debug, and fails in the principle of reusing utility
code.
I took this as an opportunity to show you how to create a small Arduino library. The code
that follows is the code that I want to convert into a library so that I can reuse it in my
sketches by simply including the name of the library.
You can choose to not read the next section and jump straight to the external
EEPROM demo. There will be a separate lecture which will be a more gentle
introduction to Arduino libraries.
Here it is:
Peter Dalmaris
EEPROM
An Arduino library is made up of two files: the header file, and the implementation file.
The header file contains a list of functions and variables that are publicly available. The
implementation file contains the actual code that implements the functions. Lets create the
header first. Just choose a name, I went with EEEPROM, short for External EEPROM.
10
Peter Dalmaris
EEPROM
Create a new folder, name it EEEPROM, and in it create a new file named EEEPROM.h:
#ifndef EEEPROM_h
#define EEEPROM_h
#include <Arduino.h>
#include <Wire.h> //I2C library
class EEEPROM
This library will make
use of Wire.h, so
{
The Arduino library provides access to all Arduino
include it.
data types and constants.
private:
public:
EEEPROM();
void i2c_eeprom_write_byte( int deviceaddress, unsigned int
eeaddress, byte data );
void i2c_eeprom_write_page( int deviceaddress, unsigned int
eeaddresspage, byte* data, byte length );
byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress
);
void i2c_eeprom_read_buffer( int deviceaddress, unsigned int
eeaddress, byte *buffer, int length );
};
#endif
!
!
!
!
Think of the header file as the skeleton of our library. Its just a declaration of what the
outside world should know about it, without any internal details.
11
Peter Dalmaris
EEPROM
Lets have a look at the implementation. Create a new file named EEEPROM.cpp and add
this code in it:
#include <Arduino.h>
#include <Wire.h> //I2C library
#include <EEEPROM.h>
EEEPROM::EEEPROM(){
!
continues
Peter Dalmaris
EEPROM
!
!
There is one more file we need to create, it is called keywords.txt and it contains a list of
keywords that the Arduino IDE should be able to recognise and highlight in the text editor.
Inside the EEEPROM folder, create a new file named keywords.txt with these contents:
i2c_eeprom_write_byte
i2c_eeprom_write_page
i2c_eeprom_read_byte
i2c_eeprom_read_buffer
!
!
!
KEYWORD2
KEYWORD2
KEYWORD2
KEYWORD2
I am using KEYWORD2 to highlight the function names, as per the convention. Heres what
the result is like in the IDE:
!
!
!
!
You can find the latest version of this library on Github. Copy the EEEPROM folder into the
Arduino IDE libraries folder and restart to complete the import process.
!
13
Peter Dalmaris
EEPROM
Finally, lets use the new library so that we can read and write some data from the external
EEPROM chip. Instead of an integer, lets deal with a string. Heres the sketch:
void setup()
We will write this string (array of char) to the EEPROM.
{
char somedata[] = "this is data from the eeprom";
Wire.begin();
Serial.begin(9600);
eeeprom.i2c_eeprom_write_page(0x50, 0, (byte *)somedata, sizeof(somedata));
delay(10); //add a small delay
Serial.println("Memory written"); Call i2c_eeprom_write_page, which writes multiple
bytes to the EEPROM. Pass the I2C address of the
}
void loop()
{
int addr=0;
byte b = eeeprom.i2c_eeprom_read_byte(0x50, 0);
while (b!=0)
{
Serial.print((char)b);
addr++;
b = eeeprom.i2c_eeprom_read_byte(0x50, addr);
}
Serial.println(" ");
delay(2000);
}
!
!
The sketch will store the string this is data from the eeprom in the external EEPROM, the
retrieve it and print it to the console every two seconds. By including the EEEPROM.h
library, we have simplified the sketch a lot.
There is still room for improvement though. Notice how the I2C address 0x50 has to be
provided as a parameter every time we call one of the library functions? Can you think of a
way to only provide this address once by improving the way that the library is constructed
and used?
14
Peter Dalmaris
EEPROM
Conclusion
Try to be as frugal as possible when using EEPROM. These memories have a limited lifespan, usually 100,000 read/write cycles. This is indeed a large number, however if you do
reading and writing in a loop, like when storing sensor log data every few seconds, you
could wear it out before you know it.
In general, it is a really bad idea to put write calls to the EEPROM in a loop as it is possible
to burn out those bytes in a few minutes. That is a big reason behind only playing with the
EERPOM inside the setup loop in the demos of this lecture, never inside the loop function.
15
Peter Dalmaris
Hardware Debouncing
Hardware debouncing
In cases where you need to use a push button, relays or switch (or anything mechanical
used to close a circuit), like we saw in Lectures 15 and 28, we have a situation where metal
comes in contact with metal in order to close the circuit. These contacts are never perfect,
and as a result instead of getting one clean connection, you get multiple ones.
Have a look at the oscilloscope screenshot below (I took this from Wikipedia):
The vertical lines in the centre of the measurement shows that this switch made contact
and lost it several times within the space of a couple of milliseconds. If your Arduino was
connected to this switch via a hardware interrupt pin, then the interrupt service routine
would have been called multiple times.
In many cases, like when we just want to switch on an LED, this doesnt really matter. In
other cases, like when we use a 4x4 keypad to enter a password, it does. Any number key
that you press on the keypad will generate several logical key-presses and this will make it
hard for the Arduino to determine what keys you actually pressed.
This phenomenon is called bouncing, as the voltage of the sampling pin of your switch
bounces from one state to the other before it eventually settles.
In this lecture, we will look at how we can achieve de-bouncing, which means how to get
contacts to output a clean signal when they are closing or opening.
!
1
Peter Dalmaris
Hardware Debouncing
It is possible to write a software de-bouncer that will do the job. It is not my preferred
option since it is tying up the Arduino doing house-hold maintenance and adds unnecessary complexity to your sketch. I am mentioning it here for the sake of completeness
and because the relevant sketch comes with the IDE.
You can find it at File > Examples > Digital > Debounce. It looks like this:
int buttonState;
To implement this software solution we had to add four variables and 8 lines of code that
operate in the loop, and this is far less than ideal.
Peter Dalmaris
Hardware Debouncing
You can build a simple de-bouncer by inserting a small capacitor in parallel to the button or
switch.
This is a much better solution than the software debouncing we saw already. However, if the button is
connected to digital electronics like a microcontroller, the smooth transition that the capacitor
creates between LOW and HIGH is problematic.
!
But sudden, clean transitions are good:
!
This is not to say that you cant use such a simple debouncing circuit with your Arduino, of
course. You could. But it is possible to improve and create a clean transition between LOW
and HIGH without much extra eort.
What I would like to show you next is a cost-eective solution that takes care of debouncing without adding complexity.
Peter Dalmaris
Hardware Debouncing
A Schmitt Trigger device is designed for removing noise in digital circuits. They are
commonly found in applications where bouncing from mechanical switches is a problem.
Logically, a Schmitt Trigger will invert an input signal.
The following schematic (taken from Wikipedia), shows the function of the Schmitt Trigger.
!
!
!
!
!
!
!
!
!
!
U is the original, unconditioned (noisy) signal. B is the output of the Schmitt signal, a
nice digital signal without bouncing. In the red boxed area, notice how B is HIGH when U is
above the top dotted green line and stays HIGH until U falls below the bottom green line.
In the green box area, notice how the same happens even though the U waveform bounces
up and down significantly, however it does not fall below the bottom green line. Thats
exactly what the Schmitt trigger does.
These two green dotted lines mark the high and low thresholds levels.
The A waveform is the waveform that comes out of a component of the Schmitt trigger
called a comparator. The comparator is very simple, a kind of binary amplifier, that just
outputs HIGH when its input voltage goes above a certain threshold (represented by the red
line) and LOW when it falls below that same threshold. That, on its own, is not very useful.
As you can see from its waveform, it doesnt do much to debounce the original noisy signal.
Peter Dalmaris
Hardware Debouncing
To achieve the 2 green threshold levels that we need for a proper de-bouncer, we create a
positive feedback (B - which is also the output of the Schmitt Trigger) which reinforces the
input to the comparator (A), like is shown in the schematic below (also from Wikipedia).
!
To recap, a Schmitt trigger is a device that provides de-bouncing by conditioning the
original noisy signal. The output of the Schmitt Trigger depends on the upper and lower
threshold levels. Usually, the upper threshold level is 66% of the rail voltage (in the case of
the Arduino, that is 66% of 5V) and the lower threshold level is 33% of the rail voltage.
A commonly used Schmitt Trigger IC is the the 74HC14. It contains 6 Schmitt Triggers. We
will use this IC to debounce a push button in this demo.
!
5
Peter Dalmaris
Hardware Debouncing
In this demo we will de-bounce a push button using one of the 6 Schmitt Triggers in the
74HC14 IC.
Well use the Schmitt Trigger to debounce the push button. When the button is pressed, the
LED will turn on.
!
!
!
!
!
!
!
IC Pin
Connects to
14 5V
7 GND
1 To button via a 100
resistor (or without the
resistor)
2 To Arduino pin 2
All unused
inputs
Button pin
!
!
!
!
Connects to
Peter Dalmaris
Hardware Debouncing
Here is the sketch, it is similar to the one from the lecture on interrupts.
Try the same things with various buttons and switches that you may have at hand. I find in
my experiments that dierent buttons behave very dierently to one another. The capacitor
and pull up resistor of the button can be tweaked in order to find their optimal values for
each particular button. There is an amazing article in the Ganslee Group blog that analyses
contact de-bouncing in great detail and that I strongly recommend you read at some point.
The article covers the causes and eects of bouncing and provides alternative de-bouncing
techniques that work well in the real world. You will not think of wires and switches the
same way again once you read this article!
Peter Dalmaris
Hardware Debouncing
In conclusion
De-bouncing switches has an element of art because every switch has its own analog
characteristics and imperfections. Using Schmitt Triggers is one of several techniques
available to us. The Schmitt Trigger is a common way of conditioning (cleaning) the signal
going into a digital circuit. In this lecture, we saw how to use a Schmitt Trigger to debounce a button, without having to make any software changes to our sketches.
I suspect that because of the large variation between the buttons that I use and those that
you will use, your results will be dierent to mine. Spent a bit of time tweaking your circuit
to get a feel of its behaviour. Start with large changes, like for example, try to remove the
capacitor and see what the eect on the reliability of the switch is. Try to go for a larger or
smaller capacitor, or increase/decrease the value of the pull-up resistor. Those two
components can be used to calibrate the sensitivity of the Schmitt Trigger inside the IC for
the particular characteristics of your button.
Peter Dalmaris
!
!
!
!
!
!
!
!
!
!
!
!
!
The measurement pin of the button is connected to both digital pin 2 on the Arduino and
(via a large 50k resistor) to the GND pin of the Arduino. Therefore, this is a pull-down
resistor. The large resistor ensures that when the button is not pressed, the yellow jumper
wire transmits a LOW reading to digital pin 2. When the sketch detects this, it can safely
conclude that the buttons is not pressed.
Keep in mind that we could just as easily had used a pull-up resistor. Connect the resistor
to 5V, and the other pin of the button to GND. We would just modify our sketch so that a
Peter Dalmaris
HIGH means that the buttons was not pressed, and a LOW means that the button was
pressed.
The Atmega micro-controller contains large (between 20k and 50k depending on the
model) that we can enable so that we can remove the external resistor from our circuit. In
the Arduino Due, the pull-up is between 50k and 150k.
!
!
!
!
!
!
!
!
!
!
!
The circuit is much the same with the exception that the pull-up/down resistor has been
removed. We just connect the pins of the button to GND and digital pin 2 from where we
will detect the state of the button. The LED is there just to visualise the state of the button.
Now lets move to the sketch, and see how to enable the pull-up resistor. This resistor is, by
default, disabled.
Peter Dalmaris
Any pin that can be configured as input, contains a pull-up resistor in series.
void setup() {
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
} else {
digitalWrite(ledPin, HIGH);
}
}
As you can see, enabling the pull-up for an input pin is just a matter of passing the
appropriate configuration constant INPUT_PULLUP.
Conclusion
Internal pull-up resistors only apply to pins configured as inputs, and dont have any eect
in the output behaviour. Notice how the LED has a protective resistance in series? It would
be convenient to be able to replace it with an internal one, but that is technically not a
reasonable thing to do. Each LED or other load needs a current limiting resistor appropriate
for its characteristics, and it would be too expensive to come up with a way to create a
configurable resistance inside the micro-controller.
You can have a look at your micro-controllers data sheet to find out the exact value of its
pull-up resistors, if this is important for your design. For example, you may want to know
how much current will be flowing through the internal pull-up so you will need the exact
value of the resistor to do the current calculation.
A couple of terms relating to pull-up and down resistors are strong and weak. A strong
pull-up (or down) is a large resistor that only allows a small amount of current to flow
through it, while a weak pull-up (or down) is a smaller resistor that allows more current to
flow through it.
When you have a choice of a resistor to use for pull-up, you want to choose that that is
large enough to not allow too much current to flow through it (thats a waste of energy), but
you also dont want it to be too large as at some point you will end up with the same
Peter Dalmaris
problem you had at the very beginning: the voltage when the button is pressed will be hard
to determine if the current is insucient to raise a voltage.
Large pull-up resistors can also be too slow to respond to quick voltage changes, and in
high-speed circuits, like in USB serial communications. In USB circuits, you will typically
find pull-ups in the range of 1k to 4.7k (in our examples of external pull-ups we typically
use resistors between 50k and 60k).
Peter Dalmaris
Memory
In a couple of past lectures, namely lectures 64 on EEPROM, and 45 on the SD Card, we
had a quick look at the memory architecture of the Atmega micro-controller. We learned, for
example, that there are 3 types of memory that we have under our control (this table
contains a summary):
Volatile Use
Size (Arduino
Uno)
Life expectancy
Flash
No
Program
32KBytes
SRAM
Yes
Variable data
2KBytes
Very large
EEPROM
No
Variable/user data
1KByte
It is worth knowing that the memory architecture that the Atmega and most microcontrollers use is called Harvard architecture. A computer build based on this architecture
has a separate dedicated memory for the instructions (program) and for the program data,
and it looks like this:
Data memory
CPU
Instructions
memory
!
!
1
Peter Dalmaris
Your regular PC follows the Von Neumann architecture, where the same physical memory is
used for instructions (program) and data. While Harvards architecture can yield better
performance, Von Neumanns is more flexible for general purpose applications. Since
micro-controllers are meant to perform a particular task reliably and eciently, it makes
sense to design them based on the Harvard architecture.
In this lecture, well look at some design and programming principles you can follow to
make better use of your Arduinos limited memory resources.
Flash
!
But, if your sketch is at least one byte larger than that 32,256 available flash bytes of the
Arduino Uno, you will get this instead (see next page):
Peter Dalmaris
!
To generate this error, I simply found a sketch that required 31KBytes of Flash storage and
included a new library. The compiler will include this library in the machine code that it
produces even though there is not a single call to any of its methods.
So, how can one best manage the size of a sketch so that it only consumes the amount of
Flash memory it really needs, and no more?
Remove bloat
Just like in my example above, during prototyping it is often true that you will go from one
version of a sketch to another without remembering to clean up in the process. You could,
for example, create a function in version 1, which is not used in version 2. If you dont
remove it, the compiler will include it in the compiled sketch. Same thing applies to unused
libraries, variables and code in general.
So, try to keep your sketch lean by cleaning regularly as you move through its iterations.
Remove prints
We often use print and println functions to trace the operation of the sketch while we are
debugging it. Each print function consumes around 500 bytes of memory in flash. Often in
my sketches, I find so many references to println or print that removing them frees up 2 or 3
KBytes of flash memory.
Peter Dalmaris
Use constants
When you declare a variable and you know that its value is not going to change, you can
declare it as constant. A constant allows the compiler to optimise the way that it labels it
and references it in memory.
This is a technique for more experienced makers. It enables you to save almost 2.5KBytes
of flash memory.
!
!
Peter Dalmaris
SRAM
SRAM is much smaller than Flash, only 2KBytes in the Uno, and in real life it tends to cause
more problems. Heres a couple of examples:
* If you decide to use an SD card in your project, you will immediately dedicate 512 bytes
of SRAM as buer space required by the SD Card library.
* Want a monochrome LCD display? you need 1 byte for every 8 pixels, so depending on
the resolution of the screen you would be using up a large part of your SRAM capacity.
Lets have a look at things you can do to make ecient use of your available SRAM.
The Arduino provides several data types, of which most people end up using only 3 or four.
Knowing which one most eciently fulfils its intending purpose can save you a lot of
wasted space.
This table contains the available data types and the amount of space they consume in the
SRAM.
Type
Bytes
boolean
char
unsigned char
byte
int
unsigned int
word
long
unsigned long
short
float
double
4 (8 bytes on the
Due)
string
An array of chars,
so 2 bytes * number
of chars
Lets pretend that you are creating an array to hold these numbers: 1, 5, 25, 125.
Peter Dalmaris
Only use larger size data types like long and float if you have a very good reason. My self, I
rarely use them.
Avoid recursion
Recursion is a programming technique that in certain cases can simplify the programming
eort. Here is a typical example:
In PCs and computers with lots of memory, this is a good way of solving a problem that can
be broken down into small identical pieces. The trouble is that every recursion adds a full
copy of the function and its data to the program stack, which is stored in SRAM. The larger
the n is in this example, the deeper the stack will be, bringing the sketch closer to
exhausting its available memory.
The good news is that every recurve problem can be solved with simple iteration. This may
produce slightly messier code, but at least we will not have the memory stack problem.
Here is the factorial problem solved using iteration (see next page):
Peter Dalmaris
Some people believe that shorter variable names, like i or j, can result in smaller
memory footprint. This is not true, as the compiler will replace these mnemonic names
(meant to be read by the programmer, not the compiler) with labels that it computes
dynamically.
There is a handy macro, F(), that makes the compiler store strings in Flash memory instead
of SRAM. This macro is useful in cases where you have a call to print or println where the
string parameter does not change.
System.println(Hello world!);
can be changed into this:
System.println(F(Hello world!));
so that the string Hello world! is stored in Flash.
You can use the PROGMEM modifier to instruct the compiler to store data in Flash instead
of RAM. This is similar to the eect that the F() macro has on string.
PROGMEM doesnt work with all data types, only with those that the pgmspace library
supports. To use this modifier, start by including its library:
#include <avr/pgmspace.h>
then declare the variable and mark it with the PROGMEM keyword:
PROGMEM
prog_uint16_t charSet[]
Peter Dalmaris
Notice that the type of the charSet array is prog_uint16_t, not just int. If you wanted to
store a 1-byte number, you would use the prog_int8_t data type, and for a 2-byte number
you would use prog_int16_t.
charSet[0];
to access the first item of the array, because this would result in the micro-controller
looking for this data item in SRAM, not Flash. Instead, you need to use one of the functions
that the library provides:
pgm_read_word_near(charSet + 0);
and this will retrieve the data stored in the 1st item location of the array. If you wanted
the second item, you would say:
pgm_read_word_near(charSet + 1);
and so on.
Have a look at the documentation for pgmspace for details on all the data types and
functions that it provides.
There is a very useful library that you can include in your sketch so that you can profile its
memory use. Its called MemoryFree.
Install the library, include it, and use the freeMemory() function to get a reading of your
SRAM available memory. Place freeMemory() at strategic locations throughout your sketch
to get an understanding of how memory is used, and then when your prototype settles you
can remove it. This will release a bit of flash memory.
Peter Dalmaris
One thing to note is that freeMemory() will only report the amount of SRAM available that is
continuous. If SRAM is fragmented, it will report a lot less space available.
EEPROM
EEPROM was covered in detail in Lecture 64, please check it out if you havent done so
already!
Wrap it up
Memory is a limited resource on the Arduino, and as such it must be managed with care.
Unlike programming PCs, though, memory management on embedded devices comes
down to a few simple rules and habits. This lecture described the bulk of the things that you
can do to get the most out of your Arduinos memory.
Peter Dalmaris
Commonly, SSDs also contain a dot LED at the bottom right corner,
so they should probably be called 7+1 Segment Displays or
something like that.
In this lecture, we will build on our knowledge from Lecture 60, where
you learned how to use a shift register in order to control multiple
LEDs using only three wires: one for data, one for clock, and one for
the latch. Because there are so many SSD out there, I will assume
that you dont have your particular displays datasheet and have no idea which pin controls
the pins. I will describe the process of figuring out the correct wiring and then constructing
the numerical symbols in the sketch.
Wiring
Peter Dalmaris
supply to the Arduino via its barrel connector, and this can provide power to the breadboard
via the +5 pin.
The easiest way I have found to figure out what kind of SSD you
have, is you use a 1.5V button battery to probe that pins on the
SSD. To see how this works, take a 5mm LED and connect its
two pins to the battery, like I am showing in this image.
The long leg of the LED is the anode and needs to be connected
to the positive (+) side of the battery. The cathode is connected
to the negative side.
For the SSD, first have a look at the back. There are two rows of
pins. In both rows, the middle pins are either the anode or the
cathode. They are connected to each other, so it doesnt matter which one you end up
using. You can confirm that by testing for continuity with your multimeter, if you have one.
Now plug the SSD into the breadboard. Connect a red wire in
one of the two middle pins, and another wire to any of the
other pins, it doesnt matter which one. Test to see if the
middle pin is the anode by
connecting the red wire to the
positive side of the button
battery and the other wire to
the negative.
Peter Dalmaris
Notes on wiring
The shift register allows you to control 8 LEDs, since it can store 8
bits. We want to connect each LED to the shift register in an
organised way, because that will help us a lot with the sketch.
Once you finish with the bottom row of the SSD, continue
with the right-most pin of the top row. Connect that to pin 4
(Q4) on the IC. Continue moving towards the left, and connect the rest of the pins (except
for the middle common anode one) to pins 5, 6, and 7 on the IC. You are now finished with
connecting the SSD to the IC. Finish up with the SSD by connecting the common anode pin
to the 5V rail via a single 220 protective resistor (I do both top and bottom pins in order to
distribute the current, but you could connect either one or the other without risk).
Next, continue wiring the IC. You can find the details in Lecture 60, but in summary
remember that IC pin 14 goes to Arduino pin 8 (data pin), IC pin 12 goes to Arduino pin 9
(latch pin) and IC pin 13 goes to Arduino pin 10 ( clock pin).
You should now have a circuit that resembles the one in the schematic at the start of the
Wiring section.
Peter Dalmaris
Sketch
We will now create a sketch that once loaded, will implement a counter, that uses the SSD
to display numbers from 0 to 9.
This sketch will be based on the one we saw in Lecture 60, Demo 1. The dierence is that
in the new sketch we are about to create, we dont want the LEDs in the SSD to light up
randomly, we want it to show legible numbers.
Lets start with the skeleton of the sketch and build it up.
We will need ten such patterns, one pattern per numerical symbol (I will not be using the
dod in this sketch). The easiest data structure that I can think of that allows us to store ten
bit patterns is an array. I will start with declaring a default array of ten items that holds
bytes, and then tweak each byte separately to get it to display the symbol I want.
Peter Dalmaris
const byte
const byte
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111
};
CHAR_COUNT = 10;
symbols[CHAR_COUNT] = {
// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
For the time being, all bytes are 11111111. At the end of the setup function, add this
instruction:
writeLeds(symbols[0]);
and then run the sketch. The byte in the 0-index position of the array (the first cell) will be
sent to the shift register. Because its all 1s, and this is a common-anode SSD, all the LEDs
will be o. Now try to make a change, for example change the first bit of the first byte to 0,
so that you have this pattern:
B01111111
Run the sketch again, and you will see that LED G is now lit.
The first item in the array should represent a zero, so LED G should
be on. We need to find the bits that activate LEDs A, B, C, D, E and
F. Since we now know which bit controls LED G, we know that we
need to activate all others in order to create the zero symbol.
Edit the array so that the first cell contains this bit pattern and upload the sketch again. You
should see a zero symbol appearing on the SSD.
Lets try for a symbol 1, which is encoded by the second cell in the array. First, update the
setup function so that the call to writeLeds() is like this:
writeLeds(symbols[1]);
Now, try to find out which bits activate LED segments B and C. With a bit of trial and error,
you will figure it out. The pattern for 1 is B11101101. Segment B is controlled by bit 5
and segment C by bit 2.
Peter Dalmaris
After a few rounds of this method, you will have a table that shows the relation between
SSD segments and bits for each bit pattern. You can use pen and paper to make notes,
and it will eventually look something like this:
Bit #
Segment
DP
When you have completed this, your symbols array will look like this:
const byte
B10000001,
B11101101,
B01000011,
B01001001,
B00101101,
B00011001,
B00010001,
B11001101,
B00000001,
B00001001
};
symbols[CHAR_COUNT] = {
// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
With this, getting the count-up eect that we are looking for is simple. Here is the
completed sketch (next page), with comments embedded:
Peter Dalmaris
const
const
const
const
int latchPin = 9;
int clockPin = 10;
int dataPin = 8;
byte CHAR_COUNT = 10;
const byte
B10000001,
B11101101,
B01000011,
B01001001,
B00101101,
B00011001,
B00010001,
B11001101,
B00000001,
B00001001
};
symbols[CHAR_COUNT] = {
// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
void setup() {
pinMode(latchPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}
void loop() {
pattern1();
}
void pattern1()
{
int i=0;
while(i<CHAR_COUNT)
{
writeLeds(symbols[i]);
i = i+1;
delay(500);
}
}
Peter Dalmaris
!
Multiple seven segment displays
Alphanumerical displays
I will try to create a lecture that will demonstrate how to use the MM5450, although I think
that with a little eort, you will be able to work it out (hint: it really is just a shift register with
35 bits capacity).
Wrap it up
Working with seven segment displays is straight forward once you become familiar with
their principle of operation and the way you can interface them to the Arduino.
As an exercise, add a second SSD to your circuit, controlled by a second shift register. Wire
the second SSD to the second shift register in the same way you did for the first one, and
wire the second shift register to the first in a daisy-chain fashion like you learn in Lecture
60.
Peter Dalmaris
Adjust the sketch so that now it can count up from 00 to 99. If you do that, then you have
mastered the seven segment display!
Peter Dalmaris
But what about additional digital inputs? In this lecture, Ill show you how to use the
MCP23017 IC to easily add a total of 16 digital I/O pins to your Arduino project.
The MCP23017 is an integrated circuit that communicates with the outside world using the
I2C interface, and provides 16 pins that can be configured individually as digital inputs or
outputs. Using the I2C interface means that you can potentially add up to 128 of these
devices to your project, for a grand total of 128*16 = 2048 digital I/O pins.
Any kind of simple device that provides a digital input or output can be connected: buttons,
LEDs, relays, keypads, seven segment display, and much more.
Pins 15, 16, and 17 are used for setting up the I2C device address. You can set one of the
possible 128 I2C addresses by connecting these pins to +5V or Ground.
Pins 12 and 13 are used for the I2C communication. Pin 12 is the clock and 13 the data. On
the Arduino Uno, you connect these to analog pins 5 and 4 respectively.
Pin 18 is the reset. If you take this pin to LOW, it will reset the device. Well be keeping it to
HIGH (5V) via a10K pull up resistor.
Pins 19 and 20 are used for hardware interrupts. We will not be using these ports in this
lecture.
Pins 11 and 14 are not connected to anything, just leave them floating.
Peter Dalmaris
For this first demo, well get the port expander connected to the Arduino, and write a simple
sketch that sets one of the expanders pins as a digital out, connect an LED to it and make
it blink. Instead of an LED, you could use some other kind of digital load, like a small 5V
relay switch.
Peter Dalmaris
#include Wire.h"
void setup()
{
Wire.begin();
Wire.beginTransmission(0x20);
Wire.write(0x00);
Wire.write(0x00);
Wire.endTransmission();
}
Send the instructions and
close the connection.
void loop()
{
delay(100);
Wire.beginTransmission(0x20);
Wire.write(0x12);
Wire.write(B10000000);
Wire.endTransmission();
delay(100);
Wire.beginTransmission(0x20);
Wire.write(0x12);
Wire.write(B00000000);
Wire.endTransmission();
!
In a way, controlling the pins of the port expander as outputs is similar to using a shift
register. Just sent out a byte that contains the bit pattern that you want your outputs to
have.
In most cases you will only have one or two output devices youd like to control, and for
that you might not want to deal with bytes, registers and pin banks. Instead, you could this
convenient library from Adafruit. The library is Adafruit_MCP23017. Download it from
Github and install it in your IDE.
You can achieve that same output as the sketch above, like this (see next page):
Peter Dalmaris
#include <Wire.h>
#include "Adafruit_MCP23017.h"
Adafruit_MCP23017 mcp;
void setup() {
mcp.begin();
mcp.pinMode(7, OUTPUT);
}
void loop() {
delay(100);
mcp.digitalWrite(7, HIGH);
delay(100);
mcp.digitalWrite(7, LOW);
}
How about we try to control two LEDs, one connected to a pin in Bank A, and the other one
to a pin in Bank B?
In this demo, Ill show you how to control output pins in either Bank (A or B), either using
the lower-level Wire library or the Adafruit library.
Peter Dalmaris
#include "Wire.h"
void setup()
{
Wire.begin();
Wire.beginTransmission(0x20);
Wire.write(0x00);
Wire.write(0x00);
Wire.endTransmission();
Wire.beginTransmission(0x20);
Wire.write(0x01);
Wire.write(0x00);
Wire.endTransmission();
}
void loop()
{
delay(100);
Wire.beginTransmission(0x20);
Wire.write(0x12);
Wire.write(B10000000);
Wire.endTransmission();
Wire.beginTransmission(0x20);
Wire.write(0x13);
Wire.write(B00000001);
Wire.endTransmission();
delay(100);
Wire.beginTransmission(0x20);
Wire.write(0x12);
Wire.write(B00000000);
Wire.endTransmission();
Wire.beginTransmission(0x20);
Wire.write(0x13);
Wire.write(B00000000);
Wire.endTransmission();
}
Lets have a look at the equivalent sketch using the Adafruit library (next page):
Peter Dalmaris
#include <Wire.h>
#include "Adafruit_MCP23017.h"
Adafruit_MCP23017 mcp;
void setup() {
mcp.begin();
mcp.pinMode(8, OUTPUT);
mcp.pinMode(7, OUTPUT);
}
void loop() {
delay(100);
mcp.digitalWrite(8,
mcp.digitalWrite(7,
delay(100);
mcp.digitalWrite(8,
mcp.digitalWrite(7,
}
HIGH);
HIGH);
LOW);
LOW);
In this demo, well connect a button to one of the digital inputs. When the button is
pressed, an LED connected to a digital
output will be turned o.
Peter Dalmaris
#include <Wire.h>
#include "Adafruit_MCP23017.h"
Adafruit_MCP23017 mcp;
void setup() {
mcp.begin();
mcp.pinMode(7, OUTPUT);
mcp.pinMode(0, INPUT);
}
void loop() {
mcp.digitalWrite(7, mcp.digitalRead(0));
}
!
!
!
Wrap-up
Using the MCP23017 port expander is almost trivial. The Adafruit library hides all the lowlevel detail without adding much fat to the overall size of your sketch (it adds around 1,000
bytes to the amount of memory that the Wire library requires).
You could also use the MCP23017 as an I2C-based shift register and control 16 LEDs, or
an array of 8x8 LEDs. Just remember that each pin can provide a maximum of 15mA, so
you will need to consider your displays power requirements.
You could also control relays and through them much larger loads, like motors and lights,
and make this part of your home automation system.