Newbie Project Code advice

Just started playing around with the uno I got from radio shack. i snagged an old RC car from goodwill and started the rewire. connected up a motor shield to control the motors attached a servo and mounted a ping sensor. Wow seemed easy enough to assemble.
Thru trial and error I banged out a bit of code and lo and behold ‘George’ is on the move.

Quick bit of advice needed. while sweeping the servo I want to take 5 pings(far left,near left, center, near right,far right)

1 what is the easiest way to compartmentalize the ping portion in a subroutine to call at each stop of the servo and will I be able to use a the switch command inside my if loop

ignore the odd call to the brakes, forgot i disabled those for the extra pins and never cleared the code yet

/*
George
by Daniel Glynn




Built atop of
RC Car to Robot Conversion
 by Randy Sarafan
 
 Used to convert an RC car into a robot that uses a PING sensor to avoid obstacles,
 and an Arduino motor shield for motor control.
 
 For more information see:
 http://www.instructables.com/id/RC-Car-to-Robot/
 
 Built atop Ping example code by Tom Igoe
 */

// this constant won't change.  It's the pin number
// of the sensor's output:
#include <Servo.h>
Servo servoMain; // Define servo
const int pingPin = 7;

void setup() {
  // initialize serial communication:
  Serial.begin(9600);
  //establish servo pin
  servoMain.attach(10); // servo on digital pin 10
  //establish motor direction toggle pins
  pinMode(12, OUTPUT); //motor a -- HIGH = forwards and LOW = backwards
  pinMode(13, OUTPUT); //motor b -- HIGH = forwards and LOW = backwards

  //establish motor brake pins and disengage brakes
  pinMode(9, OUTPUT); //brake (disable) the drive motor a
  pinMode(8, OUTPUT); //brake (disable) the turn motor b
  digitalWrite(9, LOW);   //Turns brake Off for motor a
  digitalWrite(8, LOW);   //Turns brake Off for motor b

  //Sets initial speed of drive motor
  analogWrite(3,135);
  analogWrite(11, 135);

  //Sets initial direction of drive motor
  digitalWrite(12, HIGH);
  digitalWrite(13, HIGH);
  servoMain.write(90);   // Center Servo
}

void loop()
{
  // establish variables for duration of the ping, 
  // and the distance result in inches and centimeters:
  long duration, inches, cm;

  // 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(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, 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(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);

  // convert the time into a distance
  inches = microsecondsToInches(duration);

  //
  //if objects are less than 12 inches away
  //the robot reverses and turns to the right
  //for 2 seconds
  //

  if (inches < 15){

    //stop and wait a tic
    analogWrite(3, 0);
    analogWrite(11, 0);
    digitalWrite(12, LOW);
    digitalWrite(13, LOW);
    analogWrite(3, 120);
    analogWrite(11, 120);
    delay(300);
    analogWrite(3, 0);
    analogWrite(11, 0);
    delay(200);

    servoMain.write(90);   // Turn Servo Left to 0 degrees
    delay(500);          // Wait .1 second
    servoMain.write(35);  // Turn Servo Left to 45 degrees
    delay(500);          // Wait .1 second
    servoMain.write(65);  // Turn Servo Left to 45 degrees
    delay(500);          // Wait .1 second
    servoMain.write(90);   // Turn Servo Left to 0 degrees
    delay(500);          // Wait .1 second
    servoMain.write(115);   // Turn Servo Left to 0 degrees
    delay(500);          // Wait .1 second
    servoMain.write(145);  // Turn Servo Left to 45 degrees
    delay(500);          // Wait .1 second
    servoMain.write(90);   // Turn Servo Left to 0 degrees
    delay(500);          // Wait .1 second

    //set motor a in reverse
    

    //rem never turned onturn off brake for turn motor 


    //activate turn motor
    analogWrite(3, 140);
    //backup for .5 seconds
    delay(500);


    //
    //stopping
    //

    //brake both motors
    analogWrite(3, 0);
    analogWrite(11, 0);


  }

  //
  //when nothing is within 12" 
  //the robot simply drives forwards
  //

  else{

    //
    //Setting drive motor
    //

    //set drive motor forward direction
    digitalWrite(12, HIGH);
    digitalWrite(13, HIGH);

    //turn off brake of drive motor
    digitalWrite(9, LOW);   
    digitalWrite(8, LOW);   

    //activate drive motor
    analogWrite(3, 135);
    analogWrite(11, 135);


  }

  delay(300);
}

