Auto Brake motor with Ultrasonic sensor

Hi,
I'm still learning Arduino code, and have put this together. I want the servo to go to position 90 degrees (stationary as using it to operate a drive motor) when the sensor detects an object within 200cm.
Looking at the serial monitor it will not make 'power' 90 when it detects something within 200cm.
Any help would be appreciated
Many Thanks

// Sweep Sample
// Copyright (c) 2012 Dimension Engineering LLC
// See license.txt for license details.

#include <Servo.h>
#include <NewPing.h>

#define TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 400 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

Servo SR; // We'll name the SyRen servo channel object SR.
// For how to configure the SyRen, see the DIP Switch Wizard for
//   http://www.dimensionengineering.com/datasheets/SyrenDIPWizard/start.htm
// Be sure to select RC Microcontroller Mode for use with this sample.
//
// Connections to make:
//   Arduino Pin 9  ->  SyRen S1
//   Arduino GND    ->  SyRen 0V
//   Arduino VIN    ->  SyRen 5V (OPTIONAL, if you want the SyRen to power the Arduino)
//
// SyRen accepts servo pulses from 1000 us to 2000 us.
// We need to specify the pulse widths in attach(). 0 degrees will be full reverse,
// 180 degrees will be full forward. Sending a servo command of 90 will stop the motor.

// Notice this attach() call. The second and third arguments are important.
// With a single argument, the range is 44 to 141 degrees, with 92 being stopped.
// With all three arguments, we can use 0 to 180 degrees, with 90 being stopped.
int Rx = 3;
int power = 0;
const int stopDist = 200; //an object has to be within x cm for the motor to stop
const int neutral = 90; //tell motor to go to 90 degrees which is neutral so it stops

void setup()
{
  SR.attach(9, 1000, 2000);
  pinMode(Rx, INPUT);
  Serial.begin(9600);
}

void loop()
{

  power = map(pulseIn(Rx, HIGH), 1000, 2000, 0, 180);

  if (sonar.ping_cm() < stopDist) {
    SR.write(90); //brake the motor if sensor picks up object
  }

  else {
    SR.write(power);
  }

  delay(50);                     // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.
  Serial.print("Ping: ");
  Serial.print(sonar.ping_cm()); // Send ping, get distance in cm and print result (0 = outside set distance range)
  Serial.println("cm");

  Serial.println(power);


}

You are sending two pings very close to each other (the one at the end of the loop is close to the one at the start)

What do you see in the console?
What’s attached to RX?

Thanks for the reply.
Should I increase the delay time in the code so there is a greater gap between sending the pings?

Attached to RX is a radio control receiver.

Sorry I haven’t yet come across the ‘console’ yet but will read into it.

Thanks

The Console is the serial monitor

You should only have one ping in the loop handled with millis() (no delay) to ping no more frequently than every 50ms

(Also NewPing Uses Timer2 interrupt, so be mindful of PWM on pins 3 & 11 on UNO and pins 9 and 10 on Mega)

In the serial monitor I could see the signal which comes from the Radio Control Receiver and is sent to the motor driver (Servo SR). This worked by itself.
The sensor value would also be displayed.

Although when sensor value would go below 200cm the ‘power’ number in the serial monitor would still let me change it by adjusting handset and not set power to 90.

I changed code so that ‘power = mapPulseIn ...etc.’ is within the if statement. This now meant that when sensor value < stopDist the ‘power’ reading would stay fixed at whatever the controller last read eg full speed 180 and not making it stop by sending 90.

How many pings are in the loop at the moment? And I’ll do a bit more reading into the millis() function and how to properly use it.

What should I expect regarding the pwm (on my arduino) with pins 3 & 11.

Many Thanks for all your help

What’s your arduino?

How many pings are in the loop at the moment?

i see two calls

It is an Elegoo UNO R3

Is the first in the if statement and the second in the serial print?
Should I remove the serial print ping?

Should I remove the serial print ping?

Yes. if you want to print it, record it in a variable when you read it and print that variable.

Also remember you set a max distance at 400cm so when the sensor does not detect anything closer than 400cm, sonar.ping_cm() will return 0

Can you try something like this

#include <Servo.h>
#include <NewPing.h>

#define TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define STOP_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
NewPing sonar(TRIGGER_PIN, ECHO_PIN, STOP_DISTANCE); // NewPing setup of pins and maximum distance.

Servo servoMotor;
const int neutral = 90; //tell motor to go to 90 degrees which is neutral so it stops

const byte remoteControlPin = 3;
unsigned long lastPing = 0;
unsigned long distance = 0;
int powerAngle;

