Self-Navigating Android Smartphone Car

2014-04-17 14.52.10

The objective of this project is to harness the power of a smartphone to create a low-cost intelligent robot.

This report demonstrates the steps involved in building an Android controlled robot that can self-navigate to any given GPS location.

Until recently, mobile robots were controlled by individual sensors such as gyroscopes, accelerometers and distance sensors in conjunction with micro-controllers in order to operate.

Nowadays, with smartphones widely available and affordable, robots can be controlled in a new way. Smartphones are equipped with a rich variety of sensors that have the capability to control a highly functioning robot.

Whether realise it or not, robots are an integral part of day-to-day life in the 21st century. There are currently a vast number of uses for robots and these are constantly growing and evolving as technology develops. We see this as robots replace many manual tasks, from domestic chores all the way through to industrial automation. The potential role and opportunities for robots both at home and in the workplace are truly endless.

This project investigates the creation of a robot for navigational purposes that is controlled by a smartphone – a robot that can self-navigate to any given GPS location.

Smartphones are equipped with a large range of sensors such as accelerometers, gyroscopes, compasses, GPS receivers, hi-resolution cameras and screens. Because of this, smartphones can replace individual sensor components and act as one ‘brain’ for a robot.

Not only are these robots highly functioning, they are also more cost effective to build. Traditionally, each sensor would have to be purchased separately in order to build the robot. Smartphones provide all these sensors within one device allowing a far cheaper solution for the build.

In addition to built-in sensors, Smartphones also have features such as cameras, microphones, speakers and GPS receivers, which enhance the functionality of the robot.

On Click Listener

The final application that controls the robot will operate using different colours (light intensities) on the screen of the smartphone that will then be read into the microcontroller’s ADC. These values will then change the state of the robot.

In order to successfully build the final application, experience with handlers such as the “OnClickListener” and the “LocationListener” was required. To achieve this, an app that responds to button clicks by changing colours on the screen was created.

An “OnClickListener” is alerted when a user either clicks, touches or focuses on any widget like button/text/ image etc. The on click event handler is trigged by the “OnClickListener” to respond appropriately to this. Android developer guide UI Events: (developer.android.com/guide/topics/ui/ui-events.html )

In Android Studio software, a layout with three buttons (White, Grey and Black) was created. The layout was given an ID as it will be called and defined within the main java file.
Here is my .xml layout file:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/background"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="com.example.buttonchangecolour.app.LinearLayoutActivity">


    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click for White"
        android:id="@+id/btnWhite"
        android:layout_gravity="center_horizontal" />

    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click for Grey"
        android:id="@+id/btnGrey"
        android:layout_gravity="center_horizontal" />

    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click for Black"
        android:id="@+id/btnBlack"
        android:layout_gravity="center_horizontal" />


</LinearLayout>


This produces the following layout:

Screenshot from 2014-03-28 12:05:22

Here is my .java file:


package com.example.buttonchangecolour.app;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;


public class LinearLayoutActivity extends ActionBarActivity {

    // define some variables
    LinearLayout background;
    Button btnWhite;
    Button btnGrey;
    Button btnBlack;


    @Override
    // setup an view with reference to the "linear_layout"
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.linear_layout);

        // give variables a value //
        // set variable "background" as "linear_layout" and giving it the id previously setup
        background = (LinearLayout) findViewById(R.id.background);
        // reference our buttons with id's
        btnWhite = (Button) findViewById(R.id.btnWhite); //
        btnGrey = (Button) findViewById(R.id.btnGrey); //
        btnBlack = (Button) findViewById(R.id.btnBlack); //

        // set listener for when btnWhite is pressed
        btnWhite.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // when clicked set background color to white
                background.setBackgroundColor(Color.parseColor("#FFFFFF"));
            }
        });
        // set listener does when btnGrey is pressed
        btnGrey.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //
                background.setBackgroundColor(Color.parseColor("#666666"));
            }
        });
        /* Set listener does when btnBlack is pressed */
        btnBlack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //
                background.setBackgroundColor(Color.parseColor("#000000"));
            }
        });


    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.linear_layout, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

Here is a video of it working:

Next stage: GPS

Getting the current GPS Location

To access the GPS location provider in the application, “Google Play Services SDK” in Android Studio was installed. A “Uses permission” must also be added to access the fine location of the device – this was achieved by adding the following to the AndroidManifest.xml file:

