Whilst digital inputs can only be on or off, analogue inputs can have a range of values. The command for reading these values is analogRead() (note the American spelling).
This command still measures the voltage at the input pin, but instead of just deciding if it is HIGH or LOW, it returns a representation of the actual value of the voltage (as a scaled proportion of the total range available).
On the Arduino the voltage range at the input pin is 0-5V, and the value returned by the command is a number between 0 and 1023. This means 0 represents 0V and 1023 represents 5V. All other values are scaled in this range.
This is best seen in the following graph, although obviously only whole number values can be returned by the command.
Ex4_1_PotSerial
We have two simple analogue inputs on our Tutorial Board, the LDR (connected to pin A3) and the potentiometer (connected to A1). We will start off by reading from the potentiometer (also known as a pot for short).
Open a blank program.
Here is the sequence of things your program will need to do:
Define a constant integer called "pot" for pin A1
Define a variable integer called "value" for storing the reading
Setup Serial communication in the setup(){}. Look back at Ex3_4_HelloWorld if you need
Each time the loop(){} loops, you want to update "value" with the current value of the potentiometer. So, have the following as the first line in the loop(){}:
value = analogRead(pot);
Then, compose the following message to be sent over the Serial monitor:
"The current value at analogue input A1 is: X"
where X is whatever is currently stored in "value". Look back at Ex3_5_Blink5TimesSerial if you are stuck with this.
Finally, add a short delay:
delay(1000);
Upload the program to the board, then check out the Serial Monitor to see what happens as you twist the potentiometer. Explore the full range of values, noting that it only updates every second.
Paste the program in your Google doc under Ex4_1_PotSerial.
Ex4_2_CylonPotDelay
Start by opening up Ex1_3_Cylon. You'll remember that the delay is a fixed number, but you can now control it!
Define a constant called "pot" for A1
Define an integer called "value"
In the setup(){} make a for(){} loop that makes Pins 1-8 outputs. Look at Ex3_3_ProgressBarVariable if you've forgotten how to do this.
In the loop(){}, make "value" equal to the value read from the pot.
Make a for(){} loop that runs from 1 to 8 in steps of 1. Have two digitalWrite() commands inside the for(){} that turn pin i on, then off. This looks a lot like the loops in Ex3_3_ProgressBarVariable, except you want to turn the LED off so you have a Cylon effect.
In between the digitalWrite() commands have a delay() the length of which is "value" (without quotation marks).
You can run a halfway test and see that the LEDs sweep from 1 to 8. Now, you need them to sweep back to get the Cylon effect.
Still in the loop(){}, make another for(){} loop that runs from 8 to 1 in steps of 1. Think carefully about the starting value of int i, the value you count DOWN to, and whether the values i takes in between are larger or smaller than the final value.
The commands inside the second for(){} loop are the same as the first: two digitalWrite() with a delay() between.
Your program should look like cylon, but with a twist!
Q4.1. Why did you have to make two for loops rather than one?
Save and paste the program in your Google Doc under Ex4_2_CylonPotDelay.
Ex4_3_MapPot
Often it is useful to convert numbers from one range to another. This is particularly true when reading from an analogue input, which produces a number between 0 and 1023, when we might want a number between 0 and 8, or 0 and 255.
There is a useful function in the Arduino programming environment called map(), which does exactly this. It appears in the form: map(variable, current min, current max, new min, new max) and essentially returns an integer representation of the number in variable (which originally ranged from current min to current max) within the range new min to new max.
Here is an example using the value that has just been read from an analogRead() command (and therefore ranges from 0 to 1023):
ledLevel = map(value, 0, 1023, 0, 255);
This will store a value in the integer ledLevel between 0 and 255 which represents the number in value that ranged from 0 to 1023. Hopefully you can see that we have changed the range to that of the analogWrite() command. This will allow us to have the brightness of an LED controlled by the potentiometer in the next program.
For now, let's make sure that the we can get the map() command to work properly.
Copy your Ex4_1_PotSerial code into the TinkerCad tutorial board.
Define another integer called "mappedValue".
In the loop(){} create a new line after the one where you read the value of the pot and before the serial print.
Q4.2. If you simply wanted "mappedValue" to take the value of "value", what line of code would you write?
What you do want is to map the range of "value" (0-1023) onto a new range (0-255) for "mappedValue". The code looks like this:
mappedValue = map(value, 0, 1023, 0, 255);
Upload your program, open the serial monitor and check the range of values read from the potentiometer.
Save and paste your work in your notebook under Ex4_3_MapPot.
Ex4_4_BrightnessControl
Having confirmed that the mapping command works, let's put it to use to make something you may be useful: as it gets darker, a light gets brighter. We can use the value read from the LDR to control the brightness of an LED. Custom routines
Open up Ex4_3_MapPot.
We will not need any Serial communications, so you can delete all of that aspect of the program from the setup(){} and loop(){}
Next, add the ledPin constant definition for pin 3 before the setup.
Use pinMode() to make it an OUTPUT
Change all references from "pot" to "ldr", and change the pin to A3
After the mapping command add an analogWrite() command where ledPin is set to a brightness of "mappedValue"
Think carefully about how the mapping function works; do you want no light on the LDR to translate into 0 brightness on the LED?
Download the program and see what happens as you cover the LDR in shadow (squishing the LDR with your thumb doesn’t work very well - use your whole palm to cover it up).
Save and paste work in your notebook under Ex4_4_BrightnessControl before you move on.
Ex4_5_ProgressBarRoutines
When writing large and complex programs it becomes impractical to simply include all of the code in the setup(){} and loop(){} sections of the program. It would quickly become very hard to follow what was going on, and tricky to debug.
The way to solve this problem is to break the program down into smaller more manageable chunks, and code and test each of the, separately. We do this using routines. You have already seen an example of this, as the setup(){} and loop(){} are both routines. We can write our own routines to help split our program into chunks.
We're going to start out with a simple example. Open up Ex3_3_ProgressBarVariable.
To start off with we are just going to create some procedures. These are identified by the keyword void at the start.
At the bottom of the code (after the last } that closes the loop(){) insert the following:
void initPins(){
}
void allOn(){
}
void allOff(){
}
As per usual, choose names that describe what the custom procedures do: the pins will be initialised as outputs in initPins(){}, all the LEDs will be turned on in allOn(){} and off in allOff(){}. As with any other procedures, code goes between the { and }.
Cut and paste the code from the setup(){} procedure into your new procedure initPins()
Cut and paste each of the for(){} loops into the corresponding allOn(){} and allOff(){} procedures.
Finally we need to "call" these procedures, so add the following into the now empty setup(){} procedure:
initPins();
And add this into the now empty loop(){}:
allOn();
allOff();
Save by pasting the program in your Google Doc under Ex4_5_ProgressBarRoutines.
Hopefully you can see how this has greatly simplified the setup() and loop() procedures, and that if we were building a program that performed a large number of different tasks, how this could be useful.
The added advantage of this process of dividing up our code into routines is that routines can be re-used from program to program, and can be developed by other people, and then copied into the bottom of our programs. In fact we can go one stage further than this, and we can bundle together a whole set of procedures and functions into what is called a library, and we will see how this works for the RGB LED and LCD screen.
Ex4_6_StreetLight
LDRs used to be how streetlights used to come in when it got dark (they are now turned on via a radio signal)
Open up Ex4_4_BrightnessControl .
Delete the map() and analogWrite() commands.
Below the loop(), create a procedure called checkLevel(){}, and fill it with the following code:
if(value<600) digitalWrite(ledPin, HIGH);
else digitalWrite(ledPin, LOW);
Call this procedure after the analogRead().
Download the program and see what happens as you cover the LDR in shadow.
You have now also been introduced to the second half of the if() statement: the else() statement. It was there when you wrote Ex2_7_ButtonCheck, but leaving it blank is the same as omitting it altogether. It does exactly what it says on the tin: tells the tutorial board what to do when the parameter inside the if() statement is false. In this case, when the light level on the LDR is below 600, it turns ledPin off.
Q4.3. Go back to Ex2_7_ButtonCheck and, after the if() statement, add the else statement from above. What happens now when you press the button? Why?
We can also use this mapping functionality to display a kind of bar graph using the LEDs. Probably best start from scratch so as not to confuse yourself and save the program Ex4_7_PotBarGraph.
If we map to 8 levels instead of the 255 in the last ex:
ledLevel = map(value, 0, 1023, 0, 8);
We have now limited the ledLevel to 0-8.
Write a program that maps the position of the potentiometer onto LEDs 1-8 so no LEDs are on if the pot is at 0 and all 8 are on when it is at 1023. This program uses everything you've learned so far:
Custom procedures to initialise LEDs and display the LEDs
Store the analogue value of the pot in a variable
Map it onto the number of LEDs
A for() loop to turn on anywhere between 1 and 8 LEDs
An if() statement to determine how many LEDs to turn on
An else() statement to turn no LEDs on
Do some simple maths in Arduino! Read the pot and LDR, add up their values and display it over the serial monitor.
Create two variables (variable1 and variable2)
Create two constants integers "pot" and "LDR" and set them equal to A1 and A3
Set up the serial connection in the setup()
In the loop(), set variable1 and 2 equal to the values read from the potentiometer and LDR
Send the following message to the serial monitor "The sum of the pot and LDR values are: X", where X is the sum of variable1+variable2. Remember, you can't mix strings and integers, so it will have to be two serial print commands.
Write a program where LED 5 dims according to the brightness in the room and LED 6 turns on if the potentiometer value is below 500. These are two of the programs from the lesson put together. Some hints:
create two constant integers ledPin1 and ledPin2 that refer to LEDs 5 and 6
create two constant integers pot and LDR that refer to A1 and A3
create two variables called valueLDR and valuePot
make ledPin 1 and 2 outputs
read the values of the pot and LDR into valuePot and valueLDR. Make sure these get updated all the time
Map value2 so that it's range is 0-255
Create a custom procedure called checkLevelPot where LED 6 turns on if value 1 is less than 500, else LED 6 is off
Create a custom procedure called checkLevelLDR where the MAPPED value2 is the brightness of LED 5.
Make sure you call both custom procedures in the loop(){}
Combine Ex4_4_BrightnessControland Ex4_7_PotBarGraph so that if you press A4 the brightness of LED 3 varies according to the LDR value and if you press A5 the bar graph effect occurs according to the pot value. Nothing should happen until either of the buttons is pressed. This is where custom procedures really come into their own as you need to think carefully about how to organise your code.