PicBasic Pro Demo
Infrared Object Detection & Navigation


 Back To Index

The demo version of the PicBasic Pro compiler is limited to a maximum 31 lines of code. We can't compile programs as large or complex as the full version examples, but we can still get a lot of things accomplished even with this limitation.

This example shows how to put together a basic infrared object detection & navigation system similar to the full version infrared navigation project shown HERE  but staying within the 31 line limitation of the demo version Pro compiler.

Operation is simple. Here's how it works.

1.  Configure the 16F628 internal hardware PWM for 40KHz [the internal bandpass frequency of the 40KHz IR detector].
2. Turn hardware PWM on.
3. Turn on the left LED, and record the output of the IR detector.
4. Turn off the left LED.
5. Turn on the right LED, and record the output of the IR detector.
6. Turn off the right LED.
7. Turn hardware PWM off.
8. Use the object detection results to turn left, right, move backwards or continue forward.

The idle [not receiving data] output state of the infrared detector module is logic 1 or +5V when no infrared energy modulated at 40KHz is detected, and logic 0 or ground when IR energy modulated at 40KHz is striking the face of the detector. Very simple, yet very effective. We use the internal hardware PWM feature of the PIC16F628 to generate the 40KHz carrier. The 40KHz carrier is applied to the anode side of each infrared LED.

We then use two additional PortB I/O-pins connected to each infrared LED cathode side to control each LED. As shown below, when the 40KHz PWM carrier is on, logic 0 on PortB.0 will turn on the LED. Logic 1 on PortB.0 will turn off the LED. The C indicates the cathode lead of the LED. The A indicates the anode lead of the LED.


40KHz Modulated IR LED Control

When PortB.0 is at ground, and the 40KHz carrier is on, the LED is on. When PortB.0 is high, the LED is off. Refer to the circuit in Figure #1 below for the complete schematic for both LEDs.

Current Through The LED:

The OED-EL-8L infrared LEDs supplied with each Micro-Bot have a typical forward voltage drop of 1.5V. We subtract this 1.5V from our operating voltage of 5V. This leaves us with 3.5V. We take 3.5V / 430Ω to find our peak current of 8.1mA. If we set our PWM duty-cycle to 20%, we then have an average current of 8.1mA x 20% for 1.6mA supplied to each LED.

Detecting Objects:


 

Infrared Object Detection

As shown above in the illustration, an object in the path of the IR beam will reflect the IR energy back onto the face of the infrared detector module. We simply turn on the PWM carrier, take the LED cathode low, and sample the output of the IR detector. A logic 0 indicates the IR energy is striking the detector, and an object is in our path. A logic 1 on the detectors output indicates there is no object present to reflect IR energy back onto the face of the detector. Simple enough..!

This method of infrared object detection and navigation is simple, and inexpensive for robotics applications.

Parts List:

bullet

2 x 430Ω resistors [YELLOW, ORANGE, BROWN]

bullet

2 x OED-EL-1L2 Infrared LEDs with heat shrink

bullet

1 x 10uF Electrolytic Capacitor

bullet

1 x 10KΩ resistor [RED, BLACK, ORANGE]

bullet

1 x 100Ω resistor [BROWN, BLACK, BROWN]

bullet

1 x TSOP1740 40KHz Infrared Photo Module


Figure #1: Infrared LED Drive Circuit

Place two pieces of heat shrink over the both of the infrared LEDs as shown on the left LED in Figure #1a below. The end of each LED lens should slightly stick out from the heat shrink. We include the heat shrink pre-cut to simplify the process.

The black heat shrink prevents IR energy from exiting the rear by covering the LED leads, and also keeps IR energy from spreading sideways, directing the IR beam forward. Since we'll be detecting the IR beams being reflected from objects, we want to concentrate the infrared energy in a forward direction.

Note: Be careful not to overheat the LEDs during the process. Use a candle flame, cigarette lighter, or heat gun to slowly warm  the heat shrink until it closes around the LED leads and lens as shown below on the left LED.

Once you're finished applying the heat shrink, wire both infrared LEDs to PortB as shown above in figure #1 schematic.


