Program description

What does will it do?

This activity will help you to create a program to make your CHRP board into a line-following robot. After having completed the previous programming projects, you now know enough about the CHRP board and C programming to make a fully autonomous, line-following robot.

CHRP Robot program

This activity will guide you through creating a program that will allow a robot to follow a black line (typically electrical tape on a white or light-coloured floor). The program will use a simple, digital control scheme, resulting in somewhat jerky movements as the robot sweeps back and forth across the width of the line. You will be able to refine your line following robot to make it work more smoothly using the analogue techniques that you will learn in the next activities.

The CHRP board is designed to be easy to build into a line-following robot by using two downward-facing light sensors (phototransistors Q1 and Q2), illuminated by an LED (LED12) to 'see' the line (check the schematic diagram for circuit details).

An easy to build, inexpensive and simple robot platform can be put together using inexpensive DC motors and 1/2" MDF.

CHRPbot-topCHRPbot-axle-detail

What you should know before starting

The line-following program will need to make decisions about which motor to turn on or off by sensing the amount of light reflected from the tape line or the floor.

Getting ready

Make sure that your motor driver and motors are tested and working. Use the Output program to determine the values that need to be written to the motor driver to make the motors run forward, reverse, left and right. Having the left and right directions work by stopping one motor and running the other works better than reversing the direction of one motor (since running motors in opposite directions stops all forward travel slowing your robot's progress).

Also, be sure that the floor LED (LED12) and the phototransistors (Q1 and Q2) have been installed, aligned and tested using the Input program, or the CHRPbot program, below.

Test your robot by holding it and physically moving it across a black tape line to check phototransistor alignment. As the robot is moved across the line, from one side to the other, one light sensor should 'see' the line first, then both, then just the second light sensor, and finally neither sensor. Check that this sequence works the same way, from left to right, and right to left, across the line. If the sensing sequence is not similar as the direction of sensing changes, adjust the position or aim of either LED12 or the Q1 and Q2 phototransistors.

Thinking about the program

Before you write any code, think about the possible input states resulting from using two light sensors (2^2 = 4 states). It is important to consider exactly what the robot should be doing in response to each possible input state.

State 1 - On the line

If both line sensors sense the line, the robot should go forward.

State 2 - On the right edge of the line

When the left sensor is still on the line, but the right sensor is off the line, the robot should turn left.

State 3 - On the left edge of the line

Similar, but opposite to state 2, the robot should now turn right.

State 4 - Entirely off the line

If both light sensors do not sense the line, a number of courses of action could be taken:

  1. Stop. This would be the simplest course of action, but having your robot just stop and give up doesn't make for a very effective line-following robot.
  2. Reverse. This is another simple solution that assumes the line must be somewhere behind the robot, and by reversing the robot should be able find the line again and try to continue along the path.
  3. Do the opposite. A slightly more complex course of action that makes the assumption that reversing the last mode of travel before leaving the line should return the robot to the line. It's more complex because it needs to know the prior state — which direction the robot was last travelling before it left the line.
  4. Freedom! Line? Who cares about the line? The robot is free to wander forever (or, at least until its batteries run out)! Unfortunately, this solution does not make an effective line follower since it makes no attempt to get back on the line.
  5. Search. This is a more complicated solution. It would have the robot use a circular or geometric search pattern to look for the line. This would work best if the robot could adjust the speed or amount of travel of each wheel so precise turns could be made.

Practically speaking, reverse is usually a good option since it is quite effective at returning to the line, and works with a minimal amount of extra code.

Create the program structure

We will start with the Input program, since it's already set up to get input from the phototransistors on Port A. You'll need to modify the program to perform a sequence of conditional checks to check each of the phototransistors in turn. The flowchart in the following photo shows one possible solution.

robotflowchart-c

Translating the flowchart into working code results in a program that contains multiple if-conditions — the diamond structures in the flowchart.

Create the CHRPbot project

Start by creating a new project in MPLAB. The CHRPbot project uses the same files as the Input project, but requires new CHRP3.c and CHRPbot.c files. All the files are listed below for reference.

Create the CHRP3.h header file

Add CHRP3.h to the Header Files section of the MPLABX project.

		
/*==============================================================================
	CHRP 3.1 (PIC16F886) symbolic constants for user and main functions.
==============================================================================*/

// TODO - Add user constant definitions for user.c and main.c here. Constant
// definitions provide names for all CHRP 3 I/O devices and program constants.

// PORTA I/O device definitions
#define Q1			RA0			// Phototransistor Q1 input
#define Q2			RA1			// Phototransistor Q2 input
#define VR1			RA2			// Potentiometer VR1 input
#define T1			RA3			// Temperature sensor T1 input
#define LED2		RA4			// Run LED, LED2 output
#define RUNLED      RA4         // Run LED, LED2 output
#define LCDBACKLIGHT	RA4		// LCD LED backlight cathode output
#define LCDLED		RA4			// LCD LED backlight cathode output
#define VM			RA5			// Motor supply voltage divider input
#define BEEPER		RA6			// Piezo beeper P1 output
#define P1			RA6			// Piezo beeper P1 output
#define LED12		RA7			// Floor/phototransistor LED (LED12) output

// PORTB I/O device definitions
#define LED10		RB0			// LED10 output (LSB)
#define M1A			RB0			// Motor 1 A output
#define S2			RB0			// Switch S2 input
#define	LED9		RB1			// LED9 output
#define M1B			RB1			// Motor 1 B output
#define S3			RB1			// Switch S3 input
#define LED8		RB2			// LED8 output
#define M2A			RB2			// Motor 2 A output
#define	S4			RB2			// Switch S4 input
#define LED7		RB3			// LED7 output
#define M2B			RB3			// Motor 2 B output
#define S5			RB3			// Switch S5 input
#define LED6		RB4			// LED6 output
#define	S6			RB4			// Switch S6 input
#define LED5		RB5			// LED5 output
#define S7			RB5			// Switch S7 input
#define LED4		RB6			// LED4 output
#define LED3		RB7			// LED3 output (MSB)

// PORTC I/O device definitions
#define H2			RC0			// External I/O header H2
#define LCDE		RC0			// LCD Enable output
#define SERVO1		RC0			// Servo 1 output (external header H2)
#define H3			RC1			// External I/O header H3
#define LCDRW		RC1			// LCD Read/!Write output
#define	SERVO2		RC1			// Servo 2 output (external header H3)
#define	H4			RC2			// External I/O header H4
#define LCDRS		RC2			// LCD Register Select output
#define	SERVO3		RC2			// Servo 3 output (external header H4)
#define H5			RC3			// External I/) header H5
#define SERVO4		RC3			// Servo 4 output (external header H5)
#define LED11		RC6			// Communication/sensor LED, LED11 output
#define IR			RC7			// IR demodulator U5 input
#define U5			RC7			// IR demodulator U5 input

// A-D Converter input channel definitions
#define ADCH0		0b00000000	// A-D channel 0 input
#define	ADQ1		0b00000000	// Q1 phototransistor A-D input channel (Ch0)
#define ADCH1		0b00000100	// A-D channel 1 input
#define	ADQ2		0b00000100	// Q2 phototransistor A-D input channel (Ch1)
#define ADCH2		0b00001000	// A-D channel 2 input
#define	ADVR1		0b00001000	// Potentiometer VR1 A-D input channel (Ch2)
#define ADCH3		0b00001100	// A-D channel 3 input
#define	ADT1		0b00001100	// Temperature sensor T1 A-D input channel (Ch3)
#define ADCH4		0b00010000	// A-D channel 4 input
#define	ADVM		0b00010000	// Motor voltage divider A-D input channel (Ch4)

// Clock frequency for delay macros and simulation
#define _XTAL_FREQ	4000000			// Set clock frequency for time delays
#define FCY			_XTAL_FREQ/4	// Set processor cycle time

void initPorts(void);    // Port configuration function prototype declaration
		

Next, create the PIC16F886config.c source file

All of the .c files belong in the Source Files section of the project.


/*==============================================================================
	CHRP 3.1 (PIC16F886) configuration bit settings.

	Processor configuration bits determine processor operating characteristics
	as well as processor-specific features. Refer to the processor datasheet
	for details on specific configuration bit settings.

	MPLAB X contains a 'Configuration Bits' settings tool accessed under:
	Window menu -> PIC Memory Views. This tool generated the settings, below.
==============================================================================*/

// TODO - Verify or modify configuration bits using the Configuration Bits tool.

// PIC16F886 Configuration Bit settings generated by MPLABX for CHRP3 Level 1.

#include    "xc.h"              // XC compiler general include file

// CONFIG1
#pragma config FOSC = INTRC_NOCLKOUT    // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = ON       // RE3/MCLR pin function select bit (RE3/MCLR pin function is MCLR)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown Out Reset Selection bits (BOR disabled)
#pragma config IESO = OFF       // Internal External Switchover bit (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)
#pragma config LVP = OFF        // Low Voltage Programming Enable bit (RB3 pin has digital I/O, HV on MCLR must be used for programming)

// CONFIG2
#pragma config BOR4V = BOR40V   // Brown-out Reset Selection bit (Brown-out Reset set to 4.0V)
#pragma config WRT = OFF        // Flash Program Memory Self Write Enable bits (Write protection off)
		

Next, create the CHRP3.c source file.


/*==============================================================================
	CHRP 3.1 (PIC16F886) hardware initialization and user functions.
==============================================================================*/

#include	"xc.h"				// XC compiler general include file

#include	"stdint.h"			// Include integer definitions
#include	"stdbool.h"			// Include Boolean (true/false) definitions

#include	"CHRP3.h"			// Include user-created constants and functions

void initPorts(void)
{
	// TODO - Initialize oscillator, ports, and add (optional) user functions.

	// Initialize user ports and peripherals.

	ANSEL = 0b00000000;			// Make all PORTA pins digital I/O
	ANSELH = 0b00000000;		// Make all PORTB pins digital I/O
	OPTION_REG = 0b01010111;	// PORTB pull-ups on, TMR0 internal div 256

	// Set port directions for I/O pins: 0 = Output, 1 = Input

	TRISA = 0b00101111;			// Set runLED, IR LEDs as outputs in PORTA
	TRISB = 0b00000000;			// Set all PORTB pins for LED output
	TRISC = 0b10110000;			// Set up receive and transmit lines for IR
								// demodulator (U5) and LED11, servo outputs

	// Set starting I/O conditions.

	PORTA = 0;					// Turn off all PORTA outputs, turn on run LED
	PORTB = 0;					// Turn off all PORTB LEDs
	PORTC = 0;					// Turn off all PORTC outputs

	// Enable interrupts, if needed.
}
		

Finally, create the CHRPbot.c source file.

This main CHRPbot file is not a complete robot program. Rather, it's currently set up to test the light sensors so that you can verify their operation and calibrate their aim. You can add-to and modify this code to make it into your robot program.


/*==============================================================================
	Project: CHRPbot
	Version: 3.1				Date: November 24, 2014
	Target: CHRP3				Processor: PIC16F886

	This program uses digital input to make a line-following robot. Use .75"
	black electrical tape on a white background (while melamine board or tile).
==============================================================================*/

#include    "xc.h"				// XC compiler general include file

#include    "stdint.h"          // Include integer definitions
#include    "stdbool.h"         // Include Boolean (true/false) definitions

#include    "CHRP3.h"           // Include user-created constants and functions

/*==============================================================================
	Motor direction constant definitions
 =============================================================================*/

#define STOP		0b00000000	// Port B value for both motors stopped
								// Add more motor direction definitions here

/*==============================================================================
	Main program code
==============================================================================*/

int main(void)
{
    // Initialize I/O and peripherals
    initPorts();

    // Turn floor LED on
    LED12 = 1;

	// Light sensor test loop
    while(1)
    {
		PORTB = PORTA;
	}
}

		

Test your knowledge

  1. What logic level do you expect when a phototransistor is positioned over the black tape line? Why?
  2. What binary patterns do you have to output to Port B to make your motors turn forward, reverse, left and right? Define them in your source code using #define statements.

Apply your skills

  1. Using your programming skills and knowledge of inputs, outputs, and decision structures to create a program that enables your CHRP-based robot to follow a black tape line.
  2. Bonus: Program additional features or functions into your robot. Some ideas include:
    • pushbutton start/stop - set the robot on the line and have it start moving after you press one button and stop after pressing the button again (or a different button)
    • light show - cycle a light pattern on the 4 most significant bits of PORTB, while the robot is travelling (change the pattern as the robot changes direction for extra skill points)
    • obstacle avoidance - add bumpers or sensors to your robot to allow it to wander around without a line
    • programmable robot - use some of the buttons to program a sequence of steps into the robot and then press a button to have it repeat the pattern back
    • beeping robot - have the robot beep or play other sounds as it drives or sees obstacles
    • IR remote control - install an IR demodulator and control your robot using a TV remote control
    • analogue line follower - sense the light level using analogue inputs and modulate the motor outputs to have the robot travel more smoothly along the line
    • light/dark seeking robot - light seeking works particularly well for obstacle avoidance or solar charging, while dark seeking mimics a scared creature
    • battery sensing - determine the battery level and change behaviour as the power supply drains
    • walking robot - because wheels are so 'Dalek'. Hook up servo-controlled legs to your CHRP board