Using a LED as a Light Sensor - Example Program

Flenser

Senior Member
LEDs are also photodiodes plus they have some internal capacitance and it is these two properties that can be used to detect light. I've done this before using AVR & PIC microcontrollers but had never tried it using a PICAXE. I assumed that interpreted basic would be too slow but when I did try it turned out to be tivially easy.

The reference I used is this paper
On the PICAXE the commands are:
  • Set the cathode pin to high. This reverse biases the LED and charges up the internal capacitance
  • Change the cathode to an input. This starts discharging the LEDs internal capacitance through the microcontroller pins input resistance.
  • Take an ADC reading of the voltage of the cathode.

That's it! You compare the ADC reading you got to a fixed threshold value to determine whether any extra light was being shone on the LED.

Here are two screen dumps of the signal at the cathode.

The first one is with no extra light shining on the LED and so you see the voltage go to 3V when the cathode pin is set to high. It stays high until we execute the command to make the cathode pin an input, after which the voltage drops in the familiar pattern for discharging a capacitor through a resistor.
Red LED (08M, 4MHz, No torch).JPG

The second one is from exactly the same setup but now I am shining a torch into the LED. The photodiode effect provides another path to discharge the LED, which now discharges much quicker.
Red LED (08M, 4MHz, Torch).JPG

On an AVR or PIC microcontroller coded in compiled C or assember the idea would be either to time how long it took the pin to discharge down to a particular voltage or to check what level the voltage had dropped to after a fixed time.

For detecting light on a PICAXE I take the second approach, to check what level the voltage has dropped to after a fixed time and this fixed time is simply going to be the PICAXE basic interpreter overhead between setting the cathode pin to an input and taking the ADC measurement. It is a fortunate cooincidence that the time it takes to do a single command on the PICAXE means that the voltage is measured at about the right point early in the discharge of the internal capacitance and we get a big difference in the voltage reading when we shine a light on the LED.

A refinement we can make is to connect the LED between two microcontroller pins. This allows us to use the LED to both detect and generate light. This first program demonstrates the whole process, including this refinement. When you shine a torch on the LED, it turns on.

Code:
; LED Sensor - Tuning 

setfreq m4
 
;           08M/08M2
;          .---_---..
;         -|+V   0V|-   D1 - Waterclear Red LED
;         -|C.5 C.0|-  | /|
;         -|C.4 C.1|---|< |--+
;          |       |   | \|  |
;          |       |         |
;         -|C.3 C.2|--/\/\/\-+
;          '-------'    R1 - Current limiting resistor

' Pins
symbol CATHODE = C.1
symbol ANODE   = C.2
 
' Constants
symbol THRESH  = 130
 
' Variables
symbol ADC     = b4

