Introduction: Arduino: Reading Analog Voltage
In this lesson you will use two resistors - a static resistor and a variable resistor - to create a voltage divider that enables you to effectively understand the intensity of light detected by the photoresistor - essentially a light meter. In the previous lesson you learned how to send OUTPUT and in this lesson you will learn to collect INPUT.
What you will need:
(1) Arduino Yun*
(1) 10k-Ohm 1/4 Watt resistor (Brown-Black-Orange)
*For this lesson series you are using an Arduino Yun. The reason for using the Yun (vs. other less expensive Arduino boards) is because in future lessons you will make use of the fact that the Yun has on-board Wi-Fi and a Linux distribution. Neither of those are relevant for this lesson, so if you have a different Arduino board (e.g. an Arduino Uno) you can use it. The ARDX Starter Kit for Arduino from Seeed Studio is a good kit with lots of parts (LEDs, resistors, servos, etc.), but it ships with an Arduino Uno instead of the Yun (the Uno doesn't have onboard Wi-Fi or the Linux distribution we will use in later lessons).
Step 1: Wiring a Voltage Divider
The first step is to wire up the Arduino to read voltage as determined by the resistance created by the photoresistor. You can simply wire your board according to the diagram (wire colors don't matter, but help with identification of purpose).
The A0-A5 pins on the Arduino enable you to read from or write to analog sensors, such as photoresistors, knobs (potentiometers), and temperature sensors. Here is the description of the analog pins from the Arduino website:
The Arduino board contains a 6 channel, 10-bit analog to digital converter. This means that it will map input voltages between 0 and 5 volts into integer values between 0 and 1023. This yields a resolution between readings of: 5 volts / 1024 units or, .0049 volts (4.9 mV) per unit.
A photoresistor, also known as light-dependent resistor (LDR) or a photocell, works by limiting the amount of voltage that passes through it based on the intensity of light detected. The resistance decreases as light input increases - in other words, the more light, the more voltage passes through the photoresistor.
In order to take advantage of the photoresistor you will create a voltage divider - a passive linear circuit that splits the input voltage amongst two or more components (similar to a Y-splitter).
To create the voltage divider needed for this lesson you will:
- Connect the voltage from the Arduino 5V pin (input voltage) to a circuit (using a breadboard).
- Connect the input voltage to a static resistor (10k Ohm)
- Establish a voltage divider coming out of the static resistor:
- One route to the analog pin (A0)
- One route to a variable resistor (the photoresistor)
In short, the more voltage to the A0 pin, the darker it is.
Here are the specific wiring instructions (see the breadboard image attached to this lesson):
Photoresistor
Insert a photoresistor into the breadboard as shown in the diagram.
Resistor
Connect a 10k-Ohm resistor from one side of the photoresistor across a couple of rows.
Wires
Connect the wires as shown in the diagram:
- Red:
- Connect the 5V pin to the red/positive side-rail on the breadboard.
- Connect the red/positive side-rail to the row where the resistor lead is connected but the photoresistor is not (this is the input voltage into the static resistor part of the voltage divider).
- Connect the row holding the other lead from the photoresistor to the black/negative side-rail on the breadboard.
- Connect the black/negative side-rail of the breadboard to the GND pin on the Arduino. This completes the circuit.
Note: You could connect the 5V pin directly to the same row as the lone lead of the static resistor and the GND directly to lone lead of the photoresistor, but I like building a habit of connecting the 5V and GND pins from the Arduino to the side rails. This will come in handy in the future lessons.
Step 2: Writing the Code
Using the Arduino IDE create a new sketch. The new sketch has two stubbed out methods.
void setup() { // put your setup code here, to run once: } void loop() { // put your main code here, to run repeatedly: }
Prior to the setup method you will declare a variable for the analog pin that is connected to the photoresistor.
//Photoresistor Pin int analogPin = 0; void setup() { // put your setup code here, to run once: }
As you recall from the previous lesson, the setup method runs once when the firmware starts. For this lesson the goal is to write the voltage value coming from the photoresistor to the serial monitor. In order to do that you will start the serial monitor using the Serial.begin method and pass in the baud rate (bits per second).
void setup() { // put your setup code here, to run once: Serial.begin(9600); }
Next you will write the code to read the raw data coming in on A0 (remember it will be a value between 0 and 1023 which is 1024 steps or units) and convert it to a voltage reading (0.0V to 5.0V).
void loop() { // put your main code here, to run repeatedly: // read the raw data coming in on analog pin 0: int lightLevel = analogRead(analogPin); // Convert the raw data value (0 - 1023) to voltage (0.0V - 5.0V): float voltage = lightLevel * (5.0 / 1024.0); // write the voltage value to the serial monitor: Serial.println(voltage); }
Step 3: Upload the Firmware
Before uploading the firmware it is always a good idea to verify the board targeted and the port connected (if you don't remember this, check the previous lesson).
Press the Upload button to compile the firmware and send it to the Arduino.
Step 4: Open the Serial Monitor
After the sketch has compiled and uploaded to the Arduino, click on the magnifying glass icon in the upper-right of the Arduino IDE. This will open the Serial Monitor.
Step 5: Read Voltage in Serial Monitor
While the firmware is running and the Serial Monitor is open you will see the data being read from the photoresistor (note: in the image above I used an Arduino Uno, not a Yun). While the firmware is running and you are seeing data in the serial monitor, try covering the photoresistor (thus decreasing the light and increasing the resistance from the photoresistor and pushing more voltage to pin A0) or shining a light on the photoresistor (thus increasing the light and decreasing the resistance from the photoresistor and allowing more voltage through to ground (effectively stealing voltage away from the A0 pin).
Congratulations! You have made your first device that responds to its environment, you learned about a voltage divider, how to read data from an input sensor, and how to use the serial monitor.
6 Comments
8 years ago on Introduction
"This yields a resolution between readings of: 5 volts / 1024 units"
float voltage = lightLevel * (5.0 / 1024.0);
not 1023! 1023 is the highest value, but 1024 is your divisor. The highest value of a 10 bit a/d is 1023/1024 times the reference. Note that a 0 reading means the input is between 0 and .0049 v, and 1023 means the input is between 4.995 and 5.000 volts.
Reply 3 years ago
I believe this is incorrect.
Arduino would never receive "1024" as digital representation since the max of 10-bits is (11 1111 1111) which is 1023. Therefore, when you write:
float voltage = lightLevel * (5.0 / 1023.0);
and 5V is the input voltage , it will be represented by 1023 not 1024 counts.
Reply 3 years ago
I understand why you, and many others, think so. But it is wrong. The divisor is 1024 not 1023.
In this instructable: https://www.instructables.com/id/Arduino-Reading-Analog-Voltage/
he computes the voltage as:
float voltage = lightLevel * (5.0 / 1024.0);
Serial.println(voltage);
This *is* correct.
Let me give you a simpler example to help understand it. Imagine a 3-bit A/D with a 1 volt maximum. When you read it, you have 8 possible values:
000 or 0 representing 0 volts
001 or 1 representing 1/8 volt
010 or 2 representing 2/8 or 1/4 volt
011 or 3 representing 3/8 volt
100 or 4 representing 4/8 or 1/2 volt
101 or 5 representing 5/8 volt
110 or 6 representing 6/8 or 3/4 volt
111 or 7 representing 7/8 volt.
EACH LEVEL REPRESENTS ANOTHER 1/8 OF A VOLT. NOT 1/7!
The level for 110 or 6 is 6/8 not 6/7.
Thus, if you read a value of "X" from this A/D, it represents X/8 not X/7 volts.
And likewise with a 10 bit system each level represents 1/1024th of a volt NOT 1/1023, and the code is X/1024.0 not X/1023.0.
Perhaps the confusion arises because we start numbering from 0. In the series (0,1,2,3,4,5,6,7) there are EIGHT numbers, not seven. Each represents 1/8th of the total. In the series (0,1,2,3,...,1022,1023) there are 1024 numbers.
When there are 1024 different things, each one must represent 1/1024th of the total.
Another way to see is is by realizing that these A/D values actually represent a RANGE of voltages.
Go back to my table at the top of this reply. Where it says
000 or 0 representing 0 volts
realize that 000 actually represents ANY voltage if the range from 0 to a hair beneath 1/8. Likewise, 001 represents any voltage from 1/8 to a hair beneath 1/4. And so on. And thus, 111 or 7 represents any voltage from 7/8 to 1.
On a 5v system like Arduino, any voltage from 4.995 up to 5.0 will return 1023. Thus, the code from the instructable referenced above could be more interestingly written:
float v1 = lightLevel * 5.0 / 1024.0
float v2 = (lightLevel+1) * 5.0 / 1024.0
Serial.println(" voltage is somewhere between " , v1 , " and " , v2 , " volts.")
Hope that helps. Divide by 1024 not 1023 in all A/D or D/A computations.
Reply 3 years ago
Hi,
Although you illustrated it in a beautiful way, you seem not to try Arduino at all to verify that assumption.
The ADC stores the digital count in 10-bit format. So the max count to fill all bits is (11 1111 1111) = 1023. Although you would have 1024 counts, the max value is 1023. Now for the code
voltage = DigitalCount * (5.0/1023.0);
here DigitalCount --> analogRead(A0) varies from 0-1023. If you divide by 1024, you would never get 5V. Instead, you will get an incorrect voltage of 4.9951171875 V which is plausible since ADC can not display 1024 while it is 10 bit
To verify this, I made a simple simulation using Proteus with the following code:
#include <TimerOne.h>
int PotPin = A0;
float DigitalValue = 0, Voltage = 0;
void TakeSample()
{
DigitalValue = analogRead(PotPin);
Voltage = DigitalValue * (5.0 / 1023);
Serial1.print(" Digital Value: " + String(DigitalValue) + " count");
Serial.print("\t");
Serial1.println(" Voltage: " + String(Voltage) + " V");
}
void setup()
{
Serial1.begin(115200);
Timer1.initialize(1000000);
Timer1.attachInterrupt(TakeSample);
}
void loop() {}
The attached image shows the result as I claim. Have a great day
Reply 3 years ago
You are correct, the max count is 1023. But you are still wrong, the divisor is 1024. And you will never print "5.0", the max you can correctly print is 5*(1023/1024) = "4.994". While that seems (and is) odd, it is nevertheless quite true.
I suggest you take a good look at a hypothetical 3-bit 1-volt system. The values will be (0,1,2,3,4,5,6, and 7). According to you, each represents a step of 1/7th volt. According to me, each represents a step of 1/8th volt.
Given those 8 values, printing value/8, a voltage rising from 0 to 1 volt will print 0.00, 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, and 0.875, and never print 1.000. That is, 0, 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, and never 8/8 or 1.
In your system, printing value/7 for incoming values of 0,1,2,3,4,5,6, and 7 would print 0, 0.143, 0.286, 0.429, 0.571, 0.714, 0.857, and 1.000. That is, 0, 1/7, 2/7, 3/7, 4/7, 5/7, 6/7, and 7/7.
I say those values are not correct.
Look at this graph:
https://microchipdeveloper.com/adc:adc-quantizatio...
This is standard, and you can google many more like it. Note that the voltage corresponding to each step is 1/8th the max or 0.25V, not 1/7th the max. And each voltage value across the bottom is 2.0*(Y/8) where Y is the vertical axis of binary values. The last step, 111, starts at 1.75 volts. Which is (7/8)*2.0.
The numbers across the bottom are multiplies of 1/8th of 2.0. You would have them be multiples of 1/7th x 2.0.
I'm not gonna belabor the point anymore, you are welcome to use 1023. I have no more to offer, so won't reply anymore.
8 years ago
StevenS10 - good catch. The lightLevel should be multiplied by the voltage/units where the units is 1024 (0-1023 is 1024 units or steps). I'll correct the lesson.