void setup() {
  pinMode(remoteControlPin, INPUT);
  
  servoMotor.attach(9, 1000, 2000);
  servoMotor.write(neutral);

  Serial.begin(115200);
  distance = sonar.ping_cm();
}

void loop() {

  if (millis() - lastPing >= 50) {  // Wait 50ms between pings (~20 pings/sec). 29ms should be the shortest delay between pings.
    distance = sonar.ping_cm();     // will be 0 if distance > STOP_DISTANCE
    lastPing = millis();
    Serial.println(distance);       // debug
  }

  if (distance != 0) { // it means it's less than STOP_DISTANCE
    servoMotor.write(neutral);    //brake the motor if sensor picks up object
  } else {
    powerAngle = constrain(map(pulseIn(remoteControlPin, HIGH), 1000, 2000, 0, 180), 0, 180);
    servoMotor.write(powerAngle);
  }
}

I juste typed this here, so unsure if it's compiling but that should give you ideas. The way this works is that I set the max distance returned by your distance sensor to be 2m, after that it returns 0. so if the distance is 0 then there is no object otherwise it means there is an obstacle and you force the stop. Also note the use of millis() to not block the loop with a delay()

PS/ I also set the serial console at 115200 bauds, no need to go slow at 9600 bauds (unless this is required by a radio attached to the serial port)

