Phone Holder for Android Robot

This entry is part 6 of 10 in the series Android Robot

Phone Holder for Android Robot


Fashioned a phone holder out of an old phone holder for my car. Note that the phone front camera (which will be used for robot navigation and localization) has a clear view of the scene in front of the robot. The rear camera looks back and up and will be used to interact with people.

Obstacle Avoidance Using an Infrared Proximity Sensor

This entry is part 5 of 10 in the series Android Robot

The Sharp GP2Y0A21YK is an infrared (IR) proximity sensor available from Little Bird Electronics for about $15 and this was the 2nd sensor I attempted in getting the Android robot (shall we christen him Bug?) to avoid obstacles.
The sensor outputs an analog signal between 0.4V and 3.1V to indicate distances to obstacles from 80 cm away to 10 cm away.

Watch a video of Bug avoiding obstacles using a single IR sensor by clicking on the pic above.

The first sensor I used was an ultrasonic sensor, the Parallax Ping available for $35. This sensor has a much longer range (2cm – 3m). It has a digital input and the input (trigger) signal is followed by the return (pulse width indicates distance to target) output on the same pin. I was not able to get input and output signals on the same pin working with the IOIO board, and from the forums, this seems to be a problem other people have had too.

The Sharp IR sensor has 3 leads: +ve, GND and signal. Connect the +ve and GND leads from the sensor to the 3.3V and GND pins of the IOIO board. Connect the analog signal lead to pin 34 on the IOIO (peripheral input output pin).

Once you’ve connected the sensor to the IOIO, you have to write and download an app to your phone that does the sensing and obstacle avoidance.

The following is a code snippet that does this:

/**
 * Modified from code from the book Getting Started with IOIO (2011 Simon Monk)
 * and code from http://mitchtech.net/android-ioio-range-finder/
 * by Jay Chakravarty 2013
 *
 * Simple program for Obstacle Avoidance using one Infrared Sensor
 */

package com.ioiobook.rover;

import ioio.lib.api.AnalogInput;
import ioio.lib.api.DigitalOutput;
import ioio.lib.api.PwmOutput;
import ioio.lib.api.exception.ConnectionLostException;
//import ioio.lib.util.AbstractIOIOActivity;

import ioio.lib.util.BaseIOIOLooper;
import ioio.lib.util.IOIOLooper;

import ioio.lib.util.android.IOIOActivity;

import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.Window;

import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;


public class MainActivity extends IOIOActivity {

    private static final int AIN1_PIN = 41;
    private static final int AIN2_PIN = 40;
    private static final int PWMA_PIN = 39;
    private static final int PWMB_PIN = 45;
    private static final int BIN2_PIN = 44;
    private static final int BIN1_PIN = 43;
    private static final int STBY_PIN = 42;
    private static final int LED_PIN = 0;
    
    private static final int INFRARED_PIN = 34;//infrared analog input


    private static final int PWM_FREQ = 100;

    private float left_ = 0;
    private float right_ = 0;
    
    TextView mXTextView, mYTextView;

	SeekBar mXSeekBar, mYSeekBar;
	
    private float infraredOutputVolts = 0;