long microsecondsToInches(long microseconds)
{
  // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
  return microseconds / 74 / 2;
}

1 what is the easiest way to compartmentalize the ping portion in a subroutine to call at each stop of the servo and will I be able to use a the switch command inside my if loop

You call analogRead() and Servo.write() with no issues. Why can't (or don't) you develop a function that does the pinging, and call that?

    servoMain.write(65);  // Turn Servo Left to 45 degrees
    delay(500);          // Wait .1 second
    servoMain.write(90);   // Turn Servo Left to 0 degrees
    delay(500);          // Wait .1 second

If you are not going to maintain the comments, why are they there? They don't contribute anything.

long Ping () { pinMode(pingPin, OUTPUT); digitalWrite(pingPin, LOW); delayMicroseconds(2); digitalWrite(pingPin, HIGH); delayMicroseconds(5); digitalWrite(pingPin, LOW); pinMode(pingPin, INPUT);

return microsecondsToInches(pulseIn(pingPin, HIGH)); }

Then in your code just use Ping():

servoMain.write(65); // Turn Servo to Left 90 degrees delay(500); inchesLeft = Ping();

servoMain.write(35); // Turn Servo to Left 45 degrees delay(500); inchesNearLeft = Ping();

... and so on

I'd add perhaps another routine that calls this, and simplifies your code:

long ServoPing (byte direction) { servoMain.write(direction); delay(500); return Ping(); }

Your main code now can do: (not sure if I got the servo directions correct)

inchesLeft = ServoPing (65); inchesNearLeft = ServoPing (35); inchesForward = ServoPing (90); inchesNearRight = ServoPing (115); inchesRight = ServoPing (145);

Thanks for the insight.

Think late last night when i tried to code the function I kept telling It to give me nothing(used void).

Sorry about the poor commenting, shoulda fixed as I posted. Was a late night backup that I had emailed myself to fix at work.

Wished there was an easy download option to get code back out. Guess I'll just store it in my Dropbox folder from now on.

Will post a code update soon with the new code ideas.

here is the updated code. thanks again.now to work out the reverse decision portion.
figure i will average out the left near and far readings and the right near and far readings then decide which motor to drive with that and then the duration will be decided by if near or far is further, i think. unless someone sees an issue with that.

any other ideas?
anything easier?

/*
RC Car to Autonomous Metal Detecting Rover
 by Daniel Glynn
 
 Used to convert an RC car(actually wasn.t traditional RC 
 control option but had IR sensors on the four corners 
 and a hand held red and IR led light with a lens)
 into a Rover that uses a PING sensor to avoid obstacles,
 and an Arduino motor shield for motor control. evntual additions will be a PIR 
 detector, ring of edge detectors, and a metal detector sensor and loop
 
 help from arduino forum member 
 Techylah
 
 and initial code based on  
 Motor control example
 by Randy Sarafan
 For more information see:
 http://www.instructables.com/id/RC-Car-to-Robot/
 
 and he included
 Built atop Ping example code by Tom Igoe
 */

// this constant won't change.  It's the pin number
// of the sensor's output:
#include <Servo.h>
Servo servoMain; // Define servo
const int pingPin = 7;
int inchesLeft = 0;
int inchesNearLeft = 0;
int inches = 0;
int inchesNearRight = 0;
int inchesRight = 0;

void setup() {
  // initialize serial communication:
  Serial.begin(9600);
  //establish servo pin
  servoMain.attach(10); // servo on digital pin 10
  //establish motor direction toggle pins
  pinMode(12, OUTPUT); //motor a -- HIGH = forwards and LOW = backwards
  pinMode(13, OUTPUT); //motor b -- HIGH = forwards and LOW = backwards

  //establish motor brake pins and disengage brakes
  pinMode(9, OUTPUT); //brake (disable) the drive motor a
  pinMode(8, OUTPUT); //brake (disable) the turn motor b
  digitalWrite(9, LOW);   //Turns brake Off for motor a
  digitalWrite(8, LOW);   //Turns brake Off for motor b

  //Sets initial speed of drive motor

  //Sets initial direction of drive motor
  digitalWrite(12, HIGH);
  digitalWrite(13, HIGH);
  servoMain.write(90);   // Center Servo
}

