GPS + OLED - Memory Issue?

Hi folks. Was wondering if I could get the experts to weigh in on this. I have (what I consider) a memory / processing intensive sketch. Running it on an Uno with the UP501 GPS and an OLED screen.

The general operation is of the app is that there is a list of 175 coordinates in an array (wayPt). With each cycle, the app uses TinyGPS to return a lat/long. That lat long is run through getClosestPt (which calculates the min distance to all points to get the closets distance). The function returns the closest distance and it is used (at the moment) to beep the piezo based on distance (in meters). The idea being, as you get closer to a point, the tone of the beep gets higher along with the beep count. A GPS locator of sorts. I am also using the OLED to render a few graphics and intend to do some things with the Lat/Long points (if I can figure this out). There are 3 graphics arrays (removed from the code below due to restrictions).

The issue I am having is that the app crashes after a number of cycles (seconds up to a minute or two). I think that I am wasting memory (or processing too much) but am not sure where. The OLED bits are commented out right now (in the main loop). When I turn them on, the app runs, then stops after a few seconds. I don’t think it is the OLED bits, but rather the function call I have (minDist = getClosestPt(flat, flon):wink: that seems to bog down the processing and if I add in any more features (eg OLED), it craps out.

A few questions:

  • I want to store a list of GPS coordinates - is the 2D float array I have (wayPt) being dealt with in the best way? Is there a better way?

  • I think my getClosestPt function (and the HaverSine formula) are bogging the controller down. It is processing 175 points through these calculations every second. I can’t think of a better way to do what I want. I considered trying to weed out points in getClosestPt() by comparing them to the reference point being fed to the function and ignoring those where lat1-lat2 && lon1 - lon2 distances where greater than some value, but figured that would waste just as many resources than just throwing them all at the HaverSine formula.

  • any other tips welcome.

Sketch.pde is attached. (code dump was too large)

sketch.pde (19.4 KB)

Hi,

I can’t see anything obviously wrong with your code. You didn’t say where you got the TinyGPS and OLED libraries from, so I can’t check those. I have a few observations:

  1. Rather than store the coordinates in a 2D array, I would declare a struct or class to represent a coordinate (i.e. pair of lat/long values), then store an array of those. However, it won’t make any difference to the amount of memory used (on the assumption that you don’t declare any virtual functions in the class or struct), it will still be 1400 bytes - leaving you 648 bytes free for the stack, static data and string literals. With care, this should be enough, if the GPS and OLED libraries are frugal in their use of RAM.

  2. If you recompile under Arduino 1.0, you can put all the string literals that you use in print() calls in progmem very easily, using the F() macro, thereby freeing up RAM.

  3. Function HaverSine includes this:

  float a = sin(dLat/2) * sin(dLat/2) +
	  cos(lat1 * ToRad) * cos(lat2 * ToRad) *
	  sin(dLon/2) * sin(dLon/2);

You are calculating sin(dLat/2) and sin(dLon/2) twice. Unless gcc knows that these calls are side-effect free, it can’t optimize the second one away. I suggest you calculate each of these just once.

deejayspinz: - any other tips welcome.

Hi!

I'm certainly not an expert, but I'm curious: Does the program work without the OLED code, or does it just run longer without crashing?

I'd suggest adding the code from http://arduino.cc/playground/Code/AvailableMemory and doing a check at the end of setup as well as at points during the main loop.

Would at least give you a general idea of how you're doing with memory usage.

Brad.

Thx for the tips guys.. I'll try to dig a little deeper. It still seems that the HaverSine calculation being run through the array of pts is the culprit. I also fixed the formula to not take a double hit on calcuating sin(dLat/2) and sin(dLon/2). Did not make a difference though.

What evidence have you that the problem is caused looping through the array of points calling HaverSine?

If it really calling HaverSine that is causing the problem, I can think of a couple of possibilities:

  1. The trig functions you are calling use more stack than most of the rest of the sketch and you are short of RAM. Fix this by moving all the string literals into progmem.

  2. Variable 'a' sometimes lies outside the interval 0.0 ... 1.0 so one of the sqrt functions either crashes or returns a NaN (I don't know how the AVR floating point library is implemented, so I can't say which). If it's returning a NaN then it's possible that something that processes the result doesn't handle NaNs.

I have looked at the UP501 several times because it is so much cheaper than other options. At the risk of straying off topic, have you found it easy to work with?

@dc42 - Some answers for you:

Q: What evidence have you that the problem is caused looping through the array of points calling HaverSine? A: Its not scientific, but when I comment out the call to run through the array of waypoints to calculate the min distance, it runs smooth as silk ( minDist = getClosestPt(flat, flon) ; )

Q: 1. The trig functions you are calling use more stack than most of the rest of the sketch and you are short of RAM. Fix this by moving all the string literals into progmem. A: this is possible. I have yet to follow the other suggestion above to see what the memory usage is like (will do this). As for moving string literals, the graphic arrays are already there. However, when I tried to move the waypoint array, I don't think it liked the fact that they were floats (dont think floats are supported). I guess I could store them as a string array and then parse each coordinate into a float when I run through the distance calc. Problem is, I have been having a hell of a time getting my head around converting these to floats. Its the byte array part that I dont get. I keep thinking I can do something in instr() where you ID the start and stop positions for each coordinate, extract it and convert to float, but its the byte array thing I cant get my head around. Anyone have a function for this?

Q: Variable 'a' sometimes lies outside the interval 0.0 ... 1.0 so one of the sqrt functions either crashes or returns a NaN (I don't know how the AVR floating point library is implemented, so I can't say which). If it's returning a NaN then it's possible that something that processes the result doesn't handle NaNs.

A:Thx for this tip. I'll look into this.

Overall, what I find odd is that if I comment out all my debugging statements (serial print), it runs smooth and I can tell it still works by the beep tones I have setup (so I know it is successfully calculating minDist each time). It is still stable when I turn most of them back on, but when I turn on the one that prints minDist, it crashes after a few iterations almost every time.

Serial.print("Delta = ");printFloat(minDist, 2);Serial.println(" Meters ")

Is it just the quantity of literal strings you print that causes the problem? Every time you do ‘Serial.print(“some string”)’ you are using up RAM to hold “some string”. That is why I suggested moving the string literals into PROGMEM. This is easier to do with Arduino 1.0 because it provides the F() macro and support for flash strings in the print() functions.

As you still haven’t said where you are getting the TinyGPS and SSD1306 libraries from, I can’t compile your sketch, so I can help with getting the float array into progmem.

TinyGPS Library 11 drivers here and GPS Drivers here and OLED drivers here.

Overall, what I find odd is that if I comment out all my debugging statements (serial print), it runs smooth and I can tell it still works by the beep tones I have setup (so I know it is successfully calculating minDist each time). It is still stable when I turn most of them back on, but when I turn on the one that prints minDist, it crashes after a few iterations almost every time.

Unless those serial prints are causing timing issues (Haven't looked at your code) that screams "Out of Memory!"

Serial.print("Delta = ");printFloat(minDist, 2);Serial.println(" Meters   ");

The Serial class is capable of printing floats with any number of decimal points. The printFloat method is not needed.

This works:

const float wayPt[175][2] PROGMEM =
{
  { -79.88897,43.25981 },
  { -79.88347,43.26085 },
  { -79.8791,43.2909 }
};

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  for (unsigned int i = 0; i < 3; ++i)
  {
    for (unsigned int j = 0; j < 2; ++j)
    {
      float f = pgm_read_float_near(&wayPt[i][j]);
      Serial.println(f, 5);
    }
  }

  Serial.println();
  delay(1000);
}

thx @dc42. I'll give this a try tonight and report back.

deejayspinz:
Overall, what I find odd is that if I comment out all my debugging statements (serial print), it runs smooth and I can tell it still works by the beep tones I have setup (so I know it is successfully calculating minDist each time). It is still stable when I turn most of them back on, but when I turn on the one that prints minDist, it crashes after a few iterations almost every time.

Well I took a quick peek and in getClosestPt, if you enable all your debugging, you’ve got over 180 bytes of string literals alone. I suppose it’s a non-issue now though.

Brad.

@dc42 - Thx, your suggestion was the fix. It was a memory problem. I added PROGMEM to the wayPt array definition (const float wayPt[175][2] PROGMEM) which freed up the memory. Although this seemed to fix the freezing issue, I found the values were coming out at 0.00000 until I realized that I did not use the call you showed in your suggestion (pgm_read_float_near(&wayPt*[j])*). At first it still did not work until I realized I was leaving out the ‘&’ before wayPt. After adding that, my floats came through as defined in the array and now the code is calculating the distance values fast and accurately.
They should add a ‘Thanks’ button to this forum. Thx everyone else as well. I can now move on to the OLED bits and animating the results! I’ll post a link to the project when I blog about it.