Thanks this sort of worked!
When sensor reading is 0 and I accelerate the motor with the Radio Control it will judder (it’s like it will try reversing and then forward twice but seems to smooth out as I apply more power (that only does it when hard doing acceleration does not happen with smooth acceleration). When I don’t touch the controller it tries moving a tiny bit very slowly. Would this have anything to do with the baud rate (I used this guide SyRen 10/25/50 DIP switch configuration wizard to find what one to use, it only worked with switch 1 down which is when using it with with RC or micro controller and baudrate set at 115200).

The sensor readings can be a bit jumpy as well (I’m guessing it is because the range/ vision of the sensor. I wanted it to be able to automatically stop if it sees a person/ other object when the operator doesn’t) if that makes sense.

I think if I’m able to turn the sensor off with a physical sliding switch so that it won’t stop if it detects something. And also allow me to manually reverse it if it does detect something it would be perfect.

But main adjustments are when sensor is clear, the radio signals are jittering when stationary (value between 83 and 89) so motor tries moving unexpectedly I need to look into that. (the motor
And allow it to reverse when sensor is detecting movement.
And finally a sensor on/off switch (could I just put a physical switch in or do I need to code it or just put switch inbetween vcc of sensor?

Thank you very much for your help!!

I also need to add a failsafe so if controller goes out of range it will just stop.

When printing powerAngle to serial monitor I get sometimes get a value of -151 hence the motor jittering. stopped angle now seems to be 80 full forward 165 and full reverse 0 any ideas why this has changed?

Thanks

When printing powerAngle to serial monitor I get sometimes get a value of -151

that's weird because it's defined as    powerAngle = constrain(map(pulseIn(remoteControlPin, HIGH), 1000, 2000, 0, 180), 0, 180);so constrain() is going to enforce the value between 0 and 180

 accelerate the motor with the Radio Control it will judder

may be there are weird values being sent

if you try this code and move the remote, what do you see in the Serial console at 115200 bauds?

const byte remoteControlPin = 3;
unsigned long oldPulse;

void setup() {
  pinMode(remoteControlPin, INPUT);
  Serial.begin(115200);
}

void loop() {
  unsigned long newPulse = pulseIn(remoteControlPin, HIGH);
  if (oldPulse != newPulse) {
    if (newPulse > 2000) Serial.print(F(">>> "));
    else if (newPulse < 1000) Serial.println(F("<<< "));
    Serial.println(newPulse);
    oldPulse = newPulse;
  }
}

I tried that sketch and results
Full Forward = between 1963 -1970
Neutral = 1492 -1499
Full Reverse = 1017 -1018

Total Range 1017 - 1970

Thanks

OK - and you never got the >>> or <<< signs showing that some data might be off (below 1000 or above 2000)?

how did you print powerAngle ?

No I never got any of those signs
This is how the code looked with the print powerAngle.
I may have done something wrong?

#include <Servo.h>
#include <NewPing.h>

#define TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define STOP_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
NewPing sonar(TRIGGER_PIN, ECHO_PIN, STOP_DISTANCE); // NewPing setup of pins and maximum distance.

Servo servoMotor;
const int neutral = 85; //tell motor to go to 90 degrees which is neutral so it stops

const byte remoteControlPin = 3;
unsigned long lastPing = 0;
unsigned long distance = 0;
int powerAngle;

void setup() {
  pinMode(remoteControlPin, INPUT);

  servoMotor.attach(9, 1000, 2000);
  servoMotor.write(neutral);

  Serial.begin(115200);
  distance = sonar.ping_cm();
}

void loop() {

  if (millis() - lastPing >= 50) {  // Wait 50ms between pings (~20 pings/sec). 29ms should be the shortest delay between pings.
    distance = sonar.ping_cm();     // will be 0 if distance > STOP_DISTANCE
    lastPing = millis();
    Serial.println(distance);       // debug
  }

  if (distance != 0) { // it means it's less than STOP_DISTANCE
    servoMotor.write(neutral);    //brake the motor if sensor picks up object
  } else {
    powerAngle = constrain(map(pulseIn(remoteControlPin, HIGH), 1000, 2000, 0, 180), 0, 180);
    servoMotor.write(powerAngle);
  }
  Serial.print("Output");
  Serial.println(powerAngle);
}

you might want to print only when it's calculated but it should not deliver negative values. try

#include <Servo.h>
#include <NewPing.h>

#define TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define STOP_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
NewPing sonar(TRIGGER_PIN, ECHO_PIN, STOP_DISTANCE); // NewPing setup of pins and maximum distance.

Servo servoMotor;
const int neutral = 85; //tell motor to go to 90 degrees which is neutral so it stops

const byte remoteControlPin = 3;
unsigned long lastPing = 0;
unsigned long distance = 0;
long powerAngle;

void setup() {
  pinMode(remoteControlPin, INPUT);

  servoMotor.attach(9, 1000, 2000);
  servoMotor.write(neutral);

  Serial.begin(115200);
  distance = sonar.ping_cm();
}

void loop() {

  if (millis() - lastPing >= 50) {  // Wait 50ms between pings (~20 pings/sec). 29ms should be the shortest delay between pings.
    distance = sonar.ping_cm();     // will be 0 if distance > STOP_DISTANCE
    lastPing = millis();
    Serial.print(F("D: ")); Serial.println(distance);       // debug
  }

  if (distance != 0) { // it means it's less than STOP_DISTANCE
    servoMotor.write(neutral);    //brake the motor if sensor picks up object
  } else {
    long powerPulse = pulseIn(remoteControlPin, HIGH);
    powerAngle = constrain(map(powerPulse, 1000l, 2000l, 0, 180), 0, 180);
    servoMotor.write(powerAngle);
    Serial.print(F("P: "));  Serial.println(powerAngle);      // debug
  }

}

Thanks.
There was a compile error with this line?
I couldn't work out what the problem was though.

powerAngle = constrain(map(, 1000l, 2000l, 0, 180), 0, 180);

sorry corrected it just after posting

    long powerPulse = pulseIn(remoteControlPin, HIGH);
    powerAngle = constrain(map(powerPulse, 1000l, 2000l, 0, 180), 0, 180);

The problem is still occurring with the motor when controller is neutral p value is flickering between 87 and 91 the motor is still moving a bit forwards and back.

When quickly pulling back throttle the P value will come back on it self slightly maybe causing the juddering?
values looked like:
141, 163, 174, 173, 171, 173 and will max out at around 174 and flicker between 171 and 174.

Full reverse it flicks between 1 and 5. It stops and starts very quickly too.

All this time sensor showing D: 0

So not sure if the sensor is interfering?

When sensor is detecting it will make the motor go back slowly. I know when I was looking at the signal being sent to the motor it was showing 91 sometimes even though we set it to 90.

Can we set a range for the neutral position i.e. any P value between 87 and 91 is sent to motor as 90 or whatever the stop is.

Any other ideas?

when controller is neutral p value is flickering between 87 and 91 the motor is still moving a bit forwards and back.

the pwm is not stable at neutral and generates thus different angles
I’d suggest you modify the code to check if powerPulse is in between the two values for neutral then fix the angle to neutral, and if it’s above or below do the map. That will drive stability at neutral.

long powerPulse = pulseIn(remoteControlPin, HIGH);
if ((powerPulse >= xxxx) && (powerPulse <= yyyy)) powerAngle = neutral;
else powerAngle = constrain(map(powerPulse, 1000l, 2000l, 0, 180), 0, 180);

ajust xxxx and yyyy to what you’ve seen is the Max variation of the pulse when idle

Adjust also the 0 and 180 to be min and max stable angles of your servo