Archive for November 28th, 2009
The general idea is a simple light sensor that can cope with typical indoor illumination, brightening and dimming the digits on a clock so that it’s both visible in the daylight and doesn’t light up the room at night.
This circuit produces a voltage that varies more-or-less inversely with the photoresistance R. The “decade resistor” DR acts as a range selector: the output voltage will be 2.5 V when DR = R.
Yesterday I doodled about the voltage-vs-resistance curves and how DR works, showing the equations that spit out V when you know R, which is handy from a circuit-analysis standpoint (if you can say that with a straight face for a two-resistor circuit).
What you want is the equation that spits out R when you know V, because you can actually measure V. Rearranging the equation in the doodle above produces that equation
R = DR * (5 – V) / V
Actually, what you really want is log R, because your sensation of brightness varies logarithmically with the illumination intensity: each doubling of intensity makes the scenery twice as bright. So you could find R, apply a floating-point math package to it, and come up with log R.
There’s a better way.
Stand yesterday’s graph on its ear and flip it side-to-side to get this view of the same data points. It’s plotted with a log scale on the Y axis, because the resistance varies over such a huge range.
The dots represent the values of R produced by the equation above with DR = 1 kΩ. Those are the actual resistance values, at least according to the circuit model.
The midsection of those dots is pretty straight, so the diagonal line second from the bottom is a straight line “curve fit” with intercepts at
(0 V, log 10000)
(5 V, log 100)
The y = mx + b equation fitting that line is
log R = (-2/5) * V + 4
where the (-2/5) comes from the slope of the line:
(log 10000 – log 100) / (0 – 5)
and the (+ 4) comes from the intercept at (log 10000), as set by the value of DR. In fact, the intercept is 1+ (log DR), because it’s always a factor of 10 higher than the value of DR.
Now, what’s nice about that is the equation spits out log R directly, with just some multiply-divide-add action.
Changing DR by factors of 10 produces the other lines, so (as before) switching DR gives you a low-budget, wide-dynamic-range output.
DR need not be a power of 10, of course. The dashed line near the middle is DR = 3 kΩ, which puts the more-or-less linear region between about 20 kΩ (fairly dim) and 500 Ω (rather bright). That’s a useful range in my house and it might be close enough for my friend.
The dashed line approximating those points has an intercept at 1 + log 3000 = 4.5, so the overall equation is
log R = (-2/5) * V + 4.5
The approximation gets progressively worse below, say, V = 0.5 and above V = 4.5, so the outline of the algorithm is:
- V < 0.5 = pretty dark: lowest digit intensity
- V between 0.5 and 4.5: puzzle over log R
- V > 4.5 = lots o’ light: max digit intensity
The Arduino ADC produces a 10-bit integer: 0 through 1023. Call that Vb (“V binary”), which you can scale to V like this
V = (5/1024) * Vb
Plugging that into the equation produces
log R = (-2/5) * (5/1024) * Vb + 4.5
log R = (-2/1024) * Vb + 4.5
The useful limits on Vb for the linear approximation are
- V = 0.5 -> Vb = 1024 * 0.5/5 = 102
- V = 4.5 -> Vb = 1024 * 4.5/5 = 922
Checking those limits against the actual formula for R
- Vb = 102 -> log R = 4.3 (instead of 4.43)
- Vb = 922 -> log R = 2.7 (instead of 2.52)
That’s about what you’d expect from the graph: the line is lower than the dots on the dim end (left) and higher on the bright end (right). On the other paw, getting log R without the floating-point math package makes up for that.
Now, given that you’re going to use a table lookup anyway, you don’t need any arithmetic on Vb at all. Shove all the stuff surrounding Vb to the other side of the equation
(log R – 4.5) * (-1024 / 2) = Vb
(4.5 – log R) * 512 = Vb
Precompute the left side for useful values of R, fill in the corresponding bit pattern to get the desired brightness, then index into the table with the measured Vb: shazam, log R -> brightness bits in one step!
If you’re exceedingly lucky, the brightness bits will be more-or-less in a binary sequence, in which case you can just right-shift Vb to get that number of bits and send ’em directly to the LED drivers. No table needed: one shift and you’re done!
So, for example, suppose you want eight brightness levels controlled by three Brightness Bits BB
BB = Vb >> 7
What’s not to like?
Maybe you already knew that and were wondering why it took me so long to get there…