void loop()
{

  inches = ServoPing (90);

  //
  //if objects are less than 15 inches away
  //the robot reverses and turns to the right
  //

  if (inches < 15){

    //stop and wait a tic
    fullstop();

    servosweep();

    //set motor a in reverse

  
    //activate turn motor
    analogWrite(3, 140);
    //backup for .5 seconds
    delay(750);


    //
    //stopping
    //

    //brake both motors
    analogWrite(3, 0);
    analogWrite(11, 0);


  }

  //
  //when nothing is within 12" 
  //the robot simply drives forwards
  //

  else{

    //
    //Setting drive motor
    //

    //set drive motor forward direction
    digitalWrite(12, HIGH);
    digitalWrite(13, HIGH);

    //turn off brake of drive motor
    digitalWrite(9, LOW);   
    digitalWrite(8, LOW);   

    //activate drive motor
    analogWrite(3, 135);
    analogWrite(11, 135);


  }

  delay(300);
}

long microsecondsToInches(long microseconds)
{
  // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
  return microseconds / 74 / 2;
}

long Ping ()
{
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);

  return  microsecondsToInches(pulseIn(pingPin, HIGH));
}

long  ServoPing (long direction)
{
  servoMain.write(direction);
  delay(300);
  return Ping();
}

void fullstop()
{
  analogWrite(3, 0);
  analogWrite(11, 0);
  digitalWrite(12, LOW);
  digitalWrite(13, LOW);
  analogWrite(3, 120);
  analogWrite(11, 120);
  delay(250);
  analogWrite(3, 0);
  analogWrite(11, 0);
  delay(300); 
}

void servosweep()
{
 inchesLeft = ServoPing (65);
    Serial.println(inchesLeft); 
    inchesNearLeft = ServoPing (35);
    Serial.println(inchesNearLeft); 
    inches = ServoPing (90);
    Serial.println(inches); 
    inchesNearRight = ServoPing (115);
    Serial.println(inchesNearRight); 
    inchesRight = ServoPing (145);
    Serial.println(inchesRight); 
    servoMain.write(90); 
}
 inchesLeft = ServoPing (65);
    inchesNearLeft = ServoPing (35);
    inches = ServoPing (90);
    inchesNearRight = ServoPing (115);
    inchesRight = ServoPing (145);

I do not understand the progression of values here (65, 35, 90, 115, 145). 35, 65, 90, 115, 145 would make sense.

An array to hold the distances would be useful, too, instead of discrete variables. An array to hold the angles would be useful. It would be easier to modify the positions using an array.

With arrays for position and distance, you could use a for loop to get the values.

It would then be easy to get the direction that corresponds to the greatest distance to the obstruction, or the direction that corresponds to the smallest distance.

I do not understand the progression of values here (65, 35, 90, 115, 145). 35, 65, 90, 115, 145 would make sense.

reason was my 11 year old thought is was too boring in a straight line so I showed her why it i didn’t like the look of different combos and forgot to put the values back(but does explain the jerk in the scan i noticed lately). will check out placing it in an array and fix the order

With arrays for position and distance, you could use a for loop to get the values.

Of course. It also makes it easy to change how many directions get checked. If the spread angle of the sensor is narrower than the servo jump, you could completely miss a narrow vertical obstruction like a lamp pole.

boolean debug_mode = true; // set to false for faster operation but no serial messages.

#define NUM_DIRECTIONS 5
byte direction[NUM_DIRECTIONS] = { 35, 65, 90, 115, 145 };
long distance[NUM_DIRECTIONS];