The layout of the application was then set up with one button and one textview. When the button is pushed, the GSP location service will be called and then the location will be displayed in the Textview. Android developer guide to using location services: (http://developer.android.com/guide/topics/location/strategies.html)

I created a layout .xml file with a button: Get Location to get the GPS coordinates.
A Text Label: location Label, to display the GSP coordinates.

Here is the .xml file:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button android:id="@+id/getLocation"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/get_location"/>


    <TextView
        android:id="@+id/locationLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>


</LinearLayout>

This produces the following layout:

Screenshot from 2014-04-17 16:38:14

Screenshot from 2014-04-17 22:40:34

Here is the java file:


package com.example.gpsdisplay.app;

import android.location.Location;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.location.LocationClient;

public class MainActivity extends FragmentActivity implements
        GooglePlayServicesClient.ConnectionCallbacks,
       GooglePlayServicesClient.OnConnectionFailedListener {
    LocationClient mLocationClient;

    // Declare some variables
    private TextView locationLabel;
    private Button getLocationBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Define variables
        locationLabel = (TextView) findViewById(R.id.locationLabel);
        getLocationBtn = (Button) findViewById(R.id.getLocation);

        getLocationBtn.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                displayCurrentLocation();
            }
        });

        // Create the LocationRequest object
        mLocationClient = new LocationClient(this, this, this);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Connect the client.
        mLocationClient.connect();
    }

    @Override
    protected void onStop() {
        // Disconnect the client.
        mLocationClient.disconnect();
        super.onStop();
    }

    @Override
    public void onConnected(Bundle dataBundle) {
    }


    @Override
    public void onDisconnected() {
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
    }

    public void displayCurrentLocation() {
        // Get the current location's latitude & longitude
        Location currentLocation = mLocationClient.getLastLocation();
        String msg = "Current Location: " +
                Double.toString(currentLocation.getLatitude()) + "," +
                Double.toString(currentLocation.getLongitude());

        // Display current location
        locationLabel.setText(msg);

    }
}

Here is a video of it in action:

Construction Time:

For the frame I cut a piece of perforated steel sheeting and it into shape like this:

2014-04-08 22.31.27

2014-04-08 22.31.01

I then found rubber tires that fitted over my robosumo motors so that the robot could drive on rougher terrain:

2014-04-07 11.48.36

2014-04-07 11.48.46

I then used two LRD’s (Light dependent resistors) and wrapped insulating tape around the edges to try block as much light as possible from the sun so that the only light getting to the LDR’s is from the phone screen. Here’s some photos of them:

2014-04-08 22.33.44

On my breadboard I drilled 4 holes to insert the legs of the LDR’s through:

2014-04-08 22.39.03

2014-04-08 22.38.41

With the phone in place you can see the the LDR’s flush with the screen:

2014-04-17 14.57.21

I then stuck the wheels and my battery pack (4xAA) to the bottom of the frame:

2014-04-17 14.46.25

2014-04-17 14.46.36

Then connect the motors and battery pack to the circuit:

2014-04-17 14.49.21

2014-04-17 14.49.58

And finally slot the phone in:

2014-04-17 14.57.46

2014-04-17 14.56.02

2014-04-17 14.52.10

I then needed to test the digital values of the LDR’s with different shades of grey on the screen.
The chip im using is a dsPIC30F3012 which has a 12 BIT ADC and my analogue reference voltage is 5V, therefor:

Screenshot from 2014-04-17 16:05:16

To then had to choose a resistor to go in series with the LDR (creating a voltage divider) to give me the biggest swing in voltage.

There is a simple formula for this: R = sqrt(Rmin x Rmax)

With my Galaxy S2 the resistance values of the LDR were as follows:

Bright (White) screen = 1.3K Ohms
Dark (Black) screen = 30K Ohms

Therefor when I use the formula the value of the series resistor I should use is 6244 Ohms.
5600 Ohms was the closest resistor value I had on hand. So with my LDR and resistor wired in series I had my simple voltage divider ready for testing. I then wrote a C program and a python script to read and print the colour values of the screen.

Here is my C file:


// Author: Aron Horan
// Date: 6 Mar 2014
// Read & display analog values
// dsPIC30F3012

#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

// Function prototypes
unsigned int read_analog_channel(int);

int main()
{
	int eye;
	
	// Configure UART
	U1BRG = 48;            // 38400 baud @ 30 MIPS
	U1MODEbits.UARTEN = 1; // Enable UART
	

	 //configure digital io
	 TRISB = 0b1111111111110000;

	// Configure analog inputs
	ADPCFG = 0b1111111100111111;// Only AN6 and AN7 are analog inputs
	ADCON1 = 0;                 // Manually clear SAMP to end sampling, start conversion
	ADCON2 = 0;                 // Voltage reference from AVDD and AVSS
	ADCON3 = 19;            	// Manual Sample, ADCS=9 -> Tad = 10*Tcy = 333ns
	ADCON1bits.ADON = 1;        // Turn ADC ON
    
    
	while(1)
	{
		eye = read_analog_channel(6); 	// read sensor
		printf("%d\n ",eye); 			// print values
		__delay32(150000); 				// small delay
	}
	return 0;
}

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 is my python script:


import time
import serial

ser = serial.Serial(
    port='/dev/ttyUSB0',
    baudrate=38400,
   
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.SEVENBITS
)

ser.close()
ser.open()

while True:
	print(ser.readline())


The digital values were as follows:

Screenshot from 2014-04-17 16:38:14

With my digital colour values measured I went ahead and wrote the C program and an Android app to control the motor circuit.

The four different states of the machine will be:

Screenshot from 2014-04-17 22:55:28

Here is the layout xml file:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/background"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="com.example.buttonchangecolour.app.LinearLayoutActivity">


    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Forwards"
        android:id="@+id/btnWhite"
        android:layout_gravity="center_horizontal" />

    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Turn Left"
        android:id="@+id/btnLiteGrey"
        android:layout_gravity="center_horizontal" />

    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Turn Right"
        android:id="@+id/btnGrey"
        android:layout_gravity="center_horizontal" />

    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Stop"
        android:id="@+id/btnBlack"
        android:layout_gravity="center_horizontal" />


</LinearLayout>

This produces a layout like this:

Screenshot from 2014-04-17 22:49:42

Here is my java file:


package com.example.buttonchangecolour.app;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;


public class LinearLayoutActivity extends ActionBarActivity {

    // define some variables
    LinearLayout background;
    Button btnWhite;
    Button btnlitegrey;
    Button btnGrey;
    Button btnBlack;


    @Override
    // setup an view with reference to the "linear_layout"
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.linear_layout);

        // give variables a value //
        // set variable "background" as "linear_layout" and giving it the id previously setup
        background = (LinearLayout) findViewById(R.id.background);

        // reference our buttons with id's
        btnWhite = (Button) findViewById(R.id.btnWhite); //
        btnlitegrey = (Button) findViewById(R.id.btnLiteGrey);
        btnGrey = (Button) findViewById(R.id.btnGrey); //
        btnBlack = (Button) findViewById(R.id.btnBlack); //

        // set listener for when btnWhite is pressed
        btnWhite.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // when clicked set background color to white
                background.setBackgroundColor(Color.parseColor("#FFFFFF"));
            }
        });

        // set listener for when lite grey is pressed
        btnlitegrey.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // when clicked set background color to white
                background.setBackgroundColor(Color.parseColor("#C0C0C0"));
            }
        });


        // set listener does when btnGrey is pressed
        btnGrey.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //
                background.setBackgroundColor(Color.parseColor("#666666"));
            }
        });
        /* Set listener does when btnBlack is pressed */
        btnBlack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //
                background.setBackgroundColor(Color.parseColor("#000000"));
            }
        });


    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.linear_layout, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

}

Here is my main.c file:


// Author: Aron Horan
// Date: 6 Mar 2014
// Final Year Project
// Version 1.1
// dsPIC30F3012

#include <xc.h>
#include <stdio.h>
#include <libpic30.h>
#include <p30F3012.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

// Function prototypes
unsigned int read_analog_channel(int);

int main()
{
	int left_eye;
	int right_eye;
	
	// Configure UART
	U1BRG = 48;            // 38400 baud @ 30 MIPS
	U1MODEbits.UARTEN = 1; // Enable UART
	

	// Configure digital io
	TRISB = 0b1111111111110000;
	_TRISD0 = 0;

	// Configure analog inputs
	ADPCFG = 0b1111111100111111;// Only AN6 and AN7 are analog inputs
	ADCON1 = 0;                 // Manually clear SAMP to end sampling, start conversion
	ADCON2 = 0;                 // Voltage reference from AVDD and AVSS
	ADCON3 = 19;            	// Manual Sample, ADCS=9 -> Tad = 10*Tcy = 333ns
	ADCON1bits.ADON = 1;        // Turn ADC ON
    
	while(1)
	{
		left_eye = read_analog_channel(6); 	// read left colour sensor
		right_eye = read_analog_channel(7); // read right colour sensor
	         // for now I'm just using one sensor //
		 if (left_eye < 500) LATB = 0b0000; // stop
		 if ((left_eye > 500) && (left_eye < 2300)) LATB = 0b0101; //turn left
		 if ((left_eye > 2300) && (left_eye < 2850)) LATB = 0b1010; //turn right
         if (left_eye > 2850) LATB = 0b1001; // forwards
	}   
	return 0;
}

