2D Plotter

The idea of this project was to create an ultrasonic radar by mounting an ultrasonic sensor on a stepper motor. 

The plan was that the stepper motor will step from 0 – 360 degrees, the ultrasonic sensor will record the distance reading on each step and then plot the recorded data using Python and Matplotlib. 

This is the circuit of the system:

Radar Drawing

This is my c code:


// Ultrasonic stepper-motor Radar Project
// Written by Aron Horan
// Last updated 21-11-2013
 
#include 
#include 
#include 
#include 
 
// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin
 
int step_counter = 0;
int angle = 0;
int get_distance();
void step_forward();
void step_back();

// create a 2D pixel array with 100 rows & 100 columns
int distance[800];

int main()
{
	int x, y, d, cm, state, reverse=0, forward=1, n;
	state = forward;

	// Configure UART
	U1BRG = 48;            // 38400 baud @ 30 MIPS
	U1MODEbits.UARTEN = 1; // Enable UART

	// Make all port D pins outputs
	TRISD = 0;
	// Make RF0 an output
	TRISF = 0b10;

	// Configure Timer 2
	T2CONbits.TCKPS = 0b10; // Timer 2 prescaler 1:64
	PR2 = 9375;             // Timer 2 period (20ms)
	T2CONbits.TON = 1;      // Enable Timer 2

	
	while(1)
	{
		d = get_distance();
		distance[step_counter] = d;

		if(state == forward) // rotate clockwise until angle = 360 degrees
		{
			step_forward();
			if (step_counter == 799) state = reverse;
		}
		else if(state == reverse) // rotate anticlockwise until angle = 0
		{
			step_back();
			if (step_counter == 0) state = forward;
		}

		if (step_counter == 0 || step_counter == 799)
		{
			// Transmit all readings to PC
			for (n=0 ; n<800 ; ++n)
			{
				printf("%d ", distance[n]);
			}
			printf("\n");
		}

		__delay32(1500000); // delay 0.05seconds
	}

	return 0;
}

//rotate through the four outputs connected to stepper motor
void step_forward()
{
	if (LATD == 0b0001) LATD = 0b0010;
	else if (LATD == 0b0010) LATD = 0b0100;
	else if (LATD == 0b0100) LATD = 0b1000;
	else LATD = 0b0001;
	step_counter++;
}

void step_back()
{
	if (LATD == 0b0001) LATD = 0b1000;
	else if (LATD == 0b0010) LATD = 0b0001;
	else if (LATD == 0b0100) LATD = 0b0010;
	else LATD = 0b0100;
	step_counter--;
}

//send a pulse to sensor and measure length of returning "echo" 
int get_distance()
{
	_LATF0=1;			// set RF0 high
	__delay32(3000); 	// wait 100 uS
	_LATF0=0;			// then set back low
	while (_RF1==0);	// wait to receive pulse from sensor
	TMR2=0;				// reset TMR2
	while (_RF1==1);	// time TMR2
	return TMR2;		// return value of TMR2
}

This is the Python code to plot the data using matplotlib:

import time
import serial
from pylab import *

# configure the serial connections
ser = serial.Serial(
    port='/dev/ttyUSB0',
    baudrate=38400,
   
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.SEVENBITS
)
ser.close()
ser.open()

# create an array of 800 numbers
distance = range(800); 

while True:
	line = ser.readline()
	values = line.split()
	for n in range(800):
		distance[n] = int(values[n])
	plot(distance)
	show()

Here are some pictres of the UltraSonic sensor mounted onto the stepper motor:

IMG_3411

IMG_3410

Here is a video of it in action:

I have discovered that this type of ultrasonic sensor has an effectual angle of 15° and a measuring angle of 30°, there for this will distort my plotted graph.
So I decided to try the Sharp IR distance sensor instead or the ultrasonic as I wouldn’t take much changing in the code nor the physical wiring.

So here is my new code for the Sharp IR distance sensor:


// Ultrasonic stepper-motor Radar Project
// Written by Aron Horan
// Last updated 21-11-2013
 
#include <xc.h>
#include <libpic30.h>
#include <stdio.h>
#include <math.h>
 
// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin
 
int step_counter = 0;
int angle = 0;
unsigned int read_analog_channel(int n);
void step_forward();
void step_back();

// create a 2D pixel array with 100 rows & 100 columns
int distance[800];

