Computing stuff tied to the physical world

Detecting a blinking LED

In Hardware on Mar 18, 2012 at 00:01

There are several scenarios where it’d be nice to detect the pulse of a blinking LED – especially low-power, because then we can sense it with a long-lasting battery-powered setup, such a JeeNode or JNµ.

Fortunately, that’s fairly easy to do. I used this test setup to try things out:

Screen Shot 2012 03 16 at 12 53 12

The left-hand side is a test pulse, generating 10 ms pulses once a second to simulate a typical indicator light. It’s simple enough with no further explanation needed.

The right-hand side of the above circuit is the actual pulse sensor we’re trying to implement. It’s a voltage divider with on the upper half a fixed resistor (well, a trimmer, but we only have to adjust it once) and the lower half is a Light Dependent Resistor (LDR) – like these two examples:

DSC 2968

We want to generate one electrical pulse for each incoming light pulse, in such a way that it could trigger an ATmega’s digital input pin. With a clean pulse we could then set up a pin-change interrupt and keep the ATmega asleep most of the time.

The trouble is that LDR’s and voltage dividers are analog i.s.o digital. One way would be to constantly read out the signal as analog input. But this sort of polling and continuous ADC use eats up quite a bit of power – a digital signal would be a lot better as it’d allow us to use pin-change interrupts.

No worries. A digital signal is also a voltage, but it has to stay under a certain limit to be treated as digital “0” and above another limit to act as digital “1”. Here are the specs from the ATmega328 datasheet:

Screen Shot 2012 03 16 at 13 05 15

With a JeeNode running at 3.3V, we get: “0” ≤ 1V and “1” ≥ 2V. Note that in theory voltages between 1 and 2V will have indeterminate results, but in practice the signal will work fine as long as it doesn’t stay forever within that gray zone.

The trick is to make that LDR sensor as sensitive as possible. The LDR which I used is a fairly standard one (same one as included with the Room Board) and rises to over 1.5 MΩ resistance when dark. Let’s assume 1 MΩ as extra margin, then we could use 470 kΩ as upper resistor of the above resistor divider, and the resulting signal would be about 2.2V when dark.

The way I maximized the dark-state resistance was to place it in a small black plastic cap, as shown in the above photograph. This is essential, as you’ll see.

Now the actual pulse detection: the resistance of an LDR drops (quite dramatically) in the presence of light, so the trick is to place it close enough to the blinking LED that we want to “read out”. I placed my blinking test LED a few millimeteres from the black cap (which is open at the end, of course);

DSC 2969

Here’s a scope snapshot of the LED pulse (channel 1, yellow trace) and the detected signal (channel 2, blue trace):


You can see the LDR signal dropping when light is detected, and that the LDR actually needs a bit of time to react. For 10 ms pulses, it’s plenty fast enough, though.

This configuration is probably ok – the voltage swings from about 1.8V (a marginal “1”) down to 0.7V (a clean “0”). The whole setup really depends on first getting the dark resistance as high as possible (i.e. shielded from any stray light) and pulling it down enough during the LED blink (i.e. close enough to pick up a good LED signal).

When the LED is inserted inside the plastic tube, the signal becomes much stronger – but recovery is slower:


It all hinges on the pull-up resistor, really. Which is why the best way to create this sensor is to use an adjustable 1 MΩ trimpot, and tweak it. You won’t need an oscilloscope or even a multimeter to get optimal results:

  • very important: shield the LDR from stray light as well as you can
  • pick as high a resistance as possible which still gives a “1” signal (between 100 kΩ and 1 MΩ)
  • place the LDR + shield near enough to the LED to generate a “0” pulse
  • tweak and iterate the above steps until it works reliably under all conditions

For minimal power consumption, the pull-up resistor should be as large as possible. Example: with an optimal pull-up of 1 MΩ and the LDR’s dark resistance about 1 MΩ as well, the quiescent current draw will be (Ohm’s law: I = E/R) 3.3 V / 2 MΩ = 1.65 µA, an excellent value for ultra-low power nodes. During the LED light pulse, this will increase to at most twice that (i.e. if the LDR resistance were to drop completely to 0 Ω).

Note that a more sensitive sensor design will be needed if you want to actually measure the length of the pulse with a decent accuracy, but for simple counting purposes where incident light can be kept out, there is nothing simpler than this LDR + pull-up trimmer, probably.