Figure #1a: Infrared LEDs With Heat Shrink

Port Pins Used & Configuration:

bullet PortB.0 = Left IRLED output
bullet PortB.1 = Right IRLED output
bullet PortB.2 = IR detector input
bullet PortB.3 = PWM Drive output


Figure #2: Infrared Detector Circuit

Wire in the TSOP1740 infrared photo detector circuit shown in Figure #2. Place the infrared detector in the center area of the bread board or solder it to the center prototyping solder pads similar to this photo HERE. We have included a pin connection diagram of the TSOP1740 and other components HERE.

Note: We have supplied the TSOP1740 IR detector module with Micro-Bot for enhanced serial communications. This particular detector module will work at baud rates up to 2400 bps, and provides better noise rejection than most off-the-shelf types.

You'll notice our programming techniques here are quite a bit different than the full version code examples HERE for a similar project. With the full version compiler, we we're not restricted to 31 lines of code, and we had plenty of room to make our code easy to follow and understand. One of the real advantages of programming in BASIC is the ability to write, review, and quickly understand what task each line of code or sub-routine is taking care of.

With BASIC we use commands that are very similar to the way we speak.

Example: If the window = open, and it's cold outside, then shut the window.

Now take a programming language like the PicBasic Pro compiler that's every bit as simple as this BASIC example, and you're on your way to programming the PIC microcontroller without a degree in rocket science, long hours in a classroom, and a ton of cash out of pocket.

About The Demo Code:

There are a few tricky parts here that were necessary to squeeze in enough functionality for infrared object detection, a little decision making, and the servo motor control loop.

If you're new to PicBasic or microcontrollers in general, this example may look a bit intimidating, but hang in there, and we'll explain how it all works at the end. For now, see if you can figure it out without scrolling to the bottom.

'****************************************************************
'*  Name    : IRDEMO.BAS                                        *
'*  Author  : Bruce Reynolds                                    *
'*  Notes   : IR Navigation Example For The PBP Demo Compiler   *
'****************************************************************
@ DEVICE HS_OSC,MCLR_OFF,LVP_OFF,WDT_OFF,PROTECT_OFF
DEFINE OSC 20    ' We're using a 20MHz crystal
CCPR1L = 25      ' 25=20% PWM Duty-Cycle
PR2 = 124        ' Set PWM for ~40KHz
T2CON = 4        ' Timer2 ON + 1:1 prescale
TRISB=%00000100  ' Set PortB TRIS register
PORTB=%00000011  ' LEDs off, pulsout pins set for logic 1 pulses

Hits        VAR BIT[2]  ' Holds IR detector reading for each LED.
Loops       VAR BYTE    ' Loop counter
MotorDir    VAR WORD    ' Motor directions for pulsout command
Left_Led    VAR PortB.0 ' PortB.0 = left IRLED output
Right_Led   VAR PortB.1 ' PortB.1 = right IRLED output
IR_Output   VAR PortB.2 ' PortB.2 = IR detector input
Left_Servo  VAR PortB.4 ' PortB.4 = left servo output
Right_Servo VAR PortB.5 ' PortB.5 = right servo output

Begin:                 ' Eats up 1 line, but we need it
  CCP1CON = 12         ' Turn hardware PWM On
  LOW PortB.0          ' Turn on left IR LED
  PAUSE 10             ' Wait 10mS to allow IR detector output to change 
  Hits[0]=IR_Output    ' Read detector output into Bit0
  Portb=%00000001      ' Turn off left IR LED, turn on right IR LED
  PAUSE 10             ' Wait 10mS to allow IR detector output to change
  Hits[1]=IR_Output    ' Read detector output into Bit1
  HIGH PortB.1         ' Turn off right IR LED
  CCP1CON = 0          ' Turn hardware PWM Off

  IF (Hits[0]=1) AND (Hits[1]=1) THEN MotorDir=$3C50 ' Forward
  IF (Hits[0]=0) AND (Hits[1]=1) THEN MotorDir=$5050 ' Right turn
  IF (Hits[0]=1) AND (Hits[1]=0) THEN MotorDir=$3C3C ' Left turn
  IF (Hits[0]=0) AND (Hits[1]=0) THEN MotorDir=$503C ' Backup

  FOR Loops = 1 TO 4                        ' Change to increase/decrease movement times
   PULSOUT Left_Servo,MotorDir.LowByte*10   ' Compute value for left servo movement & move it
   PULSOUT Right_Servo,MotorDir.HighByte*10 ' Compute value for right servo movement & move it
  PAUSE 20
  NEXT Loops
  GOTO Begin  ' 4 lines from our 31 line limit here
  
  ' You have 4 lines of code space left for the demo version. See what you can come up with.
