Python Exemplary - RPi Tutorial
deutsch     english    

LIGHT AND INFRARED SENSORS

The source code of all examples can be downloaded from here.

 

Photodetectors measure the intensity of electromagnetic radiation. If they are sensitive in the spectrum of visual light (wavelength between 300 nm and 700 nm), they are also called light sensors. If they are responsive in the infrared region (wavelength between 700 nm and 1 mm) they are called infrared sensors (IR sensor). At room temperature objects emit thermal radiation in the 8 to 35 um band.

 

 

Light Sensors

 

Light sensors are used to measure the intensity of visual light (also called luminosity). Most light sensors consist of a photoconductive cell whose resistance depends on the luminosity (Light Dependent Resistor = LDR, also called photoresistor). The cell is made of a high resistance semiconductor. Incident photons are absorbed by electrons that jump from the valence to the conduction band and so increase the conductibility (or reduce the resistance R) of the cell. Within a reasonable range of the luminosity L, the characteristics can be expressed as

  R = Ro * L-b where Ro is the resistance without light and b a material constant.

This dependency corresponds to a line in a double logarithmic plot:

ls7

Photocells are widely used in photo cameras to select the right diaphragm and shutter speed. Here the analog value is of importance. In a light barrier application, only two states are considered: Bright or Dark. The crossover value between the two states is also called trigger level, because the state change normally triggers ("fires") some action.

Aim:
Use the photocell in a light barrier application with a user selectable trigger level.