int main()
{
	int x, y, d, cm, state, reverse=0, forward=1, n;
	state = forward;

	// Configure UART
	U1BRG = 48;            // 38400 baud @ 30 MIPS
	U1MODEbits.UARTEN = 1; // Enable UART

	// Make all port D pins outputs
	TRISD = 0;
	// Make RF0 an output
	TRISF = 0b10;
	
	// Configure analog inputs
	TRISB = 0x01FF;      // Port B all inputs
	ADPCFG = 0xFF00;     // Lowest 8 PORTB pins are analog inputs
	ADCON1 = 0;          // Manually clear SAMP to end sampling, start conversion
	ADCON2 = 0;          // Voltage reference from AVDD and AVSS
	ADCON3 = 0x0005;     // Manual Sample, ADCS=5 -> Tad = 3*Tcy = 0.1us
	ADCON1bits.ADON = 1; // Turn ADC ON

	// Configure Timer 2
	T2CONbits.TCKPS = 0b10; // Timer 2 prescaler 1:64
	PR2 = 9375;             // Timer 2 period (20ms)
	T2CONbits.TON = 1;      // Enable Timer 2
	
	
	while(1)
	{
		d = read_analog_channel(0);
		distance[step_counter] = d;

		if(state == forward) // rotate clockwise until angle = 360 degrees
		{
			step_forward();
			if (step_counter == 799) state = reverse;
		}
		else if(state == reverse) // rotate anticlockwise until angle = 0
		{
			step_back();
			if (step_counter == 0) state = forward;
		}

		if (step_counter == 0 || step_counter == 799)
		{
			// Transmit all readings to PC
			for (n=0 ; n<800 ; ++n)
			{
				printf("%d ", distance[n]);
			}
			printf("\n");
		}

		__delay32(1000000); // delay 0.05/3seconds
	}

	return 0;
}

//rotate through the four outputs connected to stepper motor
void step_forward()
{
	if (LATD == 0b0001) LATD = 0b0010;
	else if (LATD == 0b0010) LATD = 0b0100;
	else if (LATD == 0b0100) LATD = 0b1000;
	else LATD = 0b0001;
	step_counter++;
}

void step_back()
{
	if (LATD == 0b0001) LATD = 0b1000;
	else if (LATD == 0b0010) LATD = 0b0001;
	else if (LATD == 0b0100) LATD = 0b0010;
	else LATD = 0b0100;
	step_counter--;
}

// This function reads a single sample from the specified
// analog input. It should take less than 2.5us if the chip
// is running at about 30 MIPS.
unsigned int read_analog_channel(int channel)
{
	ADCHS = channel;          // Select the requested channel
	ADCON1bits.SAMP = 1;      // start sampling
	__delay32(30);            // 1us delay @ 30 MIPS
	ADCON1bits.SAMP = 0;      // start Converting
	while (!ADCON1bits.DONE); // Should take 12 * Tad = 1.2us
	return ADCBUF0;
}

Here are some pictures of the Sharp IR sensor mounted on the stepper motor:

IMG_3412

IMG_3414

Ultra Sonic Distance Meter

In this Project I used an ultra sonic sensor to display the distance of an object on a LCD display.

The system works by sending the sensor a 10uS ping to its trigger input then waiting for the receiving echo on its output. The distance of an object is proportional to the delay of the echo received and the length of the pulse received.

[/caption]

Show below is the part of code that:
1. Sends a ping to the sensor
2. Waits on the receiving echo while holding TMR2 to zero
3. Once the echo is received starts TMR2
4. When echo finishes sends back the value of TMR2.