How It Works:

We'll break down the code here and explain each section.

CCPR1L = 25      ' 25=20% PWM Duty-Cycle
PR2 = 124        ' Set PWM for ~40KHz
T2CON = 4        ' Timer2 ON + 1:1 prescale
TRISB=%00000100  ' Set PortB TRIS register
PORTB=%00000011  ' LEDs off, pulsout pins set for logic 1 pulses

The PWM duty-cycle is set by writing to the CCPR1L register. Here we write a value of 25 into CCPR1L to configure the 16F628 onboard hardware PWM for roughly 20% high duty-cycle. The PR2 register is the Timer2 period register, and gets loaded with 124 to set the PWM frequency to 40KHz.

T2CON is the Timer2 control register and it gets loaded with 4 to set Timer2 prescaler to 1:1, and at the same time turn Timer2 on. For details on the 16F628 hardware PWM module, refer to the PIC16F628 datasheet included on the Micro-Bot CD-ROM.

TRISB is the PortB direction control register. Writing a value of %00000100 to TRISB sets PortB.2 to input, and the rest of PortB to outputs. As shown above in Figure #2 for the infrared detector circuit, PortB.2 is used as an input pin to read the status of the IR detectors output pin.

Hits        VAR BIT[2]  ' Holds IR detector reading for each LED.
Loops       VAR BYTE    ' Loop counter
MotorDir    VAR WORD    ' Motor directions for pulsout command

Here we simply declare a few working variables. Hits is a 2-bit array used to sample the infrared detector output state after turning on each infrared LED. Loops is the loop counter in the motor control routine. MotorDir is a word sized variable used to hold both values to be used with the PULSOUT command to control both the left & right servo motor directions.

Note: Normally we would use two word sized variables. One for each motor direction. Each word variable would hold the value for each motor direction, and we would use something like this --:

First we define a few constants for each motor direction. You'll see this in the hardware.inc file included in the beginning of each full version code example. We can't use include files with the demo version compiler, so we'll use another method as explained later.

ForwardLeft con 800
ReverseLeft con 600
ForwardRight con 600
ReverseRight con 800

This is very handy, and super easy to understand. It lets us control a motors direction by PULSOUT Left_Servo, ForwardLeft in easy to understand code or jumping to a direction sub-routine as shown below. Since we now have motor direction values associated with names like ForwardLeft, and ReverseRight, it's easier to know at a glance which direction a motor will move with directions associated to names instead of numbers.

We would control each motor by jumping to a routine for each specific direction like this --:

Forward:
   
FOR X=1 TO 2    ' Move forward
 
   PULSOUT Left_Servo,ForwardLeft   ' Forward left servo
 
   PULSOUT Right_Servo,ForwardRight ' Forward right servo
 
   PAUSE 20
    NEXT
    GOTO
Begin

Back:               ' Backwards
   
FOR X=1 TO 4  
     PULSOUT Left_Servo,ReverseLeft   ' Reverse left servo
 
   PULSOUT Right_Servo,ReverseRight ' Reverse right servo
 
   PAUSE 20    
    NEXT          
    GOTO
Begin      ' Return

Right:              ' Turn right
   
FOR X=1 TO 4  
     PULSOUT Left_Servo,ForwardLeft    ' Forward left servo
    
PULSOUT Right_Servo,ReverseRight  ' Reverse right servo
    