// 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 is a video of it in action:

Next thing I needed to do is to actually create an app that will send the Car to a given GPS location. I first created my own class that gets the bearing to my destination GPS location.
Here is my GetBearing class java file:


class bearing { 
    double currlat;
    double currlon;
    double destlat;
    double destlon;
    double result; 
     
    public bearing(double currlat,double currlon,double destlat,double destlon){
        this.currlat = currlat;
        this.currlon = currlon;
        this.destlat = destlat;
        this.destlon = destlon;


    }
    
    public double getbearing(){
    
        double longDiff = (destlon-currlon);
        
        double y= Math.sin(longDiff)*Math.cos(destlat);
        
        double x=Math.cos(currlat)*Math.sin(destlat)-Math.sin(currlat)*Math.cos(destlat)*Math.cos(longDiff);
        
		this.result= (Math.toDegrees(Math.atan2(y, x))+360)%360;;
        return result;
    }  

}         

public class GetApp{ 
    public static void main(String[] args){   
	double myposition;
        bearing b= new bearing(53.11711711711712,-6.064233730537283,53.00269,-6.11625/);
        myposition= b.getbearing(); 
        System.out.println("Bearing=" +  b.getbearing() + "Degrees");
        }
}

This when run in the terminal gives an output of: Bearing = 157.34724074606925 Degrees

I then created a class that gets the distance to the given GPS location:


class distance { 
    double currlat;
    double currlon;
    double destlat;
    double destlon;
    double result; 
    double R= 6371; // km
    
	public distance(double currlat, double currlon, double destlat, double destlon){
        this.currlat = currlat;
        this.currlon = currlon;
        this.destlat = destlat;
        this.destlon = destlon;
    }
    
    public double getdistance(){
     
        double a = Math.sin(destlat / 2) * Math.sin(destlat / 2) + Math.sin(destlon / 2) * Math.sin(destlon / 2) * Math.cos(currlat) * Math.cos(destlat);
        double b = 2 * Math.asin(Math.sqrt(a));
        this.result= R * b;
        return result;
    }
}
   
public class getdistance{
    public static void main(String[] args) {
    double myposition;
    	distance d= new distance(53.11711711711712,-6.064233730537283,53.00269,-6.11625);
    	myposition= d.getdistance();
        System.out.println("Distance=" + d.getdistance() + "Meters");
    }
}

This when ran in the terminal prints out: Distance = 17644.880299646473 Meters

I then need to access the phones magnetic field sensor to determine which way the phone is pointing referenced from north. THis will give me back a number between 0 and 360 degrees.

I firstly created a layout xml file with one textview to display:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <TextView
        android:id="@+id/tvHeading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="40dp"
        android:layout_marginTop="20dp"
        android:text="Heading: 0.0" />

</RelativeLayout>

I then created a main java file:


package com.getheading.app;


import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends Activity implements SensorEventListener {


    // Declare some variables
    private SensorManager mSensorManager;
    TextView tvHeading;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Define the textview creatd in the layout
        tvHeading = (TextView) findViewById(R.id.tvHeading);

        //  
        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
    }

    @Override
    protected void onResume() {
        super.onResume();

        // for the system's orientation sensor registered listeners
        mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
                SensorManager.SENSOR_DELAY_GAME);
    }

    @Override
    protected void onPause() {
        super.onPause();

        // to stop the listener and save battery
        mSensorManager.unregisterListener(this);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {

        // get the angle around the z-axis rotated
        float degree = Math.round(event.values[0]);

        tvHeading.setText("Heading: " + Float.toString(degree) + " degrees");

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // not used
    }
}

Here is a video of it working:

I am now created all the applications and calculations I need to do my final application.

So I then created an application to get my current GPS (without pushing a button) and update it every time I change location. I then added my heading application so that I have both current GPS coordinates and heading.
I then added my “getDistanceBearing class with the two methods ” getDistance” and “getBearing” and called them when ever my location changed so that I recalculate the bearing and heading.

I then added two text views so that the user can enter the destination location coordinates.
Next I changed from using one background colour to control the motors to using two different coloured boxes to control the left and right motors independently.

Here is a screen shot of the layout:

Screenshot from 2014-05-06 23:02:47

