software serial .available() gives inconsistent behavior

Hello! I'm trying to debug some inconsistent behavior by a bit of code and haven't been able to find any answers with web searches. The snippet is:

void readGPS()
{
while (mySerial.available() > 0)
{
int c = mySerial.read();
float flat, flon;
unsigned long fix_age;

if (gps.encode(c))
{
... A bunch of stuff
}
}
}

This code is executed by this snippet:

if (buttonValue == LOW)
{
readGPS();
digitalWrite(ledPin, HIGH);
}

I've also tried it without the "()> 0)", just "mySerial.available())" but it makes no difference. The oddity is this; on the first read, or on subsequent reads, the entire while function is skipped for no evident reason. The function is skipped about 3 out of 4 attempts to read. I can only assume that this is because there is nothing in the buffer to read, but the GPS updates constantly. If I just send anything coming from the softwareSerial port directly to Serial.print it updates faster than I can read it so it doesn't make any sense that the buffer would ever be empty. I've made a few attempts to sort this out with delays and judicious use of mySerial.flush(), but those were just guesses and didn't help. Am I missing something obvious here, or am I just mis-using the library? Thanks much in advance!

Hi rohare

Could you post your complete program?

The oddity is this; on the first read, or on subsequent reads, the entire while function is skipped for no evident reason. The function is skipped about 3 out of 4 attempts to read.

What exactly happens on the other 1 attempt?

At first glance, I wonder if the problem is due to only processing incoming data when you are in the function you call on key press. You could try moving the while loop up into the main loop(). Then, for example, if you only want to display the GPS data on key pressed, get the data from your gps object in your if statement - I think there is a way you can query the object to check that it has received valid data before you display it.

All the best

Ray

Okay hackscribble, I took your advice and brought the whole gpsRead function into the main loop. It now reads from software serial every time it loops, and only outputs when the button is pressed. It now works every single time I press the button. Only problem now is that it works every time... twice. On button push equals two loops through the gps output code. This would be funny if I were someone else looking at me. :roll_eyes:

void loop()
{
while (mySerial.available())
{
int c = mySerial.read();

if (gps.encode(c))
{
gps.f_get_position(&flat, &flon, &fix_age);
}
}

debouncer.update();
buttonValue = debouncer.read();

if (buttonValue == LOW)
{
digitalWrite(ledPin, HIGH);

// Handle if the GPS data is out of date or no satellite fix has been made.
if (fix_age == TinyGPS::GPS_INVALID_AGE)
{
Serial.println("No satellite fix detected");
}
else if (fix_age > 5000)
{
Serial.println("Warning: no recent satellite fix. GPS data may be stale.");
}

Serial.print("Latitude:\t");
Serial.println(flat, 4);
Serial.print("Longitude:\t");
Serial.println(flon, 4);

/* Caution. This is the pythagorean method of calculating distance.
It will become inaccurate at distances beyond about 20 miles
due to the curvature of the Earth.
*/
distanceToTarget = sqrt(pow((targetLat - flat),2) + pow((targetLon - flon), 2));

// Compare the current coordinates against the target coordinates and target size(circle around the target)
// If you are within that circle, open the solenoid, if not, give the distance to target.
if (flat > (targetLat - targetSize) && flat < (targetLat + targetSize) && flon > (targetLon - targetSize) && flon < (targetLon + targetSize))
{
Serial.println("You've reached the target!");
openSolenoid();
}
else
{
Serial.print("Distance to target: ");
Serial.print(distanceToTarget, 4);
Serial.println(" kilometers.");
}
}
}

Could you post the full program so we can see your declarations and setup().

And does the code execute twice as soon as you press the button, or once when you press and once when you release?

How is the button connected to the Arduino?

All the best

Ray

rohare:
I can only assume that this is because there is nothing in the buffer to read, but the GPS updates constantly. If I just send anything coming from the softwareSerial port directly to Serial.print it updates faster than I can read it so it doesn't make any sense that the buffer would ever be empty.

If your sketch is designed correctly it will be able to read input from the serial port massively faster than the serial port can receive it, so most of the time the receive buffer will be empty and available() will return zero. That's the normal and expected behaviour. The only way that you'd find the serial receive buffer always had something in it would be if your sketch was designed so that it didn't check for serial input very often. That would usually indicate a poor design.

and only outputs when the button is pressed. It now works every single time I press the button. Only problem now is that it works every time... twice.

You want to do something when the switch BECOMES pressed, not when the switch IS pressed. Look at the state change detection example for how to fix your problem.

Hooray! It works perfectly now. Thanks PaulS for the hint. I made the following changes:

boolean stateChanged = debouncer.update();
buttonValue = debouncer.read();

if (stateChanged && buttonValue == LOW)
{
digitalWrite(ledPin, HIGH);

Now it only registers a button press once per press and any presses occurring while the code is still in progress executing are ignored. Exactly what I needed.