int get_distance()
{
	_LATD2=1;			// set rd2 high
	__delay32(3000); 	// wait 100 uS
	_LATD2=0;			// then set back low
	while (_RD0==0);	// wait to receive pulse from sensor
	TMR2=0;				// reset TMR2
	while (_RD0==1);	// time TMR2
	return TMR2;		// return value of TMR2

Here is a diagram of the system:

ultrasonic

Here is my program code:


////////////////////////////////////////
// Robotics 3.1 Ultra Sonic Project.                                   
// Aron Horan 24/10/13                                                     
////////////////////////////////////////


#include <xc.h>
#include <libpic30.h>
#include <stdio.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

#define RS_PIN _LATC14
#define RW_PIN _LATC13
#define E_PIN _LATC15
 
void delay_ms(unsigned int n);
void send_nibble(unsigned char nibble);
void send_command_byte(unsigned char byte);
void send_data_byte(unsigned char byte);
void setup();

int get_distance();
int n;

int main()
{
	double d,cm;
	setup();
	
	TRISC = 0; // RC13-15 as digital outputs
    TRISB = 0xFFF0; // RB0-3 as digital outputs
    _PCFG0 = 1; // AN0 is digital
    _PCFG1 = 1; // AN1 is digital
    _PCFG2 = 1; // AN2 is digital
    _PCFG3 = 1; // AN3 is digital
 
    // Let's just write to the LCD and never read!
    // We'll wait 2ms after every command since we can't
    // check the busy flag.
    RW_PIN = 0;
    RS_PIN = 0;
    E_PIN = 1;
     
    // Initialisation
    delay_ms(16); // must be more than 15ms
    send_nibble(0b0011);
    delay_ms(5); // must be more than 4.1ms
    send_nibble(0b0011);
    delay_ms(1); // must be more than 100us
    send_nibble(0b0011);
    delay_ms(5); // must be more than 4.1ms
    send_nibble(0b0010); // select 4-bit mode
     
    // Display settings
    send_command_byte(0b00101000); // N=0 : 2 lines (half lines!), F=0 : 5x7 font
    send_command_byte(0b00001000); // Display: display off, cursor off, blink off
    send_command_byte(0b00000001); // Clear display
    send_command_byte(0b00000110); // Set entry mode: ID=1, S=0
    send_command_byte(0b00001111); // Display: display on, cursor on, blink on
     
    // Define two 8 character strings
	char line1[50]; // Text buffer - 16 characters for the screen, plus lots to spare
	char line2[50]; // Text buffer - 16 characters for the screen, plus lots to spare
    
    // Write the two strings to lines 1 and 2
	sprintf(line1, "Distance Meter  "); // display this text on start-up
	sprintf(line2, "By Aron Horan   "); // second line of text
	
	
    int n;
    send_command_byte(0x02); // Go to start of line 1
    for (n=0 ; n<=15 ; ++n) send_data_byte(line1[n]);
    send_command_byte(0xC0); // Go to start of line 2
    for (n=0 ; n<=15 ; ++n) send_data_byte(line2[n]);
	
	while(1)
	{
		d = get_distance(); //
		cm = (d / 27.0);	//
		sprintf(line2, "Distance:%.2fcm", cm);	// send string to LCD
		send_command_byte(0xC0); // Go to start of line 2
		for (n=0 ; n<=15 ; ++n) send_data_byte(line2[n]);
		printf("Distance:%.2fcmn", cm);
		__delay32(15000000); // delay 0.5seconds
	}
		
	return 0;
}
 
// Delay by specified number of milliseconds
void delay_ms(unsigned int n)
{
    while(n--) __delay32(30000);
}
 
void send_nibble(unsigned char nibble)
{
    // Note: data is latched on falling edge of pin E
    LATB = nibble;
    delay_ms(1);
    E_PIN = 0;
    delay_ms(1);
    E_PIN = 1;
    delay_ms(2); // Enough time even for slowest command
}
 
// Send a command byte (i.e. with pin RS low)
void send_command_byte(unsigned char byte)
{
    RS_PIN = 0;
    send_nibble(byte >> 4);
    send_nibble(byte & 0xF);
}
 
// Send a data byte (i.e. with pin RS high)
void send_data_byte(unsigned char byte)
{
    RS_PIN = 1;
    send_nibble(byte >> 4);
    send_nibble(byte & 0xF);
}

void setup()
{
	// Configure RD2 as output
	TRISD = 0b1011;
	
	// Configure Timer 2
    T2CONbits.TCKPS = 0b10; // Timer 2 prescaler 1:64
    PR2 = 9375;             // Timer 2 period (20ms)
    T2CONbits.TON = 1;      // Enable Timer 2

	// Configure UART
    U1BRG = 48;            // 38400 baud @ 30 MIPS
    U1MODEbits.UARTEN = 1; // Enable UART	
}

// send pulse to sensor and measure length of return pulse 
int get_distance()
{
	_LATD2=1;			// set rd2 high
	__delay32(3000); 	// wait 100 uS
	_LATD2=0;			// then set back low
	while (_RD0==0);	// wait to receive pulse from sensor
	TMR2=0;				// reset TMR2
	while (_RD0==1);	// time TMR2
	return TMR2;		// return value of TMR2
}

As shown in my code I simply needed to divide the output by 27 to convert into centimetres as the sensor was very linear within the ranges I was using it for.

Here is a video of it working (Not the best video but will make a better one soon)
Notice the LED 100us pulse on pin 22 (RD2) that goes to the trigger on the sensor.

Object Tracking Sensor

In this project I made an object tracker using the dsPIC30F4011 PIC and a SHARP infrared distance sensor mounted on a servo motor.

The idea was to scan left and right with the servo motor until the IR sensor detected an object.
When the edge of an object is detected it will turn away from it until it no longer detects it then turn back towards it. So the servo will be constantly scanning the edge of the object even when the object is moving.

This is a diagram of the system:

Tracking sens

This is a flow chart of my system:

flowchart
The output voltage of the infrared sensor was then measured at different distances, the results were as follows:

Capture

Plotted on a graph it looked like this:

Capture1

Here is my program:

//////////////////////////////////////////////////////////////////////////////
// Robotics 3.1 servo assignment 1.                                         //
// Tracking Sensor using ds pic & IR sensor                                 //
// Aron Horan 10/10/13                                                      //
//////////////////////////////////////////////////////////////////////////////
//
// Servo-mounted rangefinder scans left and right. When an object is detected,
// the rangefinder moves back and forth tracking the left edge of the object.
//
//////////////////////////////////////////////////////////////////////////////

#include <xc.h>
#include <libpic30.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

// Configure max and min OC1RS values
int min = 200;          // OC1RS value for 0 degrees
int max = 1150;         // OC1RS value for 180 degrees

// Function prototypes
void setup();
unsigned int read_analog_channel(int sensor);

int main()
{
    // Configure UART
    U1BRG = 48;            // 38400 baud @ 30 MIPS
    U1MODEbits.UARTEN = 1; // Enable UART
    
    // Configure digital i/o, analog inputs, Timer 2, Output Compare
    setup();

    // Define some numbered states.
    // These values won't change, so we can
    // declare them as "const" which prevents
    // them from getting changed by accident.
    const int scan_left = 1;
    const int scan_right = 2;
    const int object_detected = 3;

    // Set initial state and then enter state loop
    int distance;
    int angle = 1;
    int state = scan_right;
    int threshold = 350;
    
    while(1)
    {
        // Update distance reading
        distance = read_analog_channel(0);
        
        if (state == scan_right)
        {
            // Turn on YELLOW LED to show its in scan right state
            LATE = 0b0001; // RE0 high, RE1-3 low
            
            // Keep moving right
            angle++;
            
            // If max angle is reached, scan in other direction
            if (angle >= 180) state = scan_left;
            
            // If an object is detected, switch state
            if (distance >= threshold) state = object_detected;
            __delay32(150000); // 5ms delay
        }
        else if (state == scan_left)
        {
            // Turn on GREEN LED to show its in scan left state
            LATE = 0b0010; // RE1 high

            // Keep moving left
            angle--;
            
            // If min angle is reached, scan in other direction
            if (angle <= 0) state = scan_right;
            
            // If an object is detected, switch state
            if (distance >= threshold) state = object_detected;
            __delay32(150000); // 5ms delay
        }
        else if (state == object_detected)
        {
            // Turn on RED LED to show its in object detected state
            LATE = 0b0100; // RE2 high

            // Scan left looking for left edge of detected object
            angle--;
            
            // If the minimum angle is reached, just stay still
            if (angle <= 0) angle = 0;
            if (angle >= 180) angle = 180;
            
            // If the object disappears, begin scanning right again
            if (distance < threshold) state = scan_right;
            __delay32(150000); // 5ms delay
        }

        // Set servo to current angle 
        OC1RS = min + (angle/180.0)*(max-min);      
    }
    
    return 0;
}

void setup()
{
    // Configure RD0, RD1 and as outputs
    TRISD = 0b1100;
    TRISE = 0b111110000; // set RE0-3 as digital outputs

    // Configure Timer 2 (default timer for output compare)
    T2CONbits.TCKPS = 0b10; // Timer 2 prescaler 1:64
    PR2 = 9375;             // Timer 2 period (20ms)
    T2CONbits.TON = 1;      // Enable Timer 2

    // Configure Output Compare channel 1 (OC1)
    OC1CONbits.OCM = 0b101; // continuous pulse mode
    OC1R = 0;               // pulse start time
    OC1RS = min;            // pulse stop time

    // Configure digital I/O
    LATD = 0;
    TRISD = 0b11111110;

    // Configure analog inputs
    TRISB = 0x01FF;      // Port B all inputs
    ADPCFG = 0xFF00;     // Lowest 8 PORTB pins are analog inputs
    ADCON1 = 0;          // Manually clear SAMP to end sampling, start conversion
    ADCON2 = 0;          // Voltage reference from AVDD and AVSS
    ADCON3 = 0x0005;     // Manual Sample, ADCS=5 -> Tad = 3*Tcy = 0.1us
    ADCON1bits.ADON = 1; // Turn ADC ON
}

// This function reads a single sample from the specified
// analog input. It should take less than 2.5us if the chip
// is running at about 30 MIPS.
unsigned int read_analog_channel(int channel)
{
    ADCHS = channel;          // Select the requested channel
    ADCON1bits.SAMP = 1;      // start sampling
    __delay32(30);            // 1us delay @ 30 MIPS
    ADCON1bits.SAMP = 0;      // start Converting
    while (!ADCON1bits.DONE); // Should take 12 * Tad = 1.2us
    return ADCBUF0;
}

Here it is in action!

The End!