Here is the xml layout file:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="1">

    <TextView
        android:id="@+id/Heading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10sp"
        android:text="Heading: unknown"
        android:textSize="15sp"/>

    <TextView
        android:id="@+id/currlat"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="Curr Lat: unknown"
        android:textSize="15sp" />

    <TextView
        android:id="@+id/currlon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="Curr Long: unknown"
        android:textSize="15sp" />

    <TextView
        android:id="@+id/Bearing"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="Bearing: unknown"
        android:textSize="15sp"/>

    <TextView
        android:id="@+id/Distance"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="Distance: unknown"
        android:textSize="15sp"/>
    <TextView
        android:id="@+id/Direction"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginBottom="10dp"
        android:text="Direction: unknown"
        android:textSize="15sp"/>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="80dp"
        android:layout_marginBottom="100dp"
        android:orientation="horizontal"
        android:padding="3sp" >

        <EditText
            android:id="@+id/Destlat"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5sp"
            android:layout_weight="1.0"
            android:hint="Dest lat"
            android:inputType="numberDecimal|numberSigned"
            android:lines="1" />

        <EditText
            android:id="@+id/Destlon"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5sp"
            android:layout_weight="1.0"
            android:hint="Dest Lon"
            android:inputType="numberDecimal|numberSigned"
            android:lines="1" />

        <Button
            android:id="@+id/latlong_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5sp"
            android:text="Go!" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/LinearLayout02"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:layout_alignParentBottom="true">

        <LinearLayout
            android:id="@+id/left_layout"
            android:orientation="horizontal"
            android:layout_width="90dp"
            android:layout_height="220dp"
            android:layout_weight="1">
        </LinearLayout>

        <LinearLayout
            android:id="@+id/right_layout"
            android:orientation="horizontal"
            android:layout_width="90dp"
            android:layout_height="220dp"
            android:layout_weight="1">
        </LinearLayout>

    </LinearLayout>

</LinearLayout>

Here is my main java file:


package com.displaygps2.app;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;


public class MainActivity extends Activity implements SensorEventListener, LocationListener, android.view.View.OnClickListener{
    // Declare some variables
    public TextView latitudeField;
    public TextView longitudeField;
    public TextView DestlatField;
    public TextView DestlonField;
    public TextView bearingField;
    public TextView distanceField;
    public TextView Heading;
    public TextView Direction;

    LinearLayout left_motor;
    LinearLayout right_motor;

    public LocationManager locationManager;
    public String provider;
    public SensorManager mSensorManager;

    public int heading;
    public int bearing;
    public double distance;
    public int direction;
    public double destlat;
    public double destlon;



    /** Called when the activity is first created. */

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        // setup backgrounds to control motors
        left_motor = (LinearLayout) findViewById(R.id.left_layout);
        right_motor = (LinearLayout) findViewById(R.id.right_layout);

        latitudeField = (TextView) findViewById(R.id.currlat);
        longitudeField = (TextView) findViewById(R.id.currlon);
        DestlatField = (TextView) findViewById(R.id.Destlat);
        DestlonField = (TextView) findViewById(R.id.Destlon);
        bearingField = (TextView) findViewById(R.id.Bearing);
        distanceField = (TextView) findViewById(R.id.Distance);

        Heading = (TextView) findViewById(R.id.Heading);
        Direction = (TextView) findViewById(R.id.Direction);
        View goButton = findViewById(R.id.latlong_button);
        goButton.setOnClickListener(this);