PAUSE 20
    NEXT           
   
GOSUB Check_Right
    IF Right_IR=Yes THEN Right
    GOTO Begin      ' Return
   
Left:               ' Turn left
   
FOR X=1 TO 4   
     PULSOUT Left_Servo,ReverseLeft   ' Reverse left servo
    
PULSOUT Right_Servo,ForwardRight ' Forward right servo
    
PAUSE 20
    NEXT           
   
GOSUB Check_Left
    IF Left_IR=Yes THEN Left
    GOTO Begin      ' Return

Or we can simply use PULSOUT Left_Servo, ForwardLeft. Either way, it's much easier to look at your code and know what's happening. Much easier than using something like PULSOUT, Left_Servo, 800. You don't have to keep looking things up.

You'll see these methods used in the full version compiler examples. This is an easy to read & understand method, but working with the PicBasic Pro demo version we'll need to be a little more creative to reduce the number of lines our code requires to do similar things. How we'll do this is explained below.

PortB.0, PortB.1 and PortB.3 are outputs. PortB.0 & PortB.1 control the infrared LEDs, while PortB.3 is the output pin for the 16F628 onboard hardware PWM. The variable declarations shown below setup names for each port pin. Declaring names for port pins lets us control or read values on each pin by simply referring to the pins name.

Remember, variables do not count in the demo compilers 31 line code limit. Constants, and even the routine name such as Begin: do. Since the name of a routine counts as a line of code, we'll use only the single routine name Begin, and everything will return to this routine after code execution.

Left_Led    VAR PortB.0 ' PortB.0 = left IRLED output
Right_Led   VAR PortB.1 ' PortB.1 = right IRLED output
IR_Output   VAR PortB.2 ' PortB.2 = IR detector input
Left_Servo  VAR PortB.4 ' PortB.4 = left servo output
Right_Servo VAR PortB.5 ' PortB.5 = right servo output

Our main routine Begin is shown below. This is where code execution begins, and it's also the routine we jump back to each time to begin everything over after moving servos forward or backwards.

The first line in Begin writes a value of 12 to the CCP1CON register. CCP1CON is the hardware Capture/Compare/PWM configuration register. Writing a value of 12 to CCP1CON sets the Mode Select bits to use PWM, and turns it on. Writing a value of 0 to CCP1CON turns off hardware PWM. Notice in the start we write 12 to CCP1CON to turn on PWM, and at the end, after sampling the IR detector output, we write 0 to CCP1CON to turn hardware PWM back off.

Hardware PWM consumes a lot of power, and when not in use, we simply turn it back off. We only need PWM on to generate the carrier frequency while driving the infrared LEDs during the object detection section of our code.

Begin:                 ' Eats up 1 line, but we need it
 
CCP1CON = 12         ' Turn hardware PWM On
 
LOW Left_LED         ' Turn on left IR LED
 
PAUSE 10             ' Wait 10mS to allow IR detector output to change
 
Hits[0]=IR_Output    ' Read detector output into Bit0
 
Portb=%00000001      ' Turn off left IR LED, turn on right IR LED
 
PAUSE 10             ' Wait 10mS to allow IR detector output to change
 
Hits[1]=IR_Output    ' Read detector output into Bit1
 
HIGH Right_LED       ' Turn off right IR LED
 
CCP1CON = 0          ' Turn hardware PWM Off

With hardware PWM now turned on, we simply take each LED cathode to ground by making a port pin low. LOW Left_LED sets PortB.0 to ground, and turns on the left IR LED. The short pause time after turning on each LED allows time for the infrared detector to respond to the reflected infrared light. After the 10mS pause, we sample & record the output logic state of the infrared detector while the LED is on. This value is recorded in Hits[0] for the left LED, and Hits[1] for the right LED.
Notice that we don't use HIGH Left_LED to turn the LED back off. This would require two lines of code. One HIGH Left_LED followed by one LOW Right_LED to turn on the right infrared LED. We do both in a single line using PortB=%00000001. This sets PortB.0 to 1 turning the left LED off, and PortB.1 to 0 turning the right LED on, and saves us 1 valuable line of code space.

