Introduction: Cheap Robot With Arduino

About: I used to be a developer for Instructables. I probably made something around here.
I wanted to learn electronics, Arduino programming, and soldering so I figured I'd pick one of Randy's projects and copy it.

I ended up with a different motor configuration. My front motor points down and turns a little quarter gear which then turns the front wheels. This meant that none of Randy's code was applicable to my project.

I started looking into the somewhat icky world of Arduino programming. It's all global variables and single file projects. I found it difficult to understand many of the examples available online, not because they were particularly difficult concepts, but because they were such poor examples of programming that my refined senses could not make sense of it.

Also the Arduino programming environment leaves a lot to be desired. That lead me to the excellent embedXcode project, which enabled me to use XCode in stead.

Once I had a nice environment I started looking at better ways of writing code for the Arduino. Turns out the whole thing is just C++, which I know plenty about. The MotorControl class I wrote, which makes use of a DasPing class, are both below.

Cleaning up the code like this enabled my final Arduino sketch to look like the following:

#include "Arduino.h"

#include "DasPing.h"
#include "MotorControl.h"

MotorControl *mc;

void setup() {
    Serial.begin(9600);
    mc = new MotorControl(7, 100, 125);
    mc->setUp();
}

void loop() {
    mc->checkDistance();
    if (mc->distance > 12 || mc->distance == 0) {
        mc->straighten();
        mc->forward(mc->getBaseSpeed());
    } else {
        mc->right();
        mc->backward();
    }
}
You can see the actual behavior of the robot there. Abstracting the MotorControl and DasPing classes, to control movement and sense distance respectively, enabled me to work with the much more fun problem of bringing a robot to life.

The basic behavior shown in this video is derived from approximately 10 lines of code, once you abstract out everything you don't need to worry about.

Here are the MotorControl and DasPing class definitions, followed by their respective implementation files.

MotorControl.h

//
//  MotorControl.h
//
//
//  Created by David Worley on 1/1/13.
//
//

#ifndef ____MotorControl__
#define ____MotorControl__

#include "Arduino.h"
#include "DasPing.h"

class MotorControl
{
public:
    MotorControl(int pingPin, int defaultDelay, int baseSpeed);
    void setUp();
    
    int distance;
    
    int getBaseSpeed();
    void setBaseSpeed(int baseSpeed);
    void forward(int goSpeed);
    void backward();
    void left();
    void right();
    void stop();
    void checkDistance();
    
    void easeToBaseSpeed();
    void slowFromBaseSpeed();
    
    void straighten();
protected:
    int _baseSpeed;
    int _turnSpeed; // for controlling the turn motor
    int _distance;
    int _defaultDelay;
    DasPing *_pinger;
    
    // motor 1, turning
    int _brake1;
    int _direction1;
    int _speed1;
    
    // motor 2, forward & reverse
    int _brake2;
    int _direction2;
    int _speed2;
};

#endif /* defined(____MotorControl__) */

MotorControl.cpp

//
//  MotorControl.cpp
//
//  A class for controlling a particular motor configuration
//  using the Arduino motor shield and a Parallax Ping sensor.
//
//  Created by David Worley on 1/1/13.
//
//

#include "MotorControl.h"

MotorControl::MotorControl(int pingPin, int defaultDelay, int baseSpeed)
{
    Serial.println("MotorControl init.");
    _pinger = new DasPing(pingPin);
    this->checkDistance();

    _defaultDelay = defaultDelay;
    this->setBaseSpeed(baseSpeed);
    
    // since we're using a specific configuration, we know
    // what values to use based on the motor shield documentation
    _brake1 = 9;
    _direction1 = 12;
    _speed1 = 3;
    
    _brake2 = 8;
    _direction2 = 13;
    _speed2 = 11;
    
    _turnSpeed = 255; // set to max for the turn motor
}