Circuitry:
The LDR is one of the resistors in a potentiometer circuit (two resistors in series) with a fixed supply voltage (3.3V) . The output voltage is fed into a ADC and converted to a digital signal. You can create (and then even sell) your own "Digital Photocell" by soldering the LDR with a 1 kOhm resistor and a MCP3021 ADC on a SMT-to-DIL adapter. (If you don't have the necessary equipment, use an ADC you have at hand.)

ls8
ls9

Program:[►]

# LightSensor.py
# Light barrier with LDR sensor

import smbus
import time

TRIGGER_LEVEL = 300 # user selectable

def readData(port = 0):
    if port == 0:
        adc_address = 0x48
    elif port == 1:    
        adc_address = 0x4D
    rd = bus.read_word_data(adc_address, 0)
    data = ((rd & 0xFF) << 8) | ((rd & 0xFF00) >> 8)
    data = data >> 2
    return data

print "starting..."
bus = smbus.SMBus(1) 

state = "DARK"
while True:
    v = readData(1)  # adapt to your ADC (0 or 1)
    if v >= TRIGGER_LEVEL and state == "DARK":
       state = "BRIGHT"
       print "BRIGHT event"
    if v < TRIGGER_LEVEL and state == "BRIGHT":
       state = "DARK"
       print "DARK event"
Highlight program code (Ctrl+C copy, Ctrl+V paste)

Remarks:
In the main loop you poll the ADC rapidly to detect if the returned value is below or above the trigger level. You must use a "state variable" state, because you only want to report the crossover of the trigger level. Instead of a string, you could also use a boolean variable isBright (or isDark).

 

 

Hardware Threshold Detection

 

The cross-over of a trigger level can be detected by a simple electronic circuit called a Schmitt-trigger. The output is a LOW or HIGH signal and may be directly fed into a standard GPIO port without any ADC conversion. This simplifies the program considerably and requires much less processing power, especially in a robot application where more than one IR or LDR based proximity detectors are needed.

A classical circuit uses an operational amplifier (OpAmp). Because the output voltage is proportional (with a high gain factor) to the difference of the input voltages at the non-inverting and inverting inputs, but limited to the supply voltage, the output voltage is rapidly changing when the input voltage difference crosses zero. So when the OpAmp is driven by a single voltage supply VCC the output is at digital level LOW or HIGH. A potentiometer is used to set the luminosity where the cross-over takes place. A simple white LED may be used as light source for the reflecting light.

ls10 A op-amp based Schmitt-trigger with adjustable cross-over

 

 

 

 

Infrared Sensors / Distance (Proximity) Sensors

 

Infrared sensors are also used for light barriers, where a infrared source emits light not visible to the human eye. This may present some advantages over devices where the light beam is visible. Because many materials reflects infrared waves, a reflective light barrier are widely use in all kind of distance detectors. For short distances (1 - 100 cm) the light source is a photodiode (IR LED) that emits IR light around 1000 nm and a photodiode or phototransistor captures the reflection and translates the light intensity to a voltage

Both components can be mounted in the same housing. In hobby robotics the reflective optical sensor with transistor output TCRT5000 from VISHAY is widely used. (It can be purchased from many sources, e.g. 4tronix (http.//4tronix.co.uk) or by an Ebay search. For detailed information, consult the data sheet and the application examples.  


Aim:

Detect the distance to an object using a reflective light barrier and display the result in the console and (if available) on an attached display.

Circuitry:
We use the TCRT5000 that needs a 5V supply. Connect the line Analog In to any of the ADCs that you have at hand.

 

To learn how to connect your ADC, consult the previous page. Be aware to remove any pull-up resistors on the SDA and SCL lines if you power the ADC with 5V.

 


  Phototransistor current (in mA) versus distance to a reflecting object (mm) (for constant 10V transistor voltage and a typical reflecting material).

Since the input-output relationship is completely non-linear, it is an algorithmic problem how to transform the voltage to an approximate distance. As you may suggest, some kind of interpolation is necessary. We will not do right now and just display the output voltage of the sensor.

In many proximity sensor applications, the absolute value of the distance is not of importance because the sensor is used to trigger an action, when the distance cross a certain level (from far to near or vice versa). In this example we turn a light or buzzer on, when the distance becomes too small (e.g. to stop a moving robot near an obstacle).

Program:[►]

# Infrared1.py
# using the PCF8591 ADC

import smbus
import time
import RPi.GPIO as GPIO


P_LED = 22
TRIGGER_LEVEL = 25
DT = 0.2

def setup():
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(P_LED, GPIO.OUT)

def beep(n):
    for i in range(n):
        GPIO.output(P_LED, GPIO.HIGH)
        time.sleep(0.05)
        GPIO.output(P_LED, GPIO.LOW)
        time.sleep(0.05)

bus = smbus.SMBus(1)  # RPi revision 2 (0 for revision 1)
i2c_address = 0x48
setup()
beep(3) # to say we are ready
while True:
    data = bus.read_byte_data(i2c_address, 0) # read ch0
    if data > TRIGGER_LEVEL:
        beep(2)
    time.sleep(DT)
Highlight program code (Ctrl+C copy, Ctrl+V paste)

 

Infrared reflective light barriers are widely-used in industrial products. The optoelectronic device GP2Y0A21YK fabricated by Sharp is still going strong with good availability all over the world. It is a simple 3-wire device that is powered by 5 V and outputs a voltage below 3.3V that needs no external components. So it can be easily interfaced with a 3.3V ADC.  

Again the output voltage u is not in a simple relationship to the distance d as we see from from the data sheet. But u = f(1/d) may be approximated by a straight line u = mx + b with
m = 19.8
and b = 0.228 in the range of d = 7 to d = 80 cm.

Our program shows this approximated distance at the terminal and (if available) on a attached display.

Program:[►]

# Infrared2.py
# GP2Y0A21YK sensor with PCF8591 ADC

import smbus
import time
from py7seg import Py7Seg # xxx

bus = smbus.SMBus(1)  # RPi revision 2 (0 for revision 1)
i2c_address = 0x48
ps = Py7Seg() # xxx
# u = mx + b, x = 1/d
m = 19.8
b = 0.228
while True:
    data = bus.read_byte_data(i2c_address, 0) # use CH0
    u = data / 255 * 5
    d = int(m / (u - b))
    print "d =" ,d, "cm"
    ps.showText("%4d" %d) # xxx
    time.sleep(0.1)
Highlight program code (Ctrl+C copy, Ctrl+V paste)

Remarks:
If you do not have a ELV display or you use another display, comment out or modify the lines marked with xxx.