Need Help With PWM Arduino

Hello,
I’m working on a project for my Design class using Arduino and a Ping ultrasound sensor to avoid objects.
Check it out at http://letsmakerobots.com/node/25837

I’ve figured out the hardware issues are because I need a motor driver that can handle more current. The L293D cant handle the current the motors require.

The problem I’m stuck at now is with the code. I’m new to Arduino and can use some help with writing it. The code I have here works but as you can see in the video in my post, the vehicle is very fast, probably too fast to work correctly. Using PWM on the enable pins or directly on the motor pins will allow me to control the speed. I tried editing it code in every way I can think yet still no success.
I would also like to add a section to the code to allow the servo to pan while its driving so it will avoid it from running into anything at any angle OR when it comes to an object look left and right and choose the best direction. If anyone can help would be greatly appreciated.

Heres the code:

#include <Ping.h>
const int numOfReadings = 10;                   // number of readings to take/ items in the array
int readings[numOfReadings];                    // stores the distance readings in an array
int arrayIndex = 0;                             // arrayIndex of the current item in the array
int total = 0;                                  // stores the cumlative total
int averageDistance = 0;                        // stores the average value
const int pingpin = 12;                         // ping pin (digital 12)
unsigned long pulseTime = 0;                    // stores the pulse in Micro Seconds
unsigned long distance = 0;                     // variable for storing the distance (cm)
int motor1Pin1 = 3;                             // pin 2 on L293D
int motor1Pin2 = 4;                             // pin 7 on L293D
int enable1Pin = 9;                             // pin 1 on L293D
int motor2Pin1 = 5;                             // pin 10 on L293D
int motor2Pin2 = 6;                             // pin  15 on L293D
int enable2Pin = 10;                            // pin 9 on L293D
void setup() {
  // set the motor pins as outputs:
   Serial.begin(9600);
  pinMode(motor1Pin1, OUTPUT);
  pinMode(motor1Pin2, OUTPUT);
  pinMode(enable1Pin, OUTPUT);
  pinMode(motor2Pin1, OUTPUT);
  pinMode(motor2Pin2, OUTPUT);
  pinMode(enable2Pin, OUTPUT);
  // set enablePins high so that motor can turn on:
  digitalWrite(enable1Pin, HIGH);
  digitalWrite(enable2Pin, HIGH);
  pinMode(pingpin, OUTPUT);                     
  pinMode(pingpin, INPUT);                      
  // create array loop to iterate over every item in the array
  for (int thisReading = 0; thisReading < numOfReadings; thisReading++) {
    readings[thisReading] = 0;
  }
}
void loop() {
  pinMode(pingpin, OUTPUT);
  digitalWrite(pingpin, HIGH);                  // send 10 microsecond pulse
  delayMicroseconds(10);                        // wait 10 microseconds before turning off
  digitalWrite(pingpin, LOW);                   // stop sending the pulse
  pinMode(pingpin, INPUT);
  pulseTime = pulseIn(pingpin, HIGH);           // Look for a return pulse, it should be high as the pulse goes low-high-low
  distance = pulseTime/58;                      // Distance = pulse time / 58 to convert to cm.
  total= total - readings[arrayIndex];          // subtract the last distance
  readings[arrayIndex] = distance;              // add distance reading to array
  total= total + readings[arrayIndex];          // add the reading to the total
  arrayIndex = arrayIndex + 1;                  // go to the next item in the array
  // At the end of the array (10 items) then start again
  if (arrayIndex >= numOfReadings)  {
    arrayIndex = 0;
  }
  averageDistance = total / numOfReadings;      // calculate the average distance
  delay(10);
  // check the average distance and move accordingly
  if (averageDistance <= 10){
    // go backwards
    digitalWrite(motor1Pin1, HIGH);
    digitalWrite(motor1Pin2, LOW);
    digitalWrite(motor2Pin1, HIGH);
    digitalWrite(motor2Pin2, LOW);
  }
  if (averageDistance <= 25 && averageDistance > 10) {
    // turn
    digitalWrite(motor1Pin1, HIGH);
    digitalWrite(motor1Pin2, LOW);
    digitalWrite(motor2Pin1, LOW);
    digitalWrite(motor2Pin2, HIGH);
  }
  if (averageDistance > 25)   {
    // go forward
    digitalWrite(motor1Pin1, LOW);
    digitalWrite(motor1Pin2, HIGH);
    digitalWrite(motor2Pin1, LOW);
    digitalWrite(motor2Pin2, HIGH);
  }
}

Welcome to the wonderfull world of Arduino

  • please modify your post, select the code and use the # button so the code is tagged properly (more readable)

Can you tell us what the behavior of the robot is? What did you expect, and what does it do?

OK, the code looks quite good, only a few remarks

  • #include ping.h ?? // Why ?? - compiles without

  • numOfReadings , I would use an #define (choice)

  • Serial.begin(9600); why not 115200 ( is 12 times faster!)
    for the rest Serial is not used (probably when you debug)

  • your code does not check if pulseTime gives a meaningfull value. Suppose it does not see a HIGH pulse what will happen? return value will be large (?) and corrupt calculations. (serious one)

  • distance = pulseTime/58;
    These are all integers, that’s OK but if pulseTime is small the rounding errors can become relative large. E.g. if pulseTIme < 57 ==> distance = 0;
    TODO: check if the value of distance make sense.

  • averageDistance = total / numOfReadings;
    the calculation of the average will be too low in the beginning as you divide by numReadings while the actual count is less. Therefor the device will start running backwards I guess. Check the range of the var’s averageDistance and total. I assume an int (as you did) is enough but unsigned int would give a 2x bigger range as both var’s can never be negative. It also prevents some type of errors. Unsigned long will be safest.

