Maker Advent Calendar Day #6: Looking for Light!
Welcome to day six of your 12 Projects of Codemas Advent Calendar. Today we’ll be using a light sensor which - you guessed it - can detect the amount of light around it and provide us with a reading value which we can use in our code.
The light sensor we're using is another analogue component, which means it's able to provide a nice range of values to give us really granular light readings for our projects.
Box #6 Contents
In this box you will find:
- 1x Photo Transistor
- 1x 10k Resistor
- 3x Male to male jumper wires
Today we'll be learning how to take analogue readings from our light sensor and converting those into something more useful, as well as some other new tricks to manipulate data and strings. We'll also make a light indication project with our LEDs.
Sensors similar to these are used everywhere in the world around us - in street lamps, vehicles, room lighting and more. They detect the amount of light, and this determines the amount of electrical current that is passed across the the legs (and therefore into our Pico as an input).
A great one to learn and use in many projects!
Construct the Circuit
We're going to use the LEDs again and also the buzzer, so leave them in place and remove everything else. Your circuit should look like this to start with:
Before we start with today's circuit we must stress that you need to pay close attention to the legs of the light sensor - if you get the wiring wrong here, your light readings won't make a lot of sense!
With that in mind, grab your mini breadboard and fit the sensor with the long leg to the left. It should look like this:
Now we need to add three wires and the resistor:
- Connect the right (short) leg to the 3.3V pin (physical pin 36)
- Connect the left (long) leg to GPIO 26 (physical pin 31)
- Add the resistor to connect the left leg over to another empty unused lane on your breadboard
- Connect the other end of the resistor to a GND pin on your Pico (we're using the blue lane as it's already connected to GND for our LEDs)
Your circuit should look like this:
Activity 1: Simple Light Sensing
Let's start with a simple starter program which grabs a light reading for us.
This is just a minimal program to return the analogue value once, which isn't all that useful to humans but we'll convert it to something more practical in the next activity.
In this example we:
- Import ADC to use the ADC function
- Setup the sensor GPIO pin as an ADC pin
- Create a variable called 'light' which reads the sensor value
- Print the variable value (the sensor reading)
Copy the code below over to Thonny and give it a try:
# Imports from machine import ADC, Pin # Define pin for our sensor lightsensor = ADC(Pin(26)) # Read sensor value and store it in a variable called 'light' light = lightsensor.read_u16() # print the value print(light)
Activity 2: Making Light Readings Usable
We know we can grab a single reading from the sensor with a very short program, but we really need that reading to be in a format that we can use and recognise.
In the example below we convert the reading to a percentage using a little maths. This will come in handy in one of our later boxes when we...oops nearly dropped a spoiler there!
We'll show a few examples as there are some other tricks we can use to make our printed values even more useful...
This example takes the reading and turns it into a percentage.
It divides our reading by 65535 (the maximum of the analogue reading range) then multiplies this by 100 to give us our percentage value:
# Imports from machine import ADC, Pin from time import sleep # Define pin for our sensor lightsensor = ADC(Pin(26)) # Read sensor value and store it in a variable called 'light' light = lightsensor.read_u16() # Turn our reading into a percentage of the analogue range lightpercent = light/65535*100 print(lightpercent)
This example builds on the previous one by limiting the number of decimal places, as we don't really need that much detail for something like a light reading.
To do this we use the built-in 'round' function in our percentage calculation. You'll see this below where we use round(light/65535*100,1).
This is taking the light reading, applying the same percentage calculation, and then the final number (1) defines how many decimal places to use.
Try the example below, then try changing that last number to 2 or 3 to see what happens (and then try removing the comma and number altogether, which will remove the decimal):
# Imports from machine import ADC, Pin from time import sleep # Define pin for our sensor lightsensor = ADC(Pin(26)) # Read sensor value and store it in a variable called 'light' light = lightsensor.read_u16() # Use the round function to limit the decimal places to 1 lightpercent = round(light/65535*100,1) print(lightpercent)
Our final example adds the % symbol after the reading. We do this by adding some parts to our print line.
Normally when we print the reading, the value is a number (in our example it's a float to be exact, as it has a decimal). However if we want to print that value along with text at the same time (the % symbol), we need to convert everything to a string (text). The print function will not let us mix both together!
We can do this by using the built-in str function, which converts values to strings.
We use str(lightpercent) to convert our light value reading to a string, then add another string next to this with +"%". You can see this in the example below - give it a try:
# Imports from machine import ADC, Pin from time import sleep # Define pin for our sensor lightsensor = ADC(Pin(26)) # Read sensor value and store it in a variable called 'light' light = lightsensor.read_u16() # Use the round function to limit the decimal places to 1 lightpercent = round(light/65535*100,1) # Print our reading and the % symbol # Convert the reading to a string first using str print(str(lightpercent) +"%")
Activity 3: Light level LED indicators
We've covered grabbing readings and converting the values, so let's put that to work with a nice little light indication project using our LEDs.
We're going to run an example where we continually check the light readings, and assign our three LEDs to a range of readings - red will indicate a low light level, amber for normal and green for high light levels.
In the example below, we add our LEDs back into our code and define the pin numbers. We also define the light sensor pin before we start a while loop.
The while loop grabs a reading from the sensor and converts this to a percentage like we did earlier, then uses if statements to light a different LED depending on the percentage reading (similar to what we did on day #4).
Copy this over to Thonny, run the code then use your phone's camera light, a torch or simply turn your lights off to see the changes with the program.
Once complete, why not try adding the buzzer code from yesterday's box back in to alert us when the light goes below 30%?
# Imports from machine import ADC, Pin import time # Set up the LED pins red = Pin(18, Pin.OUT) amber = Pin(19, Pin.OUT) green = Pin(20, Pin.OUT) # Define pin for our sensor lightsensor = ADC(Pin(26)) while True: # Run forever # Read sensor value and store it in a variable called 'light' light = lightsensor.read_u16() # Use the round function to limit the decimal places to 1 lightpercent = round(light/65535*100,1) # Print our reading percentage with % symbol print(str(lightpercent) +"%") # 1 second delay between readings time.sleep(1) if lightpercent <= 30: # If percentage is less than or equal to 30 red.value(1) # Red LED on amber.value(0) green.value(0) elif 30 < lightpercent < 60: # If percentage is between 30 and 60 red.value(0) amber.value(1) # Amber LED on green.value(0) elif lightpercent >= 60: # If percentage is greater than or equal to 60 red.value(0) amber.value(0) green.value(1) # Green LED on
Day #6 Complete!
Good job makers! We've learnt how to use another sensor today, as well as introducing some new ways to manipulate values and strings.
We're going to use this sensor again before the end of the calendar as it's a great partner for a component hiding in one of the other boxes...
So, what did we learn today? Today we:
- Learnt how to wire a light sensor circuit
- Learnt how to manipulate data to make it more useful to us and our programs
- Learnt how to convert values to strings with the str function
- Learnt how use the round function to limit decimal places
- Learnt how to combine multiple strings in prints
As always, please leave the circuit as it is and wait for tomorrow's box before you remove any parts. Sleep well!
We used Fritzing to create the breadboard wiring diagram images for this page.