Using GFX, GPS, and SSD1306 Libraries together

Post here about your Arduino projects, get help - for Adafruit customers!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
magruder13
 
Posts: 4
Joined: Thu Sep 06, 2012 2:17 am

Using GFX, GPS, and SSD1306 Libraries together

Post by magruder13 »

I'm trying to make a very simple project where I read in data via GPS and print it out to a LCD screen. All I have done is take the example code from each and tried to merge them together. If I initialize the LCD screen, I get a ton of issues.
Here is what I have

Code: Select all

#include <Wire.h>
#include <Adafruit_GPS.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SoftwareSerial.h>
#include <MemoryFree.h>
//   Connect the GPS TX (transmit) pin to Digital 3
//   Connect the GPS RX (receive) pin to Digital 2
SoftwareSerial mySerial(3, 2);
Adafruit_GPS GPS(&mySerial);
#define GPSECHO  true

//DEFINE LCD PINS and CONST
#define OLED_DC 11
#define OLED_CS 12
#define OLED_CLK 10
#define OLED_MOSI 9
#define OLED_RESET 13
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); //THIS LINE CAUSES ALL MY ISSUES


// this keeps track of whether we're using the interrupt
// off by default!
boolean usingInterrupt = false;
void useInterrupt(boolean); // Func prototype keeps Arduino 0023 happy

void setup()  
{
  
  // connect at 115200 so we can read the GPS fast enough and echo without dropping chars
  // also spit it out
  Serial.begin(115200);
  Serial.println("Adafruit GPS library basic test!");
  // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800
  GPS.begin(9600);
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);   // 1 Hz update rate
 
  useInterrupt(true);
  delay(1000);
}


// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(TIMER0_COMPA_vect) {
  char c = GPS.read();
  // if you want to debug, this is a good time to do it!
#ifdef UDR0
  if (GPSECHO)
    if (c) UDR0 = c;  
    // writing direct to UDR0 is much much faster than Serial.print 
    // but only one character can be written at a time. 
#endif
}

void useInterrupt(boolean v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
    usingInterrupt = true;
  } else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
    usingInterrupt = false;
  }
}

uint32_t timer = millis();
void loop()                     // run over and over again
{

  
  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    // a tricky thing here is if we print the NMEA sentence, or data
    // we end up not listening and catching other sentences! 
    // so be very wary if using OUTPUT_ALLDATA and trytng to print out data
    //Serial.println(GPS.lastNMEA());   // this also sets the newNMEAreceived() flag to false
  
    if (!GPS.parse(GPS.lastNMEA()))   // this also sets the newNMEAreceived() flag to false
      return;  // we can fail to parse a sentence in which case we should just wait for another
  }

  // if millis() or timer wraps around, we'll just reset it
  if (timer > millis())  timer = millis();

  // approximately every 2 seconds or so, print out the current stats
  if (millis() - timer > 2000) { 
    timer = millis(); // reset the timer
    Serial.print(F("\nfreeMemory()="));
    Serial.println(freeMemory());    
    Serial.println(("\n"));
    Serial.print(GPS.hour, DEC); Serial.print(F(":"));Serial.println(GPS.minute, DEC);
    Serial.print(F("Fix: ")); Serial.println((int)GPS.fix);
    Serial.print(F("Qual: ")); Serial.println((int)GPS.fixquality); 
    if (GPS.fix) {   
      Serial.print(F("Speed: ")); Serial.println(GPS.speed);
      Serial.print(F("Angle: ")); Serial.println(GPS.angle);
      Serial.print(F("Alt: ")); Serial.println(GPS.altitude);
      Serial.print(F("Sats: ")); Serial.println((int)GPS.satellites);
    }
  }
}
If I leave the line "Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);" not in my program, it will read in data from the GPS and spit it out okay. Once I include it, all the data goes haywire! I don't even use the display, JUST initialize it!

I've tried moving the SPI pins around to see if it was a Timer that wasn't allowing the pins to work correctly, but no success. If anyone could help me, I would GREATLY appreciate it!

User avatar
adafruit_support_mike
 
Posts: 67454
Joined: Thu Feb 11, 2010 2:51 pm

Re: Using GFX, GPS, and SSD1306 Libraries together

Post by adafruit_support_mike »

You're probably running into memory limits for the microcontroller. The SSD1306 library uses a 1K buffer above and beyond the actual code, for instance. The fact that the system goes haywire when you try to initialize the display is a sign that there isn't enough free memory left to create that buffer.

Take a look at this tutorial and see if any of the memory-reduction tips will help you: http://learn.adafruit.com/memories-of-an-arduino

User avatar
magruder13
 
Posts: 4
Joined: Thu Sep 06, 2012 2:17 am

Re: Using GFX, GPS, and SSD1306 Libraries together

Post by magruder13 »

Thanks a bunch Mike,

Even if I go in and cut out everything I can to limit memory do you think it will even be possible to fit these two into the same program? Should I switch to a Mega? My project needs to be as small as possible, so I could try to fabricate my own Mega, that could be very complicated though.

User avatar
adafruit_support_mike
 
Posts: 67454
Joined: Thu Feb 11, 2010 2:51 pm