        // Get the location manager & sensor manager
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);


        // Define the criteria how to select the location provider
       Criteria criteria = new Criteria();
        provider = locationManager.getBestProvider(criteria, false);
       Location location = locationManager.getLastKnownLocation(provider);

        // Initialise the location fields //
        if (location != null) {
            System.out.println("Provider " + provider + " has been selected.");
            onLocationChanged(location);
        } else {
            latitudeField.setText("Current location not available");
            longitudeField.setText("Current location not available");
        }
    }

    /* Request updates at startup */
    @Override
    protected void onResume() {
        super.onResume();
        locationManager.requestLocationUpdates(provider, 100, 1, this); // 30seconds

        // for the system's orientation sensor registered listeners
        mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
                SensorManager.SENSOR_DELAY_GAME);
    }

    /* Stop the location listener updates when Activity is paused */
    @Override
    protected void onPause() {
        super.onPause();
        locationManager.removeUpdates(this);

        // stop the listener to save battery
        mSensorManager.unregisterListener(this);
    }


    @Override
    public void onLocationChanged(Location location) {
        double currlat = (float) (location.getLatitude());
        double currlon = (float) (location.getLongitude());
        latitudeField.setText(String.valueOf("Curr Lat: " + currlat));
        longitudeField.setText(String.valueOf("Curr Long: " + currlon));

        bearingDistance b = new bearingDistance(currlat, currlon, destlat,destlon);
            b.getbearing();
            b.getdistance();

            bearing = b.getbearing();
            distance = b.getdistance();

        bearingField.setText(String.valueOf("Bearing: " + b.getbearing() + " Degrees"));
        distanceField.setText(String.valueOf("Distance: " + b.getdistance() + " Meters"));



    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onProviderEnabled(String provider) {
        Toast.makeText(this, "Enabled new provider " + provider,
                Toast.LENGTH_SHORT).show();

    }

    @Override
    public void onProviderDisabled(String provider) {
        Toast.makeText(this, "Disabled provider " + provider,
                Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        // get the angle around the z-axis rotated
        int degree = Math.round(event.values[0]);

        Heading.setText("Heading: " + Float.toString(degree) + " degrees");

        direction = Math.abs(bearing - heading);
        Direction.setText("Direction to Turn: " + direction + "degrees");

        heading = degree;

        if (heading > bearing){
            turn_left();
            if (direction < 10){
                go_straight();
            }
            if (distance < 1){
                stop();
            }
        }
        if (heading < bearing){
           turn_right();
            if (direction < 10){
                go_straight();
            }
            if (distance< 1){
               stop();
            }
        }

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // not used
    }


    /** Motor Control **/
    public void go_straight(){
        right_motor.setBackgroundColor(Color.parseColor("#FFFFFF"));
        left_motor.setBackgroundColor(Color.parseColor("#FFFFFF"));
    }
    public void turn_left(){
        right_motor.setBackgroundColor(Color.parseColor("#FFFFFF"));
        left_motor.setBackgroundColor(Color.parseColor("#000000"));
    }
    public void turn_right(){
        right_motor.setBackgroundColor(Color.parseColor("#000000"));
        left_motor.setBackgroundColor(Color.parseColor("#FFFFFF"));
    }
    public void stop(){
        right_motor.setBackgroundColor(Color.parseColor("#000000"));
        left_motor.setBackgroundColor(Color.parseColor("#000000"));
    }

    @Override
    public void onClick(View v) {

        // Read the latitude and longitude from the input fields when button is pressed
        EditText latText = (EditText) findViewById(R.id.Destlat);
        EditText lonText = (EditText) findViewById(R.id.Destlon);
        String latString = latText.getText().toString();
        String lonString = lonText.getText().toString();

        destlat = Double.parseDouble(latString);
        destlon = Double.parseDouble(lonString);

    }
}

Advertisements

Motion Tracking on the Cheap with a PIC

Hackaday

motion tracking

Ever need a cheap motion tracker for very basic object following? Did you know you can throw one together with a few IR distance sensors and a PIC?

The setup is fairly simple. [Aron Horan] is using a dsPIC30F4011 PIC, a SHARP infrared distance sensor, an RC servo, and a PICkit2 for testing. It works by scanning left and right using the servo motor. When the edge of an object is detected, it will turn away from the object until it can no longer detect the edge — then it turns back. Unfortunately this does mean it will always be twitching, even when it’s tracking an object.

Like many of the other projects [Aron] has documented, he’s included everything you need to know to be able to recreate the project yourself. Flowcharts, wiring diagrams, and the code — written in C of course! The following video includes an excellent demonstration, but…

View original post 37 more words

Bluetooth Controlled Car

In this project I plan to control a robot via bluetooth using a serial bluetooth module the hc-05.
I firstly wanted to get familiar with changing the state of the robot with a hard wired serial to USB device. I built a simple circuit with three LED’s and code that has three different states; one state for each LED to be turned on, then a python script to send different characters via a USB to serial port.

Here is a circuit diagram of the system:

LEDserial

Here is a picture of the circuit:

IMG_3431

IMG_3429

Here is my code:


//
// Testing serial control with USB serial to dsPIC30F4011
// Written by Aron Horan
// 10 Dec 2013
//
 
#include <xc.h>
#include <libpic30.h>
 
// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, i.e. 30 MIPS
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

 
int main(void)
{
	int state = 0;
    char g, h, j, c; 
 
    TRISB = 0;
    
    // Configure UART
	U1BRG = 48;            // 38400 baud @ 30 MIPS
	U1MODEbits.UARTEN = 1; // Enable UART
	

	while(1)
	{
		// Check if any characters were received via UART
        if (U1STAbits.URXDA == 1)     
        {
            // Check that first character was 'f,g or h'
            c = U1RXREG;
            if (c == 'g') state = 1;
            else if (c == 'h') state = 2;
            else if (c == 'j') state = 3;
        }

		//LED on RD0
		if (state == 1) 
		{
			LATB = 0b001;
		}
		
		//LED on RD1
		if (state == 2)
		{
			LATB = 0b010;
		}
		
		//LED on RD2
		if (state == 3)
		{
			LATB = 0b100;
		}
	}
	return 0;
}

I firstly wanted to setup the python to send a char every three seconds, after I get that going I will change it so that it will take inputs from my keyboard.
I also am using a USB to serial port for the moment, once I get that up and running I will setup the bluetooth serial port and control remotely.

Here is my python code that sends a different char every three seconds:


import time
import serial

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

	print "g"
	ser.write("g")
	time.sleep(3)
	
	print "h"
	ser.write("h")
	time.sleep(3)
	
	print "j"
	ser.write("j")
	time.sleep(3)


Here is a video of it in action:

Now that I have that working I have edited the python script to recieve inputs from the keyboard to change state.

Here is the new python code:


import time
import serial

# configure the serial connections (the parameters differs on the device you are connecting to)
ser = serial.Serial(
    port='/dev/ttyUSB0',
    baudrate=38400,
   
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS
)
ser.close()
ser.open()
	
while True:

#print 'Enter your commands below.\r\nInsert "exit" to leave the application.'
	
	# get keyboard input
	input = raw_input(">> ")
        # Python 3 users
        # input = input(">> ")
	if input == 'exit':
		ser.close()
		exit()
	else:
		# send the character to the device
		# (note that I happend a \r\n carriage return and line feed to the 			characters - this is requested by my device)
		ser.write(input + '\r\n')
		out = ''
		
		# wait half a second before reading output (give device time 			 	     # to answer)
		time.sleep(0.5)
		while ser.inWaiting() > 0:
			out += ser.read(1)
			
		if out != '':
			print ">>" + out

The only thing is the I must press enter (return) for the char to be sent. My next stage I will overcome this problem. Also instead of using the USB to serial port I will be using a serial Bluetooth device so I can transmit wirelessly.

So I finally got it working.

Its wirelessly switching a few LEDs (wow!) but now I have the power to do a lot more with it.

I ended up using Tkinter to create a canvas with 3 buttons. Each button sends a different character to the PIC, changing it between three different states.

My C programm is exactly the same as above the only difference is the python script.

Here is my Python script:


import time
import serial
import Tkinter
 
# Define a keyboard callback function. This will be called
# every time a key is pressed anywhere in the window.
def key(event):
    print "Sending", repr(event.char)
    ser.write(event.char)
 
# Callback function for the 'A' button
def a_press():
    print "Sending 'g'"
    ser.write('g')
 
# Callback function for the 'B' button
def b_press():
    print "Sending 'h'"
    ser.write('h')
 
# Callback function for the 'C' button
def c_press():
    print "Sending 'j'"
    ser.write('j')
 
# Open first serial port
ser = serial.Serial(port='/dev/rfcomm0', baudrate = 9600, timeout=1)
 
# Create a Tk GUI window
root = Tkinter.Tk()

# create canvas with a width and height
Tkinter.Canvas(root, width=600, height=600)

# Add title to window
root.title("ARONS BLUETOOTH RC CAR")
 
# Bind all keypresses to the "key" function (define above)
root.bind("<Key>", key)
 
# Create a few buttons and connect each one to a function
a = Tkinter.Button(root, text='A', command=a_press)
b = Tkinter.Button(root, text='B', command=b_press)
c = Tkinter.Button(root, text='C', command=c_press)
 
# Lay out buttons in the GUI
a.grid(row=0, column=0, sticky='EW')
b.grid(row=1, column=0, sticky='EW')
c.grid(row=2, column=0, sticky='EW')
root.columnconfigure(0, weight=1)
 
# Enter the Tkinter main event loop. The program will stay
# in this event loop until it's time to exit.
root.mainloop()
 
# Close serial port
ser.close()
	

Here is a video of it in action:

Having awful trouble with my bluetooth device, possible blew it somehow 😦 gonna order a new one and will get back to this project when it arrives.

The Dodging Robot

IMG_3562

In this Project I plan on remotely controlling a two-wheeled car I built via bluetooth.In this project I wanted to make a small two wheeled robot that drives around dodging evrything infront of it.
I mounted two SHARP IR Distance sensors to the front so that the robot can decide which way to turn if an object triggers either sensor.

I used an 18 pin dsPIC30F3012 for this as I wanted to fit the dsPIC and driver chip SN754410NE onto one small breadboard, also the dsPIC30F3012 had all the io I needed.

I used state’s for each circumstance the robot can be in, in each state there are conditions that if occur the program will change to a different state.

The states of the code go as follows:

StateMachineTable

The output voltage of the infrared sensor was then measured at different distances so that I could decide at what distance I wanted it to turn.
The results were as follows:

Capture

Plotted on a graph it looked like this:

Capture1

Here is a wiring diaram of the circuit:

rect4922

Here is my code:


// dsPIC30F3012
// The dodgy Robot
// Written by Aron Horan
// The plan of this project is to make a two wheeled robot with two IR sensors
// mounted to the front. The robot will simply drive around dodging objects.
// If the two sensors have a low reading the robot will drive straight,
// if the left sensror goes high and right is low the robot turns right and vise versa,
// if either sensor goes very high the robot will reverse for a second and turn around

#include <xc.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

// Function prototypes
unsigned int read_analog_channel(int);

int main()
{
	const turn_right = 0, turn_left = 1, forward = 2, turn_around = 3;
	int state = forward;
	int left_eye;
	int right_eye;
	int sw = 0;
	
	// Configure UART
	U1BRG = 48;            // 38400 baud @ 30 MIPS
	U1MODEbits.UARTEN = 1; // Enable UART
	

	// Make RB0-3 outputs
	TRISB = 0b1111111111110000;

	// Configure analog inputs
	ADPCFG = 0b1111111100111111;// Only AN6 and AN7 are analog inputs
	ADCON1 = 0;                 // Manually clear SAMP to end sampling, start conversion
	ADCON2 = 0;                 // Voltage reference from AVDD and AVSS
	ADCON3 = 19;            	// Manual Sample, ADCS=9 -> Tad = 10*Tcy = 333ns
	ADCON1bits.ADON = 1;        // Turn ADC ON

	
	while(1)
	{
		left_eye = read_analog_channel(6); 	// read infrared detector
		right_eye = read_analog_channel(7); // read infrared detector
	
	   
       if (state == forward)
       {
            LATB = 0b1010; // motors: left forward, right forward
            if (left_eye > 900 && right_eye < 900) state = turn_right;
            if (left_eye < 900 && right_eye > 900) state = turn_left;
            if (left_eye > 2200 || right_eye > 2200) state = turn_around;
       }
       
       else if (state == turn_right)
       {
              LATB = 0b1000; // motors: left stop, right forward
              if (left_eye < 900 && right_eye < 900) state = forward;
              if (left_eye > 2200 || right_eye > 2200) state = turn_around;
       }
       
       else if (state == turn_left)
       {
            LATB = 0b0010; // motors: left forward, right stop
            if (left_eye < 900 && right_eye < 900) state = forward;
            if (left_eye > 2200 || right_eye > 2200) state = turn_around;
       }
       else if (state == turn_around)
       {
            LATB = 0b0101; // motors: left reverse, right reverse
            __delay32(40000000);
            LATB = 0b0110; // motors: left reverse, right reverse
            __delay32(30000000);
            state = forward;
       }
       	    __delay32(300000);
	}
	return 0;
}

// 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 finished piece:

IMG_3402

IMG_3403

IMG_3404

IMG_3406

IMG_3407

I will have a video of it in action very shortly, im just low on batteries at the moment and can only run it while plugged in, which wouldn’t make a good video…

Had a bit of a disaster with this little poor fellow. Afer driving from Greystones to Dublin on my bike I Arrived into college to find my bag zip open… Dodgbot was gone…

So without further wait I bring to you…..

DODGBOT V2.0IMG_3554IMG_3556

IMG_3558

IMG_3560

IMG_3562

IMG_3554

I ended up having to buy a set of AA NI-MH rechargable batteries as it kept resetting when the motors switched into reverse due to the curent surge and voltdrop.

Still even with these new 1700mAH batteries it was still resetting, so I ended up using a heap of capacitors (2160uF) is was done the trick.

So finally here is a video of it in action:

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 &amp;amp; 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 &amp;amp; 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&amp;lt;800 ; ++n)
			{
				printf(&quot;%d &quot;, distance[n]);
			}
			printf(&quot;\n&quot;);
		}

		__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 &amp;quot;echo&amp;quot; 
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 &lt;xc.h&gt;
#include &lt;libpic30.h&gt;
#include &lt;stdio.h&gt;
#include &lt;math.h&gt;
 
// Configuration settings
_FOSC(CSW_FSCM_OFF &amp; 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 &amp; 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 -&gt; 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&lt;800 ; ++n)
			{
				printf(&quot;%d &quot;, distance[n]);
			}
			printf(&quot;\n&quot;);
		}

		__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!