I see two possible options for achieving what you want:
Voltage divider
This is the solution proposed by William Roy: you make a voltage divider
circuit and check whether it gives a voltage above the threshold of the
digital input. For example:
+5V ---+
|
R
|
+--- digital input
|
LDR
|
GND ---+
There are a couple of issues with this approach though:
Every digital input is fitted with a Schmitt trigger which makes
it hysteretic, meaning the voltage threshold for switching from LOW
to HIGH
is higher than the threshold for switching from HIGH
to
LOW
. If the pin voltage is between the two thresholds, it will read
either HIGH
or LOW
, as determined by the history of that voltage.
Depending on your application, this may not be a problem, and may
even be desirable. But if you want, as you wrote in your question, “a
specific set threshold of light”, it sure will be problematic.
You do not know what those threshold voltages are. The datasheet of
the ATmega328P (the main chip of the Uno) states that
they should “typically” be around 2.1 and 2.6 V, but it only
guarantees that the low threshold should be no less than 1.5 V
and the high threshold no more than 3 V. For any input voltage
between 1.5 and 3 V (which is a very significant range), the
chip manufacturer doesn't guarantee what the pin will read.
The first issue can be solved by using a write-before-read protocol:
first you set the pin to OUTPUT LOW, which will force the input Schmitt
trigger to the LOW state. Then you set it to INPUT and take a reading.
This way you will read HIGH if and only if the pin voltage is above
the high threshold. The low threshold becomes irrelevant. Example code:
/*
* Read the given pin avoiding the input hysteresis.
*
* Returns HIGH if the pin voltage is above the upper (LOW to HIGH)
* threshold, LOW otherwise.
*
* WARNING: The pin is set to OUTPUT LOW for a very short time.
*/
int anhystereticRead(uint8_t pin) {
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
pinMode(pin, INPUT);
return digitalRead(pin);
}
The second point can be solved by calibrating your input using a trimpot
instead of a resistor. You would adjust the pot until the threshold is
exactly what you want. Using fixed resistors instead of a pot will make
it very difficult to achieve your “specific set threshold of light”,
unless you can live with a very rough precision.
Capacitive measurement
The second option is use the LDR as the resistor in an RC circuit, and
measure the charging time of the circuit, like this:
digital output --- LDR ---+--- digital input
|
capacitor
|
GND
Starting with a discharged capacitor, you set the output to HIGH
and
measure how long it takes for the input to read HIGH
. This time is
typically something like 0.73 R C, where R is the
resistance of the LDR and C the capacitance of the capacitor. The
exact numerical factor is not know, as it depends on the high threshold
voltage of the digital pin.
For details and example code, you could check the Capacitance Meter and
RC Time Constants Arduino tutorial, or search the Web for
“Arduino capacitance meter”. You will find material about how to measure
an unknown capacitance using a know resistance, but you can use the same
technique to measure an unknown resistance using a known capacitance.
And you should replace the waiting loop:
while (analogRead(input) < THRESHOLD) {} // wait
by a similar loop using the digital input threshold:
while (digitalRead(input) == LOW) {} // wait
The interesting thing about this technique is that you do not need a
trimpot to set the illuminance threshold: you will measure the
charging time and set the threshold in software. Also, this technique is
not affected by the input hysteresis. The main drawback is that the
measurement takes longer than the voltage divider technique.
Edit: I found a blog post describing this exact method in greater
detail: Measuring resistance or voltage with 1 digital I/O,
by Moser.