HC SR04

Hello guys,

I`m making project with two ultrasound sensors, code is written with Newping library. With few tests I noticed, that time by time my sensors act weird, they say that the distance is 0, even tho it is impossible because sensors can detect from 30mm to like 3m or something, and I making calculations in my room which is not that big :smiley: I made few tests, first when my robot is stable and second while moving.

First test showed nothing weird, both sensors act pretty much the same. I calculated the echo pin with my multi-meter to find out what kind of voltage I`m getting from them. Calculations showed, that if wall is close like 10-30cm, voltage gets to like 20mV, and if the obstacle is far it gets maximum to 300-400mV. Both sensors showed same results pretty much.

Second test was while my robot is moving. Time by time I get 0 from one or another sensor, even tho the distance is like 10 - 50cm. I checked it with multi-meter and found out something weird. When I get 0 distance, the multi-meter shows way bigger voltage, from 2-4V. As my robot goes faster, it gets more interrupts like that.

I wanted to ask what is the reason? Is some kind of information instability within sensors, or some kind of lose of information and the ping doesn't go back (disappears). Is there a way to avoid that?
My code is typical with Newping library. I can upload rest piece of it, if it is necessary, just tell me.

void sonar()
{

  Front = sonar1.ping_cm() * 10;
  Serial.print("Front: ");
  Serial.print(Front);
  Serial.println("cm");
  delay(100);

  Input = sonar2.ping_cm() * 10;
  Serial.print("Input: ");
  Serial.print(Input);
  Serial.println("cm");
  delay(100);

}

I don't believe that voltage on the echo pin will tell you very much. These devices simply send out a pulse when triggered and then set the echo pin when the pulse is returned. You write software which measures the time lapse between the trigger and the echo and calculate the distance based on that.
Post your full code for more assistance.

Information has to be somewhere and I believe it is in the voltage. I have read something, that as the pulse travels further the level on echo gets bigger. And my test showed exactly the same, as long as I didn't started to test it while moving it gets some kind of interruptions.

Small introduction to my code: I`m making wall follower with PID. Ignore PID variables and other stuff. I just want to deal with the sensors.

MAIN:

#include <AFMotor.h>
#include <PID_v1.h>
#include "const.h"
#include "debug.h"
#include "drive.h"
#include <NewPing.h>
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
NewPing sonar1(TRIGGER1, ECHO1, MAX_DISTANCE);
NewPing sonar2(TRIGGER2, ECHO2, MAX_DISTANCE);

// void counter()
// {
//Update count
//  pulses++;
// }

void setup()
{
  Serial.begin(9600);
  // pinMode(encoder_pin, INPUT);
  //  attachInterrupt(0, counter, FALLING);   //Interrupt 0 is digital pin 2  , Triggers on Falling Edge (change from HIGH to LOW)
  //  pulses = 0;
  //  rpm = 0;
  //  timeold = 0;
  Setpoint = 160;                 //cm
  myPID.SetMode(AUTOMATIC);
  myPID.SetSampleTime(150);       //ms
  myPID.SetControllerDirection(DIRECT);
  myPID.SetTunings(0.8, 0, 0);             //Kp , Kd, Ki
  myPID.SetOutputLimits(-30, 30);
}

void loop()
{ 
  sonar();  
  myPID.Compute();
  drive();
  debugOutput(); // prints debugging messages to the serial console
  //countrpm();

}


void Compute()
{
  if (!inAuto) return;
  unsigned long now = millis();
    int timeChange = (now - lastTime);
  if (timeChange >= SampleTime)
  {
    /*Compute all the working error variables*/
    double error = Setpoint - Input;
    ITerm += (ki * error);
    if (ITerm > outMax) ITerm = outMax;
    else if (ITerm < outMin) ITerm = outMin;
    double dInput = (Input - lastInput);

    /*Compute PID Output*/
    Output = kp * error + ITerm - kd * dInput;
    if (Output > outMax) Output = outMax;
    else if (Output < outMin) Output = outMin;

    /*Remember some variables for next time*/
    lastInput = Input;
    lastTime = now;

  }
}

void sonar()
{
    Front = sonar1.ping_cm()*10;
    Serial.print("Front: ");
    Serial.print(Front);
    Serial.println("mm");
    delay(100);

    Input = sonar2.ping_cm()*10;
    Serial.print("Input: ");
    Serial.print(Input);
    Serial.println("mm");
    delay(100);
}

//void countrpm()
//{
// if (millis() - timeold >= 1000)
//  {
//Don't process interrupts during calculations
//      detachInterrupt(0);
//     rpm = (60 * 1000 / pulsesperturn )/ (millis() - timeold)* pulses;
//    timeold = millis();
//   pulses = 0;
//Restart the interrupt processing
//   attachInterrupt(0, counter, FALLING);
//  }
// }

void SetTunings(double Kp, double Ki, double Kd)
{
  if (Kp < 0 || Ki < 0 || Kd < 0) return;

  double SampleTimeInSec = ((double)SampleTime) / 1000;
  kp = Kp;
  ki = Ki * SampleTimeInSec;
  kd = Kd / SampleTimeInSec;

  if (controllerDirection == REVERSE)
  {
    kp = (0 - kp);
    ki = (0 - ki);
    kd = (0 - kd);
  }
}

void SetSampleTime(int NewSampleTime)
{
  if (NewSampleTime > 0)
  {
    double ratio  = (double)NewSampleTime / (double)SampleTime;
    ki *= ratio;
    kd /= ratio;
    SampleTime = (unsigned long)NewSampleTime;
  }
}

void SetOutputLimits(double Min, double Max)
{
  if (Min > Max) return;
  outMin = Min;
  outMax = Max;

  if (Output > outMax) Output = outMax;
  else if (Output < outMin) Output = outMin;

  if (ITerm > outMax) ITerm = outMax;
  else if (ITerm < outMin) ITerm = outMin;
}

void SetMode(int Mode)
{
  bool newAuto = (Mode == AUTOMATIC);
  if (newAuto == !inAuto)
  { /*we just went from manual to auto*/
    Initialize();
  }
  inAuto = newAuto;
}

void Initialize()
{
  lastInput = Input;
  ITerm = Output;
  if (ITerm > outMax) ITerm = outMax;
  else if (ITerm < outMin) ITerm = outMin;
}

void SetControllerDirection(int Direction)
{
  controllerDirection = Direction;
}

variables:

/*working variables*/
unsigned long lastTime;
double Input, Output, Setpoint, Front;
double ITerm, lastInput;
double Kp, Ki, Kd;
double kp, ki, kd;
double outMin, outMax;
int SampleTime;
bool inAuto = false;


#define MANUAL 0
#define AUTOMATIC 1

#define DIRECT 0
#define REVERSE 1
int controllerDirection = DIRECT;

//Motor set
AF_DCMotor motor1(1,MOTOR12_8KHZ); // create motor #1, 1KHz pwm
AF_DCMotor motor2(2,MOTOR12_8KHZ); // create motor #2, 1KHz pwm
int Lmotor;
int Rmotor;
const int motorNeutral = 60;
int speedmax = 255;

//sensor
#define TRIGGER1  A0
#define ECHO1    A1
#define TRIGGER2  A2
#define ECHO2    A3
#define MAX_DISTANCE 50000





///////encoder
//int encoder_pin = 2; // pulse output from the module
//unsigned int rpm; // rpm reading
//volatile byte pulses; // number of pulses
//unsigned long timeold;
// number of pulses per revolution
// based on your encoder disc
//unsigned int pulsesperturn = 20;

debugg:

void debugOutput()
{

  Serial.print("Lmotor: ");
  Serial.print(Lmotor);
  Serial.println();

  Serial.print("Rmotor: ");
  Serial.print(Rmotor);
  Serial.println();

  Serial.print("Output: ");
  Serial.print(Output);
  Serial.println();

//  Serial.print("RPM = ");
//Serial.println(rpm,DEC);
//  Serial.println();
  
}

Drive:

void drive()
{
  if (Front > 30 && Front > 160) //160mm
  {
  Lmotor =  motorNeutral - Output;
  Rmotor =  motorNeutral + Output;
  }
  if (Front > 30 && Front < 160)
  {
    Lmotor = 0;
    Rmotor = speedmax;
  }

  // limit rightMotorSpeed
  if (Rmotor > 100)
    Rmotor = 100;
  else if (Rmotor < 0)
    Rmotor = 0;

  // limit rightMotorSpeed
  if (Lmotor > 100)
    Lmotor = 100;
  else if (Lmotor < 0)
    Lmotor = 0;


  motor1.run(FORWARD);
  motor2.run(FORWARD);

  motor1.setSpeed(Lmotor);
  motor2.setSpeed(Rmotor);


}

And my test showed exactly the same

The echo pin is either LOW or HIGH. What, exactly, were you measuring? With what equipment?

I guess you should try a simple example with the HC SRO4 in a basic sketch like here before trying to integrate the sensors in a large block of code: Arduino Playground - NewPing Library

This statement:

Front = sonar1.ping_cm()*10;

causes the trigger on the HC SRO4 to send a tone burst out and the code waits until it gets the tone burst return acknowledgement on the echo pin. On the basis of the time lapse between trigger and echo and the speed of sound, it calculates the object distance and returns it in the variable Front (multiplied by 10 to convert it to mm)
You (normally) connect the HC SRO4 to digital pins on the Arduino so it sees only a logic level 0 or 1. These are of course represented by a voltage within a range of 0 to 5 volts but contain no other information.
In your case, you appear, however, to use analog pins for the HC SRO4 but that is not necessary.

PaulS:
The echo pin is either LOW or HIGH. What, exactly, were you measuring? With what equipment?

Basically, I tried measuring voltage on echo pin. My results are the same for both sensors:
I did this with this little fella: http://www.proskit.com/test-instruments/multimeters/3-3-4-true-rms-auto-range-multimeter

Distance,mm Voltage, mV
30 6
50 8
100 14
200 24
300 39
400 49
500 57
~3000 350

I changed my code a bit, now it shows distance ~30 meters, when echo level gets to 2-4V, instead of 0

Basically, I tried measuring voltage on echo pin

With a DVM?
What do you think you were measuring?

I don't know some kind of signal, arduino has to find out somehow and get the information. We`ll if I'm wrong this is kind of weird coincidence, because the voltage gets bigger as the obstacle is further.