void MotorControl::setUp()
{
    Serial.println("MotorControl setUp.");
    // establish motor direction pins
    pinMode(_direction1, OUTPUT);
    pinMode(_direction2, OUTPUT);
    
    // brakes
    pinMode(_brake1, OUTPUT);
    pinMode(_brake2, OUTPUT);
    
    digitalWrite(_brake1, HIGH);
    digitalWrite(_brake2, HIGH);
}

void MotorControl::forward(int goSpeed)
{
    digitalWrite(_brake2, LOW);
    digitalWrite(_direction2, LOW);

    analogWrite(_speed2, goSpeed);
    
    delay(_defaultDelay);
}

void MotorControl::backward()
{
    digitalWrite(_brake2, LOW);
    digitalWrite(_direction2, HIGH);
    
    analogWrite(_speed2, 155); // back up at high speed
    
    delay(_defaultDelay);
}

void MotorControl::left()
{
    digitalWrite(_brake2, LOW);
    digitalWrite(_direction2, HIGH);
    analogWrite(_speed2, _turnSpeed);
    
    delay(_defaultDelay);
}

void MotorControl::right()
{
    digitalWrite(_brake1, LOW);
    digitalWrite(_direction1, LOW);
    analogWrite(_speed1, _turnSpeed);
}

void MotorControl::stop()
{
    digitalWrite(_brake1, HIGH);
    digitalWrite(_brake2, HIGH);
    
    analogWrite(_speed1, 0);
    analogWrite(_speed2, 0);
    
    delay(_defaultDelay);
}

void MotorControl::straighten()
{
    digitalWrite(_brake1, HIGH);
    analogWrite(_speed1, 0);
}

void MotorControl::checkDistance()
{
    _pinger->pingDistance();
    _distance = _pinger->distance();
    distance = _distance;
}

int MotorControl::getBaseSpeed()
{
    return _baseSpeed;
}
void MotorControl::setBaseSpeed(int baseSpeed)
{
    _baseSpeed = baseSpeed;
}

void MotorControl::easeToBaseSpeed()
{
    for (int i = 0; i <= _baseSpeed; i++) {
        forward(i);
        delay(100); // every 1/10th of a second, speed up
    }
}

void MotorControl::slowFromBaseSpeed()
{
    for (int i = _baseSpeed; i >= 0; i = i - 10) {
        forward(i);
        delay(50);
    }
    this->stop();
}

DasPing.h

//
//  DasPing.h
//  
//
//  Created by David Worley on 1/2/13.
//
//

#ifndef ____DasPing__
#define ____DasPing__

#include "Arduino.h"

class DasPing
{
public:
    DasPing(int pingPin);
    void pingDistance();
    int distance();
protected:
    int _distance;
    int _pin;
    int microsecondsToInches(long microseconds);
};

#endif /* defined(____DasPing__) */

DasPing.cpp

//
//  DasPing.cpp
//
//  A class for controlling the Parallax Ping sensor
//
//  Created by David Worley on 1/2/13.
//
//

#include "DasPing.h"

DasPing::DasPing(int pingPin)
{
    _distance = 0;
    _pin = pingPin;
}

void DasPing::pingDistance()
{
    int duration;
    
    // The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
    // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
    pinMode(_pin, OUTPUT);
    digitalWrite(_pin, LOW);
    delayMicroseconds(2);
    digitalWrite(_pin, HIGH);
    delayMicroseconds(5);
    digitalWrite(_pin, LOW);
    
    // The same pin is used to read the signal from the PING))): a HIGH
    // pulse whose duration is the time (in microseconds) from the sending
    // of the ping to the reception of its echo off of an object.
    pinMode(_pin, INPUT);
    duration = pulseIn(_pin, HIGH);
    
    // convert the time into a distance
    _distance = this->microsecondsToInches(duration);
}

int DasPing::distance()
{
    return _distance;
}

int DasPing::microsecondsToInches(long microseconds)
{
    // According to Parallax's datasheet for the PING))), there are
    // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
    // second).  This gives the distance travelled by the ping, outbound
    // and return, so we divide by 2 to get the distance of the obstacle.
    // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
    return microseconds/74/2;
}