Introduction: Arduino: Input Controls Output
In this lesson you will combine the ideas that you learned in the previous lessons (here and here) to make a LED change its brightness based on the level of light available - essentially a nightlight. You will learn a couple of new methods you can use and we'll discuss Pulse Wave Modulation (PWM) briefly.
What you will need (all the parts from the previous lessons):
(1) Arduino Yun*
(1) 5mm Green LED
(1) 560-Ohm 1/4 Watt resistor (Green-Blue-Brown)
(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 the Arduino
This lessons combines the previous two lessons, so its likely that you already have most of the wiring done from the previous lesson. Wire the Arduino according to the picture at the beginning of this lesson.
Resistors
The 10k Ohm resistor is one part of the voltage divider, working in partnership with the photoresistor.
The 560 Ohm resistor connects to the negative (shorter) lead of the LED.
Wires
This is where building a habit of connecting positive (5V) and negative (GND) pins from the Arduino to the breadboard side-rails starts to pay off. In this lesson the pin coming from GND to the negative side-rail supports both the photoresistor circuit and the LED circuit.
Step 2: Using Pulse Width Modulation (PWM)
Pulse Width Modulation (PWM) is a technique for simulating analog values on a digital pin. There are several digital pins on the Arduino that support PWM depending on the board you are using. For example, the Yun we are using in this workshop supports 8-bit (0-255) PWM on digital pins 3, 5, 6, 9, 10, 11, and 13 using the analogWrite() function.
PWM simulates analog data by creating a square wave (basically a repeating switch between on and off) where the duration of 'on' time is the pulse width. If the square wave has a 50% pulse width (more commonly known as a duty cycle), then the output from that pin is equal amounts of on and off. If the duty cycle is 25% then the output from the pin will be off for three times as long as it is on (25% on, 75% off).
Because the time windows of a cycle is too fast for the human eye to perceive (about 2 milliseconds on the Arduino pins that support PWM), instead of causing an LED to strobe or flicker, it simply appears to be more or less bright. Using a 25% duty cycle the LED would be on (high output) for half a millisecond and off for 1.5 milliseconds which makes the LED appear to be at 25% brightness. So while we aren't truly sending analog data to a digital LED, we are using PWM to simulate the effect of analog data.
Step 3: Writing the Code
Using the Arduino IDE create a new sketch.
Prior to the setup() function, declare variables for the analog pin connected to the photoresistor and the digital pin connected to the LED.
//Photoresistor Pin
int analogPin = 0; // LED Pin int ledPin = 9;
For this firmware you don't need to put anything into the setup method. Normally you would declare the digital pin as OUTPUT using the pinMode() function, however, we are going to be using the analogWrite() function, whihc doesn't require the pin to be declared as OUTPUT.
In the loop() function you will start by reading in the light level from the photoresistor using the same code from the last lesson.
void loop() {
// put your main code here, to run repeatedly: // read the raw data coming in on analog pin 0: int lightLevel = analogRead(analogPin); }
Next you will convert the incoming light level to the correct range for the LED. To do this you will use two new functions - map() and constrain().
The map function has five input arguments - the value, a low value and a high value from the current range, and a low value and a high value for the target range.
int val = map(value, fromLow, fromHigh, toLow, toHigh);
All this does is re-map a value from one range to the equivalent value in another range. For example, map(25, 0, 50, 0, 100) would return 50 (50 is the equivalent within the 0-100 range to 25 within the 0-50 range). In this lesson you will map the lightLevel (a 0-1023 range) to a PWM range (0-255). Another way of accompishing the same goal in this instance would be to divide the lightLevel value by 4 (0-255 is on fourth of 0-1023), but if you did that, you wouldn't get to learn the map() function. Additionally, because you don't want your nightlight to be on when there is some light (e.g. low light) you will want to modify the lightLevel range. For example, in my office the lightLevel value is around 340 when the lights are on (its not a very bright office). I would like to treat this as though there is enough light that the LED should not be lit, so I am not burning the LED when the lights are on. To do this, I map the lightLevel range as 350-1023.
void loop() {
// put your main code here, to run repeatedly: // read the raw data coming in on analog pin 0: int lightLevel = analogRead(analogPin); // re-map the value from the analog pin to a smaller range // Experiment with the right fromLow level based on your environment int brightness = map(lightLevel, 350, 1023, 0, 255); }
You will have to experiment with this to find the best range to use (remember, you can use the serial monitor from the previous lesson to find the raw value coming from the photoresistor).
Mapping the lightLevel range using a low value that is higher than a value that you might be inputting could result in a negative number (e.g. in my example, the lightLevel could be 340 even though I identified the low end of the range at 350). To protect against setting brightness to a negative number you can use the constrain() function.
The constrain() function takes in three input arguments - the value, a low value and a high value.
int val = constrain(value, lowValue, highValue);
If the value is between the lowValue and the highValue, then the value will be returned. If the value is lower the lowValue, then lowValue will be returned. If value is higher than highValue, then highValue will be returned.
Using the constrain() function you can ensure you don't set brightness to a bad value. Now that you have a good brightness value you can use the analogWrite() function to set the PWM for the LED pin. A brightness value of 0 is a 0% duty cycle (off) and a brightness value of 127 is a 50% duty cycle, etc.
void loop() {
// put your main code here, to run repeatedly: // read the raw data coming in on analog pin 0: int lightLevel = analogRead(analogPin); // re-map the value from the analog pin to a smaller range // Experiment with the right fromLow level based on your environment int brightness = map(lightLevel, 350, 1023, 0, 255); // use constrain to avoid negative numbers and/or numbers above the high range brightness = constrain(brightness, 0, 255); // use analogWrite to send PWM data (a square wave) analogWrite(ledPin, brightness); }
Step 4: 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 first lesson).
Press the Upload button to compile the firmware and send it to the Arduino.
If your mapping of the ranges was done right the LED should be off. If you cover the photoresistor with your hand you should see the LED light up. The more you cover the photoresistor the brighter the LED will glow.
Congratulations! You made a device that both detects its environment and responds to it. You learned about Pulse Width Modulation and the analogWrite() function for simulating analog behavior on a digital device, and you learned how to use the map() and constrain() functions.