    /**
     * Called when the activity is first created. Here we normally initialize
     * our GUI.
     */
   @Override
    public void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    
		setContentView(R.layout.main);
		mXTextView = (TextView) findViewById(R.id.tvX);
		mXSeekBar = (SeekBar) findViewById(R.id.sbX);
	    


	    

    }

    /**
     * This is the thread on which all the IOIO activity happens. It will be run
     * every time the application is resumed and aborted when it is paused. The
     * method setup() will be called right after a connection with the IOIO has
     * been established (which might happen several times!). Then, loop() will
     * be called repetitively until the IOIO gets disconnected.
     */
    class Looper extends BaseIOIOLooper {
        private DigitalOutput ain1_;
        private DigitalOutput ain2_;
        private PwmOutput pwma_;
        private PwmOutput pwmb_;
        private PwmOutput led_;
        private DigitalOutput bin2_;
        private DigitalOutput bin1_;
        private DigitalOutput stby_;
        
        private AnalogInput mXInput;

        /**
         * Called every time a connection with IOIO has been established.
         * Typically used to open pins.
         * 
         * @throws ConnectionLostException
         *             When IOIO connection is lost.
         * 
         * @see ioio.lib.util.AbstractIOIOActivity.IOIOThread#setup()
         */
        @Override
        protected void setup() throws ConnectionLostException {
        	
        	try{
	            ain1_ = ioio_.openDigitalOutput(AIN1_PIN);
	            ain2_ = ioio_.openDigitalOutput(AIN2_PIN);
	            pwma_ = ioio_.openPwmOutput(PWMA_PIN, PWM_FREQ);
	            pwmb_ = ioio_.openPwmOutput(PWMB_PIN, PWM_FREQ);
	            //pwma_ = ioio.openPwmOutput(new DigitalOutput.Spec(PWMA_PIN, OPEN_DRAIN), PWM_FREQ);
	            
	            
	            bin2_ = ioio_.openDigitalOutput(BIN2_PIN);
	            bin1_ = ioio_.openDigitalOutput(BIN1_PIN);
	            stby_ = ioio_.openDigitalOutput(STBY_PIN);
	            stby_.write(true); // On
	            led_  = ioio_.openPwmOutput(LED_PIN,PWM_FREQ);
	            
	            mXInput = ioio_.openAnalogInput(INFRARED_PIN);
        	} catch (ConnectionLostException e) {
				throw e;
			}
        }

        /**
         * Called repetitively while the IOIO is connected.
         * 
         * @throws ConnectionLostException
         *             When IOIO connection is lost.
         * @throws InterruptedException
         * 
         * @see ioio.lib.util.AbstractIOIOActivity.IOIOThread#loop()
         */
        
        public void loop() throws ConnectionLostException {
        		led_.setDutyCycle((float)0.9);
        		
        		
        		if( infraredOutputVolts*100 > 40 )
        		{
        			//Obstacle very close, turn tracks in opposite directions (turns left on the spot)
        			pwma_.setDutyCycle((float)0.8);
            		ain1_.write(true);
            		ain2_.write(false);
            	
            		pwmb_.setDutyCycle((float)0.8);
            		bin1_.write(false);
            		bin2_.write(true);
        		}
        		else if( infraredOutputVolts*100 > 25 )
        		{
        			//Obstacle a little further away, still turn
        			pwma_.setDutyCycle((float)0.0);
            		ain1_.write(false);
            		ain2_.write(true);
            		
            		pwmb_.setDutyCycle((float)0.8);
            		bin1_.write(false);
            		bin2_.write(true);
        		}
        		else
        		{
        			//No obstacles in immediate vicinity, move straight
        			pwma_.setDutyCycle((float)0.8);
            		ain1_.write(false);
            		ain2_.write(true);
            		
            		
            		pwmb_.setDutyCycle((float)0.8);
            		bin1_.write(false);
            		bin2_.write(true);
        		}
        		
        		   

            try {
            	infraredOutputVolts = mXInput.read();         
            	setSeekBars((int) (infraredOutputVolts * 100));

				setText(Float.toString((infraredOutputVolts * 100)));
            	Thread.sleep(10);
        	} catch (InterruptedException e) {
			}
        }

    }

    /**
     * A method to create our IOIO thread.
     * 
     * @see ioio.lib.util.AbstractIOIOActivity#createIOIOThread()
     */
    @Override
    protected IOIOLooper createIOIOLooper() {
        return new Looper();
    }
    
    private void setSeekBars(final int xValue) {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				mXSeekBar.setProgress(xValue);
			}
		});
	}

	private void setText(final String xText) {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				mXTextView.setText(xText);
			}
		});
	}

}