The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Day: November 28, 2009

  • Deriving the Logarithm of Photoresistor Resistance From Voltage

    Photoresistor circuit and equation
    Photoresistor circuit and equation

    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.

    CdS Resistance vs Voltage
    CdS Resistance vs Voltage

    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)

    and

    (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…