, because the voltage gets bigger as the obstacle is further.

Because the pulse is longer!
It's still a digital signal though.

Groove:
Because the pulse is longer!
It's still a digital signal though.

That makes sense :smiley: thank you for introducing me to basic electronics. What about pins? Do you think it is okay to connect both echo and trig to analog pins or should I change it to digital ones?

I have arduino mega, so enough pins to do it.

On a Uno, A0 to A5 can be used as digital pins simply by setting the appropriate pinMode and then digitalRead or digitalWrite.
On a Nano, you can't do this with A6 and A7, which are analogue inputs only.

What about mega? Does it work the same way as uno? I have set pin mode to input and output.

I don't know because I don't have a Mega, but I'd bet there's either something on the product page or somewhere in the reference that would tell you.
My guess is that all 16 analogue pins can be used as digital.

hollowltu:
What about mega? Does it work the same way as uno? I have set pin mode to input and output.

Since you tell newPing the pin numbers you are using, I assume that it handles the pinMode etc. but find an example sketch to see. In most cases (apart from pin numbers for specific uses and maybe some special functions like timers) you'll find that the Uno and Mega are equivalent for this application.

Use NewPing's "median" method to easily discard outliers and get more stable results.

Are there some kind of examples for newping multiple sensor median method? Need some kind of introduction to it, because that method looks really good for clearing the outliers for me, which I get plenty when robot is moving.

Hey guys Im trying to solve problem with random wrong inputs, which happens as I suppose because of trig echo signal lose. I tried using multi sensors NewPing sketch with my code. What Im doing here is saving last correnct input, if I get wrong one, I go with that which was good.

Here is my code:

NewPing sonar[SONAR_NUM] = { // Sensor object array.
  NewPing(A0, A1, MAX_DISTANCE),
  NewPing(A2, A3, MAX_DISTANCE)
};
void setup()
{
  Serial.begin(115200);
 pingTimer[0] = millis() + 75; // First ping start in ms.
  for (uint8_t i = 1; i < SONAR_NUM; i++)
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;

void loop()
{
  for (uint8_t i = 0; i < SONAR_NUM; i++)
  {
    if (millis() >= pingTimer[i]) {
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;
      if (i == 0 && currentSensor == SONAR_NUM - 1)
        oneSensorCycle(); // Do something with results.
      sonar[currentSensor].timer_stop();
      currentSensor = i;
      cm[currentSensor] = 0;
      sonar[currentSensor].ping_timer(echoCheck);
    }
  }
}

void echoCheck() { // If ping echo, set distance to array.
  if (sonar[currentSensor].check_timer())
    cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
}

void oneSensorCycle() { // Do something with the results.
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
    currentFront == cm[0] * 10;
    currentInput == cm[1] * 10;
    if (currentFront == 0)
    {
      Front = LastFront;
      Serial.print("Eror Front");
    }
    if (currentInput == 0)
    {
      Input == LastInput;
      Serial.print("Eror Input");
    }
    if (currentFront > 0)
    {
      Front == currentFront;
      LastFront == currentFront;
    }
    if (currentInput > 0)
    {
      Input == currentFront;
      LastFront == currentFront;
    }
  }
  Serial.println();
}