Note:
Instead of first converting the distance to centimeter (with rounding error) and then add them up (all errors add up) you better add up the pulseTimes (no rounding error) and then calc the average pulseTime and convert that to centimeter. This will give a smaller cumulative rounding error (factor 10). But that said your code should work

  • delay(10);
    makes no sense imho, why wait before acting?

Applied the above and some whitelines for readability gives:

//
// FILE:
// AUTHOR:
// DATE:
// PURPOSE:
// HISTORY:
//

#define MAXREADINGS 10                          // number of readings to take/ items in the array

int readCount = 0;                              // number of samples read
int readings[MAXREADINGS];                      // stores the distance readings in an array
int arrayIndex = 0;                             // arrayIndex of the current item in the array

unsigned long total = 0;                        // stores the cumlative total
unsigned long averageDistance = 0;              // stores the average value

const int pingpin = 12;                         // ping pin (digital 12)
unsigned long pulseTime = 0;                    // stores the pulse in Micro Seconds
unsigned long distance = 0;                     // variable for storing the distance (cm)

int motor1Pin1 = 3;                             // pin 2 on L293D
int motor1Pin2 = 4;                             // pin 7 on L293D
int enable1Pin = 9;                             // pin 1 on L293D

int motor2Pin1 = 5;                             // pin 10 on L293D
int motor2Pin2 = 6;                             // pin  15 on L293D
int enable2Pin = 10;                            // pin 9 on L293D


void setup() 
{
	// Serial.begin(9600);  // 115200 ??

	// set the motor pins as outputs:
	pinMode(motor1Pin1, OUTPUT);
	pinMode(motor1Pin2, OUTPUT);
	pinMode(enable1Pin, OUTPUT);
	pinMode(motor2Pin1, OUTPUT);
	pinMode(motor2Pin2, OUTPUT);
	pinMode(enable2Pin, OUTPUT);
	
	// set enablePins high so that motor can turn on:
	digitalWrite(enable1Pin, HIGH);
	digitalWrite(enable2Pin, HIGH);
	
	pinMode(pingpin, OUTPUT);                    
	pinMode(pingpin, INPUT);
	
	// clear the array
	for (int i = 0; i < MAXREADINGS; i++) 
	{
		readings[i] = 0;
	}
}

void loop() 
{
	// GET A DISTANCE
	pinMode(pingpin, OUTPUT);
	digitalWrite(pingpin, HIGH);                  // send 10 microsecond pulse
	delayMicroseconds(10);                        // wait 10 microseconds before turning off
	digitalWrite(pingpin, LOW);                   // stop sending the pulse
	pinMode(pingpin, INPUT);
	pulseTime = pulseIn(pingpin, HIGH);           // Look for a return pulse, it should be high as the pulse goes low-high-low
	
	// CALC AVERAGE TIME
	total= total - readings[arrayIndex];          // subtract the last pulseTime
	readings[arrayIndex] = pulseTime;             // add pulseTime reading to array
	total= total + readings[arrayIndex];          // add the reading to the total
	
	arrayIndex = arrayIndex + 1;                  // go to the next item in the array
	
	// At the end of the array (10 items) then start again
	if (arrayIndex >= MAXREADINGS)  
	{
		arrayIndex = 0;
	}
	
	if (readCount < MAXREADINGS) readCount++;     // how many samples do we have
	averageDistance = (total / readCount) / 58;   // calculate the average distance => pulseTime / 58 to convert to cm.
	
	// check the average distance and move accordingly
	if (averageDistance <= 10)
	{
		// go backwards
		digitalWrite(motor1Pin1, HIGH);
		digitalWrite(motor1Pin2, LOW);
		digitalWrite(motor2Pin1, HIGH);
		digitalWrite(motor2Pin2, LOW);
	}
	if (averageDistance <= 25 && averageDistance > 10) 
	{
		// turn left/right ?
		digitalWrite(motor1Pin1, HIGH);
		digitalWrite(motor1Pin2, LOW);
		digitalWrite(motor2Pin1, LOW);
		digitalWrite(motor2Pin2, HIGH);
	}
	if (averageDistance > 25)   
	{
		// go forward
		digitalWrite(motor1Pin1, LOW);
		digitalWrite(motor1Pin2, HIGH);
		digitalWrite(motor2Pin1, LOW);
		digitalWrite(motor2Pin2, HIGH);
	}
}

Hopes this helps,
Rob

Thanks!! I am still learning about arduino and its language. This is very new to me. To control the speed I was thinking about adding

analogWrite(enable1Pin, 127);
analogWrite(enable2Pin, 127);

To the void setup to cut the speed in half. What do you think? Also i want to add code to allow the ping attached to the servo to pan back and forth while driving and chose the best direction to go. Or when confronted with an object look left, right and choose the best direction. Any advice/suggestions? Examples of similiar code for me to study? Thanks again. check the pics below

Using analogWrite() gives you far more control (assuming the motors can hanndle it) over your speed. First thing to program is to slow down when you approach a wall. The horizontal equivalent of a moonlander. Try to touch the wall softly.

Further make separate functions for turnLeft() turnRight() etc. Add an LDR to sense darkness and add some LEDs as headlights. Add an SD card to log the trajectory, ...