Between rigging up a small LED matrix to my member storage and returning my 3D printer + webcam to the shop, the idea of measuring ambient light has come up several times. Finding a photoresistor to do this turned out to be beyond simple: a member gave me one to use. But once the thought had crossed my mind, I wanted to follow through with this LED sensor thing and see if it’s really up to snuff.

First, gotta find out how it all works. Easy: another shop member wrote a library for it. And the library references an Arduino page which describes the operation: Light Emitting Diodes are typically wired to produce light. But, if you attempt to apply reversed voltage, then the LED will reject current (it is a diode after all) and will do something I find pretty cool: it acts as a tiny capacitor. And even cooler: the capacitor’s characteristics will change based on how much light is reaching the LED’s substrate. So this library will “charge” the LED by applying VCC->VSS backwards on the device, and then measure the length of time it takes for the capacitor to discharge (which is dependent on the ambient light). Neat!

wiring the LED

So let’s give it a shot:

Wire up like so. In this layout we’d normally turn on the LED by driving pin D11 to VCC, and holding D12 low. Instead, we’ll use Tyler’s library to reverse the voltage and count the milliseconds until the LED reads low voltage as an input.

This works well, but I noticed some variations in the returned value from the LED. Let’s investigate:

Just by visual inspection, it looks like the time between samples affects the returned value. If you sample, wait a few seconds, and then sample twice in a row, all three values vary heavily. So I set up a timer to sample at intervals and collected the results:

s_0.5 s_1.0 s_5.0 s_10.0 s_15.0
300 281 257 248 243
300 282 256 249 239
298 283 258 247 240
299 295 255 233 248
299 284 240 246 248
300 283 242 243 245
299 284 243 246 244
300 283 243 241 254
300 295 241 242 242
300 280 241 242 254
300 282 239 247 242
301 280 249 238 241
300 281 247 239 243
299 281 246 245 257
299 294 246 245 244
299 293 245 250 237
298 294 243 253 239
298 294 253 242 238
298 294 237 246 234
298 293 236 248 241
297 292 245 253 303
297 294 245 245 243
296 294 243 245 245
295 294 242 244 256
296 293 240 245 265
297 293 222 247 262
298 293 252 249 256
298 291 253 249 256
297 292 251 247 254
296 292 251 264 252
296 292 250 262 254
297 293 246 262 240
296 293 260 261 245
297 293 261 264 246
296 291 257 249 256
295 291 243 258 254
295 293 253 242 267
294 293 251 242 266
293 292 250 242 263
294 295 249 255 263
294 294 249 251 244
293 294 247 266 257
294 291 249 253 256
293 291 233 251 251
293 291 245 253 243
294 291 242 251 239

With at least 30 sequentially sampled values for each interval, we turn to pspp – the GNU statistical analysis tool.

GET DATA
  /TYPE=TXT
  /FILE="/home/berocs/vals.csv"
  /IMPORTCASES=ALL
  /ARRANGEMENT=DELIMITED
  /DELCASE=LINE
  /FIRSTCASE=2
  /DELIMITERS=","
  /VARIABLES=
    s_0.5 F3.0
    s_1.0 F3.0
    s_5.0 F3.0
    s_10.0 F3.0
    s_15.0 F3.0.

DESCRIPTIVES
    /VARIABLES= s_0.5 s_1.0 s_5.0 s_10.0 s_15.0
    /STATISTICS=DEFAULT RANGE.

Valid cases = 46; cases with missing value(s) = 0.
╭────────╥──┬──────┬───────┬─────┬───────┬───────╮
│Variable║ N│ Mean │Std Dev│Range│Minimum│Maximum│
╞════════╬══╪══════╪═══════╪═════╪═══════╪═══════╡
│s_0.5   ║46│297.09│   2.34│ 8.00│ 293.00│ 301.00│
│s_1.0   ║46│290.04│   5.00│15.00│ 280.00│ 295.00│
│s_5.0   ║46│246.65│   7.44│39.00│ 222.00│ 261.00│
│s_10.0  ║46│248.70│   7.35│33.00│ 233.00│ 266.00│
│s_15.0  ║46│250.20│  11.78│69.00│ 234.00│ 303.00│
╰────────╨──┴──────┴───────┴─────┴───────┴───────╯

Huh. Not quite what I expected. As the interval decreases, so does the error. It looks like if I want to use this library with long intervals, smoothing should be used.

Another round, this time each value is a hysteresis average: value = (value/2)+(raw/2)

GET DATA
  /TYPE=TXT
  /FILE="/home/berocs/vals_avg2.csv"
  /IMPORTCASES=ALL
  /ARRANGEMENT=DELIMITED
  /DELCASE=LINE
  /FIRSTCASE=2
  /DELIMITERS=","
  /VARIABLES=
    s15_avg2 F3.0.

DESCRIPTIVES
    /VARIABLES= s15_avg2
    /STATISTICS=DEFAULT RANGE.

Valid cases = 46; cases with missing value(s) = 0.
╭────────╥──┬──────┬───────┬─────┬───────┬───────╮
│Variable║ N│ Mean │Std Dev│Range│Minimum│Maximum│
╞════════╬══╪══════╪═══════╪═════╪═══════╪═══════╡
│s15_avg2║46│274.57│   6.59│27.00│ 264.00│ 291.00│
╰────────╨──┴──────┴───────┴─────┴───────┴───────╯

That’s a little better. Let’s go again with slightly more aggressive smoothing: value = (value*0.75)+(raw*0.25)

GET DATA
  /TYPE=TXT
  /FILE="/home/berocs/val_15s_a4.txt"
  /IMPORTCASES=ALL
  /ARRANGEMENT=DELIMITED
  /DELCASE=LINE
  /DELIMITERS=","
  /VARIABLES=
    s15_avg4 F3.0.

DESCRIPTIVES
    /VARIABLES= s15_avg4
    /STATISTICS=DEFAULT RANGE.

Valid cases = 46; cases with missing value(s) = 0.
╭────────╥──┬──────┬───────┬─────┬───────┬───────╮
│Variable║ N│ Mean │Std Dev│Range│Minimum│Maximum│
╞════════╬══╪══════╪═══════╪═════╪═══════╪═══════╡
│s15_avg4║46│304.78│   5.73│21.00│ 293.00│ 314.00│
╰────────╨──┴──────┴───────┴─────┴───────┴───────╯

Diminished returns, oh well. I’m particularly interested in the mean, which changes by a lot more than I’d like, about 50 points between tests.

So now we’ve established a decent idea of the operation with this particular LED (5mm, white), and a pretty solid operating value of 250-300ms for the lit lab. With a threshold of 350, the system works reliably to keep light available for the webcam!

** I’ve moved the LED to a more favorable location for collecting light form the overhead fixtures. The returned value is now reliable enough to require no smoothing.

Leave a Reply

Your email address will not be published. Required fields are marked *