Update – More info about LDR’s on the LadyAda site.

  1. I’m actually surprised your LDR is fast enough to capture the 10 ms pulse (but the scope trace looks like 50 ms?). Is it cadmium sulfide or something else? I would have first thought about using a phototransistor in this application. But maybe the LDR is less expensive (?)

  2. You beat me to it Mr Beale! I was also quite astonished as how fast the reaction was. Now I’m going to have to have a dig in my bit box and see if the LDR I spotted the other day lurking in at the bottom, is up to the task.

  3. I used 10 ms pulses because it matches one half wave of 50 Hz.

    In fact, after testing a bit more: the LDR will even pick up 30 KHz from a fluorescent lamp (about 0.7V amplitude). I think the trick is to stay close to its dark current, i.e. keep light levels low to improve recovery times.

    @JBeale – in that first capture, the scope time base was set to 5 ms/div (the 25 ms refers to the trigger offset from the middle of the screen).

  4. A phototransistor ADC story is described in the Café at It’s been working flawlessly for me, and I can see my power consumption in a Windows 8 live tile.

  5. Great article, it’s exact what i work on in order to read my current consume meter, counting the pulse led :-)

    I just hope you will go on with this article, and you’ll make it work in full power down sleep mode using an external crystal in order to have the time between 2 pulses… like here:

    Thanks a lot Denis

  6. LDR’s require bias current, which wastes power. A zero-power solution is to use 2 red LEDs as photodiodes, simply wire them in series, ground the cathode and measure the volts with a high impedance input.

    LEDs make fantastic photodiodes, I saw a dual wavelength photometer with a red and a green LED, each alternately acting as radion source & detector.

    • I’ve not been able to see a signal with a LED (note that I’m trying to detect another blinking LED – which may not be bright enough as light source).

      With an AMS302 light sensor, I do see a clean 0.3V pulse on the scope (no pull-up) when placing it touching the blinking LED, but that’s not enough to trigger a digital I/O pin without further circuitry.

      With a SFH309 phototransistor and 1 MΩ pull-up, I still get only 0.4V when carefully placing the sensor right in front on the LED.

    • More results – with one specific 1.8mm transparent LED, I got a 0.07V blip, still not as good as other photodiodes. And with a tiny 0.4V solar cell I also see a jump from 0.1 to 0.2V.

      So far, the above LDR circuit still seems to work best and offer the lowest power solution, IF it is well-shielded from ambient light and kept near its dark resistance most of the time.

  7. Shouldn’t the AMS302 be used in photocurrent mode? Then it takes one giant resistor to drive up the voltage? Vr is specified as 5V @5 lx (doubt you get 5lx without a lens, though).

    • Yes – probably. I was just trying it out to see whether it can generate a signal on its own – which would remove the need for a pull-up.

  8. I know that it is possible to make a 9600 bps data link receiver using 2 parts: a phototransistor (emitter goes to GND) and a 1 k-ohm resistor from +3.3V to the transistor collector. The voltage swing at the collector is reliably read as logic levels at a standard GPIO pin on a micro. The transmitter was an IR LED, those probably generate a bigger signal. Range was about 5 cm or more. 1 k may seem like a low value, but current draw is minimal if the device is kept shielded from ambient light.

  9. I’d love to see this carry on into the software side to see how pulses can be counted and/ or timed in a low power scenario. In my application I’m measuring time between pulses on an electricity meter to give power consumption, but I’m trying to move to a battery powered solution – and am having trouble with reconciling an asleep low power Jeenode that has no sense of time with an interrupt driven pulse input.

    My best guess so far is to move to counting pulses in a given fixed period of time, say 10 seconds, (using a timer interrupt). This will give an initial inaccuracy, but if the pulse total is transmitted, it will average over time… unless anyone knows a better way?

    • How about keeping approximate track of time and then letting the receiving node take care of the accurate timestamping? IOW, just make sure a packets gets sent every once in a while with the count so far, preferably as the pulse count changes (but waiting a bit, if they come in too fast).

  10. I found n article about sensing light with a LED, but it seems it is not useable as it relies on timing a discharge curve. I guess this is too slow a process to be able to detect a 50usec pulse/wave

  11. I’ve had similar success with an LDR on my recently changed Kamstrup electricity meter, no issue with short pulses. However, my two remaining meters are older Kamstrups with a much weaker blinking led, where I could not get sufficient sensitiveness. I therefore went for LDR + resistor for the new meter, and phototransistor for the old ones. One issue I have come across often is that it is not clear to all that an external interrupt will reset the watchdog timer, hence making time measurement impossible with decent precision. I applied jwc’s 32KHz crystal mod (, so I can put the node in deep sleep and have timer2 overflow every second with precision. The sensors (LDR or phototransistor) are connected to 2 digital pins, and through a simple diode OR gate, to IRQ2 (Rising). Each blink generates an external interrupt that wakes the node up, increments the correct counter according to the pin(s) currently high and goes back to sleep. Every 10 seconds (i.e. 10 timer2 overflows), I calculate and send out the Watts as reported by the meters (simple division). Still lots of room for improvements (averaging over a few iterations, sending out batches of 6 measurements every minute rather than 1 every 10 seconds, clean presentable code, acks, etc) , I wish I had more time! as suggested above, I considered sending only the pulse counts and letting the destination deal with time (rrdtool does this very weel in COUNTER mode), still thinking about it but I kind of like the Jeenode to report Watts directly.

    • Callis, I’d love to see this code as well please. The combination of interrupts and low power is frying my brain a little.

  12. Callix@ …can you put a source sample on the forum please ? I want to make the same think like you but i don’t know how to progrm it. Thanks a lot Denis

  13. Sorry the LED as photodiode didn’t work for you. I have used this trick in the past and got over a volt, a quick test with a jellybean 5mm red LED in a clear package in front of a small blinking red LED got me 180mV into 1M load.

    Tricks like sanding the front of the LED flat and then polishing it on a sheet of paper increased the sensitivity as I remember.

Comments are closed.