main:
    low ANODE            ; Reverse bias LED to charge it up
    high CATHODE
     
    input CATHODE        ; Then discharge LED through the input pin
 
    readadc CATHODE, ADC    ; Measure cathode voltage shortly after it starts discharging
 
    low CATHODE            ; Set cathode low ready to use LED as light emitter

    if ADC > THRESH then    ; If input still above our threshold there was no extra light
        low ANODE        ; so turn the LED off
    else                ; If input now below our threshold there was extra light
        high ANODE        ; so turn the LED on
    endif
     
    sertxd (#ADC,cr,lf)    ; Trace the ADC reading to sertxd (use to set THRESH)

    pause 50            ; Pause long enough for the LED to appear bright when on

    goto main
It does the charge/discharge/ADC measurement and then compares the measured ADC value to a hardcoded threshold. If the measured ADC value is below the threshold it puts the cathode pin low and anode pin high to turn on the LED otherwise it puts both pins low to leave the LED turned off. The ADC values are also sent to serout so that you can see what the values are with no extra light and when extra light is on the LED as an aid to setting the threshold value.

The LED is not giving off any light when the charge/discharge/ADC measurement cycle is being done so you need to set a long enough value for the delay in order for the LED to shine brightly when it is on. As long as the whole loop is more then 100 Hz you should not see any flickering.

The paper TR2003-35.pdf makes these two important points to keep in mind when choosing which LED to use as a sensor:
- The LED is a photodiode that is sensitive to light at and above the wavelength at which it emits. This means that we can expect red LEDs to have a greater sensitivity to white light than yellow, green or blue LEDs.
- LEDs encapsulated in coloured plastic will filter out light that we want to detect. This means that we can expect waterclear LEDs to have a greater sensitivity then LEDs that are encapsulated in coloured plastic.
 
Last edited:

Flenser

Senior Member
This second program is a hack of Alex Weber's excellent programmable LED instructable to work with just the LED instead of using a LED and an LDR.
http://www.instructables.com/id/Programmable-LED/

Code:
; Programmable LED	

#picaxe 08M
	
setfreq m8
	
;             08M
;          .---_---..
;         -|+V   0V|-   D1 - Waterclear Red LED
;         -|C.5 C.0|-  | /|
;         -|C.4 C.1|---|< |--+
;          |       |   | \|  |
;          |       |         |
;         -|C.3 C.2|--/\/\/\-+
;          '-------'    R1 - Current limiting resistor


	' Constants
	symbol TRUE=1
	symbol FALSE=0
	symbol DURATION=25	' time between samples
	symbol THRESH=160		' values under THRESH are recorded as "ON"
	symbol NBR_BYTES=50	' number of bytes used to store samples
	symbol STORAGE_BASE=80	' first location of storage variable
	
	' Pins
	symbol CATHODE = C.1
	symbol ANODE   = C.2
	
	' Registers
	symbol SAMPLE_BYTE=b0
	symbol SAMPLE_LOCN=b2
	symbol LOOP1=b3
	symbol LOOP2=b4
	symbol FLASH_LED_DELAY=b5
	symbol ADC=b6
	symbol DETECTED_LIGHT=b7

	low CATHODE

	for LOOP2 = 1 to 5	' Flash the LED slowly 5 times to signal
		low ANODE		' the start of the program
		pause 400
		high ANODE
		pause 400
	next LOOP2
	
	pause 500
	
record:
	for LOOP2 = 1 to 5	' Flash the LED rapidly 5 times to signal
		low ANODE		' the start of the recording
		pause 80
		high ANODE
		pause 80
	next LOOP2

	for LOOP1 = 1 to NBR_BYTES
	      let SAMPLE_LOCN = STORAGE_BASE+loop1-1
	      
	      gosub read_led	' Read the LDR
	      let bit0=DETECTED_LIGHT
	      
	      gosub read_led	' Read the LDR
	      let bit1=DETECTED_LIGHT
	      
	      gosub read_led	' Read the LDR
	      let bit2=DETECTED_LIGHT
	      
	      gosub read_led	' Read the LDR
	      let bit3=DETECTED_LIGHT
	      
	      gosub read_led	' Read the LDR
	      let bit4=DETECTED_LIGHT
	      
	      gosub read_led	' Read the LDR
	      let bit5=DETECTED_LIGHT
	      
	      gosub read_led	' Read the LDR
	      let bit6=DETECTED_LIGHT
	      
	      gosub read_led	' Read the LDR
	      let bit7=DETECTED_LIGHT

		poke SAMPLE_LOCN, SAMPLE_BYTE ' Now save the sample byte to location b2
	next LOOP1

	for LOOP2 = 1 to 5	' Flash the LED rapidly 5 times to signal
		low ANODE		' the end of the recording
		pause 40
		high ANODE
		pause 40
	next LOOP2
	
playback:
	for LOOP1 = 1 to NBR_BYTES
	      let SAMPLE_LOCN = STORAGE_BASE+loop1-1
	      
		peek SAMPLE_LOCN, SAMPLE_BYTE ' Read the sample byte at location b2
			      
	      ' Set the LED based on the value of bit0
	      let DETECTED_LIGHT=bit0
	      gosub play_led
			      
	      ' Set the LED based on the value of bit1
	      let DETECTED_LIGHT=bit1
	      gosub play_led
			      
	      ' Set the LED based on the value of bit2
	      let DETECTED_LIGHT=bit2
	      gosub play_led
			      
	      ' Set the LED based on the value of bit3
	      let DETECTED_LIGHT=bit3
	      gosub play_led
			      
	      ' Set the LED based on the value of bit4
	      let DETECTED_LIGHT=bit4
	      gosub play_led
			      
	      ' Set the LED based on the value of bit5
	      let DETECTED_LIGHT=bit5
	      gosub play_led
			      
	      ' Set the LED based on the value of bit6
	      let DETECTED_LIGHT=bit6
	      gosub play_led
			      
	      ' Set the LED based on the value of bit7
	      let DETECTED_LIGHT=bit7
	      if DETECTED_LIGHT = TRUE then
			high ANODE
		else
		     	low ANODE
		endif
		pause DURATION
	
	next LOOP1
	
	goto playback		' play back for ever
	
read_led:
	low ANODE			; Reverse bias LED to charge it up
	high CATHODE
		
	input CATHODE		; Then discharge LED through the input pin
		
	readadc CATHODE, ADC

	low CATHODE

	if ADC > THRESH then	; If input not below our threshold there was no extra light
		low ANODE
		let DETECTED_LIGHT=0
	else				; If input now below our threshold there was extra light
	     	high ANODE
	     	let DETECTED_LIGHT=1
	endif

	pause DURATION
	
	return
	
play_led:
	if DETECTED_LIGHT = TRUE then
		high ANODE
	else
	     	low ANODE
	endif

	pause DURATION
	
	return
But wait, there's more!

When I'm playing with PICAXEs I usually power it with a 3V CR2032 battery on the breadboard and when you power a PICAXE with 3V it turns out that the PIC processor can only source and sink around 7.6 mA from each pin. This means that if you use 3V then the programmable LED can be made using just a PICAXE and a LED. No other components are needed:

Code:
;             08M
;          .---_---..
;      3V -|+V   0V|-   D1 - Waterclear Red LED
;         -|C.5 C.0|-  | /|
;         -|C.4 C.1|---|< |--+
;          |       |   | \|  |
;          |       |         |
;         -|C.3 C.2|---------+
;          '-------'
WARNING. If you power you PICAXE with anything higher than 3V you must use a current limiting resistor with the LED. If you do not you _will_ damage your PICAXE chip.
 

chipwich

Member
Flenser, interesting post. I've seen the topic of using LEDs for input many times on this forum. I posted a very similar project ( http://corticalcafe.com/picaxe_simple_nightlight.html ) and there's a schematic of a circuit with just an LED dating back to 2007 at http://www.user.dccnet.com/wrigter/picaxe/darksensingLEDflasher.gif . In my design, I don't reverse biase the diode, but simply activate the internal pullup resistor and read the ADC value of the LED/photodiode directly.

I found some odd behaviour in that the ADC values seemed to change when the serial cable was plugged in, probably due to some grounding issues. My workaround was to keep min and max ADC values over 24 hours so that I can judge the relative current light level, instead of using a fixed threshold. This seems to work pretty well.

WARNING. If you power you PICAXE with anything higher than 3V you must use a current limiting resistor with the LED. If you do not you _will_ damage your PICAXE chip.
Good advice and proper engineering. The current limiting resistor also keeps the LED current to an acceptable range. But I believe that each PICAXE output pin can only sink about 20mA max, and the whole chip is limited to about 90mA output. As PICAXE chips are very low-cost, I've become quite cavalier and pretty much never use a current limiting resistor on my LEDs. I have yet to break either a PICAXE or an LED like this, even when driving it at 5v. Still, I wouldn't want students taking this shortcut without understanding what is going on.
 
Last edited:

Paix

Senior Member
Code:
#PICAXE 08M
'
' Ian Leitch - November 2012
' LIGHT & DARK SENSING LED FLASHER
' based on a circuit by Wilf Rigter 17 January 2007
' 
symbol cathode = C.1	; pin1 is cathode
symbol anode   = C.2	        ; pin2 is anode
symbol auxout  = C.4

symbol cntr    = b1		; counter for control
symbol xvalue  = b2		; xor operator value
output 4				; pin4 is output

' initialise
pause 100
xvalue =0

DO
	inc cntr
	if cntr = 64 then
		cntr = 0
	endif
	
	if cntr < 32 then
		xvalue = 0
	else
		xvalue = 1
	endif

sertxd (#b0)
	high cathode		; reverse bias LED
	low  anode
	input anode			; isolate anode pin
	nap 4				; nap for 1 second (7 original) <2 unstable 3 fast 7 slow
	
	b0 = pinC.2 xor xvalue	; anode output = input (0 active light) (1 active dark)
	pin2 = b0			; pin2 set from variable
	
	output anode		; enable anode output
	low cathode			; pulse cathode low
	nap 3				; nap 200mSeconds (3 original)
	

	if b0 = xvalue then		; sound when dark is sensed, regardless of the
		sound auxout, (400,500); sensor/indicator xor directive
	endif
LOOP					; repeat



'             +Vo  +-o-V   (4.5V battery pack)
'               o--+ o
' LED anode ----o  | o-- cathode LED
' LED cathode   o  | o-- anode   LED
'         |        |
'         +--470R--+
A counter increments from 0 to 64 to give 32 steps each of light and dark sensing which is dependent upon the 1 and 0 (dark and light) state of the xor value variable xvalue (b2).

The code serves no particular purpose other than to demonstrate how the circuit is made to operate and to hint at future uses that the sensor may be put to. The sensor is a water green 5mm LED, but in the original circuit by Wilf Ritger I also tried a red enveloped 5mm LED and a yellow enveloped 3mm yellow. Both gave good acceptable results but haven't been used as sensors in the current slightly changed configuration.

Note that in my diagram I have strapped pin C.5 to 0V, but I normally use an AXE029 download adaptor and flying leads connected appropriately to 0V, txd and rxd appropriately and the strap C.t to 0V would need to be removed. I have left in the sertxd statement which I use during programming/running iterations to display the ascii numeric character state of the sense variable b0.

Occluding the sensor when light sensing will cause it to stop flashing. When in the dark sensing phase occluding the sensor will cause it to flash.
The sounder I have in the code is replaced with a LED in the circuit and the values I find to be quite handy. In reality I would swap the sound command as it is with a shorter beep and a suitable delay to keep the DO loop whizzing around at a suitable rate.

The code has been written for clarity and not compactness and should be fairly readable.

I may try out modified code with a CR232(?) 3V lithium coin cell and use a couple of 08M chips as entertaining novelties, using C.3 as a switch to determine whether it is to be dark or light sensing,

The 470R current limiting resistor for the LED on C.4 was an arbitrary choice and being the first suitable value that came out of my bag of resistor packets.

I hope that this has been of some interest and added to the valuable insights given by Chipwich and Flenser. I suspect that we are far from breaking new ground, but bringing it to your attention, because as Flenser has said, it has turned out to be trivially easy. I'm going to figure out how to monitor some bee movements now that I have what might prove to be an ideal sensor.
 
Top