Sonar func sometimes prints values

Hi all,

Should be straightforward. I can get the global var distance to print within the pingSonar func, but not when I return distance, and then try to pass it into printSonar func (it just returns 0.00). Any ideas?

Also, since 'distance' is a global var, do I even need to have pingSonar func a double? Should it only be a void?

Full code

/**********LIBRARIES**********/
#include <NewPing.h>  // sonar library


/**********PINS**********/
//leds
#define led1 12   


// Sonar
#define TRIGGER_PIN  6  // Arduino trigger pin on sonar
#define ECHO_PIN     7  // Arduino echo pin on sonar
#define MAX_DISTANCE 200 // Max dist (cm) to ping, sensor dist rated 400-500cm 
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.                         



/**********GLOBAL VARIABLES**********/
double distance;  // sonar reading


/**********SETUP**********/
void setup() 
{
  //board setups
  pinMode(led1,OUTPUT);
  Serial.begin(9600);

  //code
  start_led();
  pingSonar(distance);
  printSonar(distance);
  end_led();
  //exit(0);

}


/**********MAIN LOOP**********/
void loop() 
{
  pingSonar(distance);
  printSonar(distance);

}

/**********FUNCTION DEFINITIONS**********/
void start_led(void)
  {
    digitalWrite(led1,HIGH);
    delay(300);
    digitalWrite(led1,LOW);
    delay(300);
  }

void end_led(void)
  {
    digitalWrite(led1,HIGH);
    delay(1500);  
    digitalWrite(led1,LOW);
   
  }

double pingSonar(double distance)
{
  int samples = 10;
  for (int i = 0; i < samples; i++)
  {
  // turn ON next line if using repeated sampling
  delay(500);    // Wait 50ms between pings (~ 20 pings/sec). 29ms should be min delay between pings
  
   // initially, these types were 'unsigned int', but seemed to be no difference
   double uS = sonar.ping(); // Send ping, get ping time in microseconds (uS)
   //double distance = (uS / US_ROUNDTRIP_CM) / 2.54;  // Convert ping time to distance, 0 = outside set distance range
                                                    // units are converted from cm to inches
   distance = (uS / US_ROUNDTRIP_CM) / 2.54;     
   Serial.print("dist (cm) ");
   Serial.println(distance);

   return distance;
  }
}

void printSonar(double distance)
{
  Serial.print("dist (cm) ");
  Serial.println(distance);
}

When you have the identifier, "double" in front of the name of the passed variable, you are creating a new variable to be used, and not the value you are trying to pass. Get rid of the word "double" and test again.

I'd recommend that you rename that function's formal parameter. There could be some variable shadowing happening.

1 Like

This is a function declaration... you cannot leave out the type of the arguments. You might remove the argument as a whole if it is global (bad practice, but maybe ok for now).

void printSonar(){
   Blablabla
}

@project_science
Define the function with parameter distance, you create a separate variable, that has nothing connection to the global one.

Calling the function as

you just copying the value from global distance to the internal variable of the function. After copying, any changes that will be applied to the internal distance, will not affect the global one.

In order for changes to an internal variable to affect an external one, it must be passed by reference.

double pingSonar(double &distance)

Or do not create a separate function variable at all and work in a function with an external variable:

double pingSonar() {
...
 distance = 33;        // << this is a global variable
1 Like

@b707
Editing
double pingSonar(double distance) to double pingSonar(double &distance) worked. So I made a few comments to see if I understand this as references (c++), pointers (c), and memory concepts are confusing so I want to see if this makes sense how I understand it:

  1. Passing distance into the arguments of pingSonar creates a copy of itself, but doesn't edit the original. It creates more memory allocation to hold this copy.

  2. Passing distance by reference as &distance allows access the r-value of the global var distance directly, thus the original value is changed.

  3. Since I'm passing the ref of distance which is global var, I should redefine the func as void pingSonar(double &distance) instead of double, since I'm not returning anything.

  4. When I only want to view the value of distance, it's correct passing distance into the printSonar func - the copy - which protects the value of the global var distance by not allowing accessing to it.

  5. For calling the code, i.e. in Setup and Loop, I don't include the '&', only the variable.

1 Like

Wow!
Almost perfect!

There is not much to add to this, except to clarify a little.

This called "passing by value".

I would be say "if you only want to use the value and not to modify it".

@b707
Your feedback has been great, thanks! I do get the reference part, so I wanted to try and use pointers instead. I used this tutorial for understanding the differences between the two.

I received an error message upon compiling:

'ptrDistance' does not name a type ptrDistance = &distance;
 ^~~~~~~~~~~
Compilation error: 'ptrDistance' does not name a type

This was surprising, as I followed the tutorial. What could be going wrong?

The main changes I made were:

/**********GLOBAL VARIABLES**********/
double distance;  // sonar reading
double *ptrDistance = &distance;
ptrDistance = &distance;
void pingSonar(double *ptrDistance)
{
  int samples = 10;
  for (int i = 0; i < samples; i++)
  {
  // turn ON next line if using repeated sampling
  delay(500);    // Wait 50ms between pings (~ 20 pings/sec). 29ms should be min delay between pings
  
   // initially, these types were 'unsigned int', but seemed to be no difference
   double uS = sonar.ping(); // Send ping, get ping time in microseconds (uS)
   //double distance = (uS / US_ROUNDTRIP_CM) / 2.54;  // Convert ping time to distance, 0 = outside set distance range
                                                    // units are converted from cm to inches
  //  distance = (uS / US_ROUNDTRIP_CM) / 2.54;     
   *ptrDistance = (uS / US_ROUNDTRIP_CM) / 2.54;     
   Serial.print("dist (cm) ");
   Serial.println(*ptrDistance);

   //return distance;
  }
}

Full code

/**********LIBRARIES**********/
#include <NewPing.h>  // sonar library


/**********PINS**********/
//leds
#define led1 12   


// Sonar
#define TRIGGER_PIN  6  // Arduino trigger pin on sonar
#define ECHO_PIN     7  // Arduino echo pin on sonar
#define MAX_DISTANCE 200 // Max dist (cm) to ping, sensor dist rated 400-500cm 
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.                         



/**********GLOBAL VARIABLES**********/
double distance;  // sonar reading
double *ptrDistance = &distance;
ptrDistance = &distance;


/**********SETUP**********/
void setup() 
{
  //board setups
  pinMode(led1,OUTPUT);
  Serial.begin(9600);

  //code
  start_led();
  pingSonar(ptrDistance);
  printSonar(distance);
  end_led();
  //exit(0);

}


/**********MAIN LOOP**********/
void loop() 
{
  pingSonar(ptrDistance);
  printSonar(distance);

}

/**********FUNCTION DEFINITIONS**********/
void start_led(void)
  {
    digitalWrite(led1,HIGH);
    delay(300);
    digitalWrite(led1,LOW);
    delay(300);
  }

void end_led(void)
  {
    digitalWrite(led1,HIGH);
    delay(1500);  
    digitalWrite(led1,LOW);
   
  }

void pingSonar(double *ptrDistance)
{
  int samples = 10;
  for (int i = 0; i < samples; i++)
  {
  // turn ON next line if using repeated sampling
  delay(500);    // Wait 50ms between pings (~ 20 pings/sec). 29ms should be min delay between pings
  
   // initially, these types were 'unsigned int', but seemed to be no difference
   double uS = sonar.ping(); // Send ping, get ping time in microseconds (uS)
   //double distance = (uS / US_ROUNDTRIP_CM) / 2.54;  // Convert ping time to distance, 0 = outside set distance range
                                                    // units are converted from cm to inches
  //  distance = (uS / US_ROUNDTRIP_CM) / 2.54;     
   *ptrDistance = (uS / US_ROUNDTRIP_CM) / 2.54;     
   Serial.print("dist (cm) ");
   Serial.println(*ptrDistance);

   //return distance;
  }
}

void printSonar(double distance)
{
  Serial.print("***** dist (cm) ");
  Serial.println(distance);
}

What's the purpose of that second line? You can't have an assignment statement outside of a function.

Also, 'ptrDistance' is a global variable. Why are you even bothering to pass it to a function? It's within scope everywhere in your code.

Also, also, as I recommended in Post #3, you should give your function parameters different names than the global variables. You will get bit by variable shadowing and it will be perplexing.

Thanks for your tips! I figured it out. What I didn't want was to recreate a var in a loop, so I had global vars. But that wasn't working, so I changed the code and it works now.

What had confused me was this link I shared where I declared all 3 lines under pointers, because I thought I had to. Turns out, only needed the 1st line, and infer the rest.

Full code

/**********LIBRARIES**********/
#include <NewPing.h>  // sonar library


/**********PINS**********/
//leds
#define led_1 12   


// Sonar
#define TRIGGER_PIN  6  // Arduino trigger pin on sonar
#define ECHO_PIN     7  // Arduino echo pin on sonar
#define MAX_DISTANCE 200 // Max dist (cm) to ping, sensor dist rated 400-500cm 
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.                         



/**********GLOBAL VARIABLES**********/


/**********SETUP**********/
void setup() 
{   
  Serial.begin(9600); 
  pinMode(led_1,OUTPUT);
}


/**********MAIN LOOP**********/
void loop() 
{
  double distance;  // sonar reading
  start_led();
  pingSonar(&distance);
  printSonar(distance);
  end_led();
  exit(0);
}

/**********FUNCTION DEFINITIONS**********/
void start_led(void)
  {
    digitalWrite(led_1,HIGH);
    delay(300);
    digitalWrite(led_1,LOW);
    delay(300);
  }

void end_led(void)
  {
    digitalWrite(led_1,HIGH);
    delay(1500);  
    digitalWrite(led_1,LOW);   
  }

void pingSonar(double *ptrDistance)  
{
  /* Notes, you created var 'distance' in mail loop, and it's structured as:
     double *prtDistance
     *ptrDistance = &distance

   */


  int samples = 10;
  for (int i = 0; i < samples; i++)
  {
   delay(50);    // Wait 50ms between pings (~ 20 pings/sec). 29ms should be min delay between pings   
   double uS = sonar.ping(); // Send ping, get ping time in microseconds (uS)
   *ptrDistance = (uS / US_ROUNDTRIP_CM) / 2.54;  // Convert ping time to distance, 0 = outside set distance range, units converted from cm to in
   Serial.print("dist (cm): ");
   Serial.println(*ptrDistance);
   Serial.flush();   
  }
}

void printSonar(double distance)
{
  Serial.print("10 samples were taken.  Last sample was: ");
  Serial.println(distance);   
}

Get rid of that line. It serves no purpose. You're not programming for a PC where exit will return you to the operating system. Just let the loop() terminate normally and will will be called again from the (hidden) main() function.

Wouldn't the program loop to the start, and the sonar would keep pinging? If the Arduino was attached to a battery (and not plugged into a pc), wouldn't the code save battery power by not running the code again i.e. by "exiting" / stopping the code? Unless I misunderstand why 'exit(0)' exists for Arduino...?

That will happen anyway the given the Arduino paradigm. Are you saying you want the code to do only one ping distance calculation ever? Just hang and be dead to the world after that? If so, put all the code in the setup() function and leave the loop() function empty. That way nothing will ever happen again until the board is reset or power cycled.

If you want the processor to just die after the the first ranging, put the code in setup() as mentioned above. If you want to save battery in addition to that, look into the sleep capabilities of the specific processor ... which you didn't specify.

I because it's part of the C++ language.

This was all helpful, thanks for your input!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.