byte FindClearestDirection()
{
byte i, clearestDirection;
long longestDistance;

for (i=0; i<NUM_DIRECTIONS; i++)
{
distance[ i ] = ServoPing(direction[ i ]);
if (i==0 || distance[ i ] > longestDistance)
{
longestDistance = distance[ i ];
clearestDirection = i;
}

if (debug_mode)
{
Serial.print(direction[ i ];
Serial.println(distance[ i ]);
}
}

return clearestDirection;
}

Just out of curiosity, why does the function return an int, when the return type is long? Seems to me that the return type should be int. Or byte. How many unique directions can you reasonably detect?

The variable type for direction should not be long. There is no way that a servo can be positioned to more than 32,767 unique positions (or even 255 for that matter).

On an Arduino, with limited memory, conserving memory by using the smallest possible data type should be foremost in the mind of everyone developing code, and certainly should be given serious consideration if posting code.

Paul, you are exactly right. I am used to coding on a PC, where integers are 32bits and used synonomously with long, and where memory is abundant and free, almost without limit.

I don't know if this is generally done, but I corrected my code accordingly, leaving only the distances as longs.

Everyone should practice such memory conservation in the Arduino environment.

Tnx for correcting and not flaming me!

Tnx for correcting and not flaming me!

I didn't correct you. I pointed out some areas where you could correct yourself. Good job. Mostly.

leaving only the distances as longs.

The ServoPing() function calls the Ping() function which returns a long. So, your function is correct in that the array type matches the function return type. The problem is that the function return type is wrong. The last thing that Ping() does is call microsecondsToInches(), and there is just no way that the ping sensor is going to operate accurately at more than 32,767 inches. That's over half a mile.

So, the microsecondsToInches() function should not return a long, and none of the functions that use it should propagate that mistake.

The problem is buried pretty deep in the calling structure, which is why I suggested that at every step of the process, a sanity check needs to be performed, and the appropriate variable type chosen to hold any valid return value.

So, microsecondsToInches() should take an unsigned long, since that is the (valid) output of pulseIn(). However, a sanity check would indicate that an int would hold any valid sensor output. Therefore, Ping() should return an int, as should ServoPing(), which would allow your function to use a smaller data type, and take up less precious SRAM.

A review of the specs for the particular sensor would tell whether int was even appropriate. Most ping sensors don't even handle 20 feet, so a byte as the return type for microsecondsToInches() might be sufficient.

Actually, if I were changing things, or coding from scratch, I would have distances be in inches and as a float.
The Ping has a precision of a fraction of an inch since the spec sheet example mentions, “time-per-centimeter conversion factor of 6.6896”.
Thus,

float microsecondsToInches(long microseconds)
{
return microseconds / 73.746 / 2.0;
}

With that in your toolbox, another project looking for changes in distance could respond much sooner, without waiting for a change of a whole inch.
No sense losing precision if you don’t have to. If a specific application kept an array of 100 distances, then I would convert the float to an int, perhaps
multiplying by 10.0 first, so you would have 0.1 in precision, as in:
int dist[100];
for (i=0; i<100; i++)
dist[ i ] = (int) (0.5 + 10.0 * ServoPing(direction[ i ])); // with ServoPing returning inches in floating point

You might also choose to keep everything integer and have a routine microsecondsToInchTenths, which returns the int directly.

Actually, if I were changing things, or coding from scratch, I would have distances be in inches and as a float.

Good point, and I'm glad that you brought that up. The ping page really needs a re-write.

if I were changing things, or coding from scratch, I would have distances be in inches and as a float.

Why not just express all distances in millifurlongs and leave the return as an integer datatype instead?

I think I may end up starting it back over from scratch. the bodging together of the different code has left me wondering which var i am calling when, just outta curiosity which var would i call in my switch case to detemine reversing direction i e

switch (longestDistance){
case '30':
  analogWrite(3, 140);     
  delay(1000);
  break;
case '45':
  analogWrite(3, 140);  
  delay(750);
  break;
case '65':
  analogWrite(3, 140);     
  delay(500);
  break;
case '105':
  analogWrite(11, 140);  
  delay(500);
case '120':
  analogWrite(11, 140);     
  delay(750);
  break;
case '135':
  analogWrite(11, 140);  
  delay(1000);       
  break;   
default:
  delay(100);

}

I've got nothing against single quotes around multi character constants, but you really need to know what you're doing with them. Do you?

It depends what you want the car to do as it approaches a wall/object at anything other than directly head-on.

When an obstruction starts getting close, choosing the clearest direction and turning slightly that way will let you for example follow along the inside of a curved wall without touching it.

On the other hand, if you’re trying to navigate a room full of furniture, you might want to have a similar but opposite routine that searches for the closest obstruction direction, and turns 180 degrees from that.