As mentioned previously, after checking for an object on both sides, we turn hardware PWM back off until it's needed again by writing 0 to CCP1CON.

Now For The Tricky Part:

IF (Hits[0]=1) AND (Hits[1]=1) THEN MotorDir=$3C50 ' If no object on left, and no object on right, move forward
IF (Hits[0]=0) AND (Hits[1]=1) THEN MotorDir=$5050 ' If object on left, and no object on right, turn right
IF (Hits[0]=1) AND (Hits[1]=0) THEN MotorDir=$3C3C ' If no object on left, and object on right, turn left
IF (Hits[0]=0) AND (Hits[1]=0) THEN MotorDir=$503C ' If object on left, and object on right, backup

Here's where it gets interesting. As shown above, we're using a mere four lines of code to find which side objects were detected on, and then decide which way to make Micro-Bot turn to avoid these obstacles if present.

Since we don't have the luxury of making our code longer and easier to understand with the demo version compiler, we just have to get a little more creative. Remember, when an object is detected, the infrared detectors output pin will be at logic 0. When nothing reflects the LEDs infrared energy back onto the detector, its logic state will be 1 indicating no object detected.

Any bit in the Hits bit array that is logic 1 indicates there was no object when the bit was recorded. A bit holding a logic 0 indicates there was an object in the path when the bit was recorded. Hence, 1=No, 0=Yes, and we have a simple method of checking which direction we need to make Micro-Bot turn to avoid hitting obstacles in its path.

Hits[0] records the output logic state of the IR detector module when the left LED is on.
Hits[1] records the output logic state of the IR detector module when the right LED is on.

As shown before, we used a few constants to declare our motor directions like this;

ForwardLeft con 800
ReverseLeft con 600
ForwardRight con 600
ReverseRight con 800

With the demo version compiler, even constants are considered as 1 line of code each. Using this approach we would waste four valuable lines of code, so we'll think outside the box and come up with an alternative method.

Notice how ForwardLeft con 800 informs PBP that a value of 800 is needed for the PULSOUT command to make the left motor move in a forward direction. A value of 600 is used to move the right motor forward.

IF (Hits[0]=1) AND (Hits[1]=1) THEN MotorDir=$3C50 ' If no object on left, and no object on right, move forward

The line above loads word variable MotorDir with $3C50 if there were no objects detected on either side when we turned on each LED and recorded the IR detectors output in each bit position in the Hits bit array.

We split the word variable MotorDir into two separate byte sized portions. The high byte contains $3C or 3C hex. The lower byte contains $50 or 50 hex. Notice how $3C = 60d or 60 decimal, and $50 = 80d or 80 decimal. The high byte or MotorDir.HighByte holds the right servo direction value. The low byte or MotorDir.LowByte holds the left servo direction value.

Example:

MotorDir=$3C50

MotorDir.HighByte=$3C, and MotorDir.LowByte=$50.

This gives us 600 for forward motion of the right servo, and 800 for forward motion of the left servo after we multiply each byte value by a factor of 10, as shown below.

FOR Loops = 1 TO 4                        ' Change to increase/decrease movement times
 
PULSOUT Left_Servo,MotorDir.LowByte*10   ' Compute value for left servo movement & move it
 
PULSOUT Right_Servo,MotorDir.HighByte*10 ' Compute value for right servo movement & move it
 PAUSE 20
NEXT Loops
GOTO Begin

Presto. We now have a way to compute the value of each motor direction, and we used a single word variable to hold each value for left & right servo movements. We're feeding a word sized number value to the PULSOUT command from a byte sized variable.

Using Motor.HighByte*10 takes the high byte of word variable MotorDir, multiplies it by ten, and produces the word sized value to be used with the PULSOUT command to control the right servo motors direction.

Compare this code example to the ones used in the full version compiler IR navigation examples. If you own the full version PicBasic Pro compiler, you may even want to implement some of these more creative techniques to reduce overall code size.


 Back To Index

Copyright © 2007 Reynolds Electronics
http://www.rentron.com