Re: Using GFX, GPS, and SSD1306 Libraries together

Post by adafruit_support_mike »

I'm afraid there's no fixed answer.. memory tuning is very much a cut-and-try affair, and the results you get depend on the tradeoffs you're willing to make.

Start by getting rid of any Serial.print() traffic that isn't absolutely essential to making the program run. Then wrap any literal strings that remain in the F() macro so the compiler stores them in PROGMEM rather than SRAM. Those are easy memory savers.

If that doesn't give you enough space, start looking for buffers and reducing their size if you can. Then make copies of all your libraries and start deleting the code from any functions your program doesn't actually use.

If none of that works, or you just don't want to invest the time scanning, trimming, and trying, a Mega will give you more memory to work with. As for the physical size constraints, a Mega is ony an inch longer than an Uno. Taking the "as small as possible" constraint as a guideline, what are your hard limits on size?

User avatar
magruder13
 
Posts: 4
Joined: Thu Sep 06, 2012 2:17 am

Re: Using GFX, GPS, and SSD1306 Libraries together

Post by magruder13 »

Mike,

I am VERY limited on size. It's for my Sr. Design project (EE student) and we are going to fabricate our own board, just using the Pro Mini right now as a prototype board. I've grabbed an extra Mega ADK a friend had and I'm going to get the code working on there first, I just have a few questions on memory size. Does the bootloader take up ANY SRAM? Or is it strickly related to Flash Memory? The Mega has 8K SRAM and assuming the bootloader doesn't take up any memory, once I get my program working on the mega, if I'm using the freeRam() function (given in the guide you gave me) I need it to say I have atleast 6k memory left for it to work properly on the ATmega328, correct?

That's going to be quite a tight squeeze. We want to limit the price of our product to be as inexpensive as possible, and a ATmega 2560 is $12 vs the $6 ATmega328. Do you know, off the top of your head, any ATmega processors we could use that has more than 2k memory, and can be programmed using the ISP or similar method? I don't need the massive amount of IO from the 2560, I'm only using 1ADC, SPI, and SoftwareSerial.

Thanks a lot for all your help!

User avatar
adafruit_support_mike
 
Posts: 67454
Joined: Thu Feb 11, 2010 2:51 pm

Re: Using GFX, GPS, and SSD1306 Libraries together

Post by adafruit_support_mike »

The bootloader doesn't use any SRAM once it hands control over to the main code. It does consume about 0.5k of Flash, but that's a separate issue.

The thousand-byte gorilla in your code will be the buffer for the OLED. That's 1k if you have a 128x64 display, and half that for a 128x32 display. You'll also need some space for strings from the GPS module, but with care and some tuning, you should be able to allocate a fixed block of memory at startup and do all your string handling there.

After that, most of your SRAM will be used by local variables in functions, so trim away as many local variables as you can, and try to keep your nested function calls as shallow as possible.. all those parameters consume space in the stack until execution leaves that function's scope.

You're correct that you'll want the Mega to report at least 6k of free memory in order to fit the final product into an Uno.

WRT other processors, leaving the set of devices supported by the Arduino environment will put a lot of extra work on your plate: you'll have to go back and reimplement a bunch of low-level stuff like the Wire library. As a general rule, making a significant technology shift in the middle of a time-critical project is a reliable way to generate a train wreck.

For now, cultivate the most ruthless and bloody-minded attitude toward the code that you can, and start chopping out functions until you reach a show-stopper loss of functionality. Then put the last function you cut back in. Once you're down to the absolute minimum set of functions that don't utterly fail, start chopping out lines of code on the same principle.

Remember: the original Macintosh OS fit in 12k of ROM. You can do amazing things with limited resources if you're brutal enough.

User avatar
magruder13
 
Posts: 4
Joined: Thu Sep 06, 2012 2:17 am

Re: Using GFX, GPS, and SSD1306 Libraries together

Post by magruder13 »

Hey Mike,

Another pretty easy question for you. I'm trying to use analogRead and i'm getting crazy values because the GFX code sets the analog reference to external. Why exactly do you guys do that? Can I switch it back to internal or should I hookup the AREF pin to the values I want to compare to?

Thanks,

Chris

User avatar
adafruit_support_mike
 
Posts: 67454
Joined: Thu Feb 11, 2010 2:51 pm

Re: Using GFX, GPS, and SSD1306 Libraries together

Post by adafruit_support_mike »

'EXTERNAL' gives the best combination of range and stability. The ADC can only measure from zero to the reference voltage, so choosing a reference puts constraints on what you can do.

The ATmega328 has a built-in bandgap reference (very stable and pretty much independent of temperature), but its nominal value is 1.1v.. less than a quarter of your supply voltage. There's a 2.56v reference as well (scaled up from the bandgap I think) but that also constrains to you half the supply voltage. You can use the 5v rail as a reference, but if you're driving LEDs at 400kHz, the rail will have a lot of noise on it. That throws the reliability of your readings way off.

With the reference set to 'EXTERNAL', you can run a 100k resistor from the 5v rail to the Aref pin, then add a 1-10uF cap from the aRef pin to GND, and have a nice, smooth reference for full-scale ADC readings.

You're free to set the reference to any of the other options though.

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino”