0

Low-power GPS logging - how to do it effectively?
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Low-power GPS logging - how to do it effectively?

by emkli on Tue Jan 10, 2017 3:19 pm

Hi there!

I'm trying to build a GPS logger with the GPS feather wing and a Feather 32u4 Adalogger (might become a M0 in the future) for my hiking tours. For that purpose, I think that it's enough to log only every 10 to 15 seconds or so. I also want to save battery life as much as possible, so I want to put all components to a rest as much as possible.

I've checked the Adafruit GPS sketches coming with the code. There, It seems that the GPS (or, to be more precise, the serial line) is queried every millisecond to not miss a character. Isn't there any more effective way to query it less frequently and still be sure that I got every sentence once?

What might be the best possibility to achieve this? So far, I thought about:

1. Setting the fix rate to 10 seconds (with the PMTK_SET_NMEA_UPDATE_100_MILLIHERTZ command) and putting the processor to sleep in the meantime.
But how can I assure that the processor wakes up again right before the GPS starts to spit out the next sentence? And how do I assure that I receieve and log all neccessary data (every sentence) to the SD? Is it a possibility to count the millis I need to parse and save the GPS data and put the processor to sleep for exactly 9500 Millis - the time I've needed to parse everything? Or is there even a more effective way? And how can I be sure that I got all the data? Working with flags (if got_GPGGA and got_GPGSA and ... then go_to_sleep) maybe?

2. putting the GPS to sleep for a certain time, then wake it up again, parse data for about a second or so and put it back to sleep. And, same problem here, how to assure that I got all the data I need?

Or is there even a better approach for all this? I would be glad to get some advice.

Thanks in advance!
Markus

emkli
 
Posts: 43
Joined: Sun Mar 17, 2013 1:37 pm

Re: Low-power GPS logging - how to do it effectively?

by adafruit_support_rick on Wed Jan 11, 2017 12:34 pm

You can put the GPS in "periodic mode". It will only transmit data on a specified schedule, so you can set it for an update every 15 seconds.

If you use a Feather 32U4 Adalogger, you can put the processor to sleep, and you can have it automatically wake up when the GPS starts transmitting.

Read through this thread. It's a bit long, but in it we work out the details of doing this exact thing:
viewtopic.php?f=8&t=54272&hilit=+turtle

The only difference is that you won't be using Software Serial. You'll be using the hardware serial built into the 32U4. But I think this will also wake the processor, so the scheme should work. Try it out to make sure.

I presume it's possible to do the same thing with the M0 feathers, but I don't have the experience with sleep modes on the M0 to say for sure. You're better off with a 32U4 feather.

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Low-power GPS logging - how to do it effectively?

by emkli on Wed Jan 11, 2017 3:19 pm

Hi Rick!
Thanks for the answer! It seems to be a neat solution. However, it also seems that the HardwareSerial or the 1ms interrupt is heavily unreliable. This is the output I received via the Serial Monitor from the Feather 32u4:

Code: Select all | TOGGLE FULL SIZE
$GPGGA,190149.000,4847.0931,N,01524.2712,E,1,07,1.08,561.3,M,44.5,M,,*62
$GPGLL,4847.0931,N,01524.2712,E,190149.000,A,A*5
$PS,,,21,71,50,3,,,14,.809*D
GGV41132,8272,25,5,41,801161,5021*7
GGV421,93,932,52,9,63,939,5,7221*2
GGV431,61,062,00,8,1,7153,20,3,9*6
GGV441,80,5,4
$GRC104.0,,8703,,15421,,.3306,117,A6
$PVG306,,M00,,.5KA3
$PGTOP,11,3*6F
$GPGGA,190150.000,4847.0930,N,01524.2715,E,1,07,1.08,561.3,M,44.5,M,,*6C
$GPGLL,4847.0930,N,01524.2715,E,190150.000,A,A*5
$PS,,,21,71,50,1,,,14,.809*D
GGV41132,8272,25,5,51,4,7,61,5021*6
GGV421,4,3132,52,9,63,939,5,7221*2
GGV431,613162,00,8,1,7153,203181*8
GGV441,80,5,4
GRC105.0,,8703,,05421,,.84.5101,,*5
$PT,92,,M00,,.5KA0
$PGTOP,11,3*6F
$GPGGA,190151.000,4847.0928,N,01524.2718,E,1,07,1.08,561.3,M,44.5,M,,*69
$GPGLL,4847.0928,N,01524.2718,E,190151.000,A,A*5
$GGAA31,91,52,61,,,,.010,.00
$PS,,,3248,6,11,8271,94,7,6173,5,77
$PS,,,34,319,81,8113,21,1,2,1,5,67
$PS,,,30,3162,1,826,30,6,00,3181*8
$PS,,,31,229*E
$PM,91100A44.98N054278E02,85,117,A5
$PT,8.2T,,.2N04,,*4
$PGTOP,11,3*6F
$GPGGA,190152.000,4847.0926,N,01524.2721,E,1,07,1.08,561.3,M,44.5,M,,*6E
$GPGLL,4847.0926,N,01524.2721,E,190152.000,A,A*5
$PS,,,1,91,52,61,,,14,.809*0
$PS,,,32,8222,1,8271,94,7,61,502177
$PS,,,34,3132,52,913,21,1,2,7221*2
GPS,,,30,3162,00,86,30,6,00,3181*8
GGV4,,31,229*E
GRC105.00A44.96N05422,,.0,87,117,A5
$PT,87,,M,.0N03,,*0
$PGTOP,11,3*6F
$GPGGA,190153.000,4847.0926,N,01524.2721,E,1,07,1.08,561.3,M,44.5,M,,*6F
$GPGLL,4847.0926,N,01524.2721,E,190153.000,A,A*0
GGAA31,1,71,50,3,,,.010,.0*D


Lots of missing chars obviously. I just used the normal mode, not periodic (BTW: did you got any idea in the meantime what the 223 command means that they recommend to use together with the 225 command?).

EDIT: interesting. I just removed the "print every char" command and changed the parseNMEA() function to:

Code: Select all | TOGGLE FULL SIZE
void parseNMEA() {
 
  if (GPS.newNMEAreceived()) {
    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
    else
      Serial.println(GPS.lastNMEA());
  }
}


It turns out, that the 32u4 only recieves vaild GPGGA setences once a second:

Code: Select all | TOGGLE FULL SIZE
$GPGGA,192349.000,4847.0928,N,01524.2794,E,2,06,1.48,562.0,M,44.5,M,0000,0000*62


$GPGGA,192350.000,4847.0928,N,01524.2794,E,2,06,1.48,562.0,M,44.5,M,0000,0000*6A


$GPGGA,192351.000,4847.0928,N,01524.2794,E,2,06,1.48,562.0,M,44.5,M,0000,0000*6B


$GPGGA,192352.000,4847.0928,N,01524.2794,E,2,06,1.48,562.0,M,44.5,M,0000,0000*68


$GPGGA,192353.000,4847.0927,N,01524.2794,E,2,06,1.48,562.0,M,44.5,M,0000,0000*66


$GPGGA,192354.000,4847.0927,N,01524.2794,E,2,06,1.47,562.0,M,44.5,M,0000,0000*6E


Only the rest is scrambled. And it doesn't matter if I'm logging only RMCGGA or ALLDATA.

And another question: you commented that one should "leave the mode alone". However, I wanted to implement a function that uses the GPS in continuous mode until no fix is available and then switch to the periodic mode. Do you think that this is possible?

This is my code, merged with your sketches from the turtle tracker, without the sleep mode implemented yet (I've cut out some non-GPS-related functions):

Code: Select all | TOGGLE FULL SIZE
#define DEBUG_MODE true

#include <SPI.h>
#include <Wire.h>
#include <SD.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GPS.h>

#define VBATPIN A9

// Initialize the OLED Display + Extras
Adafruit_SSD1306 display = Adafruit_SSD1306();
#define BUTTON_A 9
#define BUTTON_B 6
#define BUTTON_C 5
#define LED      13

// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;
File logfile;
#define LOG_SDCS 4

// Initialize the GPS Module

// what's the name of the hardware serial port?
#define GPSSerial Serial1

// Connect to the GPS on the hardware port
Adafruit_GPS GPS(&GPSSerial);

//Periodic (sleep) mode command for GPS module
//2 is periodic standby mode
//5000 is the active time in milliseconds
//15000 is the sleep time in milliseconds
//Leave the mode alone. Change the times to fit your needs
// REMEMBER to CHANGE the CHECKSUM
// Checksum calculator is here:
//  http://www.hhhh.org/wiml/proj/nmeaxor.html
#define PMTK_PERIODIC_MODE "$PMTK225,2,5000,15000,0,0*18"
//#define PMTK_PERIODIC_MODE_223 "$PMTK223,1,25,180000,60000*38"
#define PMTK_NORMAL_MODE "$PMTK225,0*2B"

uint32_t dataTimeStamp; //time of most recent data received from GPS.
                        //  Sleep arduino when it's been more than 1 second
                        //  since the most recent data from GPS.

void setup() {

  // Setup the interrupt register
  OCR0A = 0xAF;
  TIMSK0 |= _BV(OCIE0A);
 
  // put your setup code here, to run once:
  if (DEBUG_MODE)
    while (!Serial);  // uncomment to have the sketch wait until Serial is ready

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  Serial.begin(115200);
  GPS.begin(9600);

  setup_GPS();
  setup_Display();
  setup_IO();
 
}

void setup_GPS() {
 
  // Initialize GPS
  //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); // RMC (recommended minimum) and GGA (fix data) including altitude
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_ALLDATA);
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
  //GPS.sendCommand(PMTK_SET_NMEA_UPDATE_100_MILLIHERTZ); // 10 sec. update rate
  GPS.sendCommand(PGCMD_ANTENNA); // Request updates on antenna status
  GPS.sendCommand(PMTK_NORMAL_MODE);
}

void setup_Display() {

  // ...
}

void setup_IO() {
  // Initialize buttons from OLED Feather Wing
  // ...
}

void loop() {
 
  parseNMEA();
 
}

void parseNMEA() {
 
  if (GPS.newNMEAreceived()) {
    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
    //else
      //Serial.println(GPS.lastNMEA());
  }
}

// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(TIMER0_COMPA_vect) {
  // on interrupt, read from the GPS
  char c = GPS.read();
  if (c)
    Serial.print(c);
  }

float getBatVoltage() {
  // gets the voltage at Pin D9/A7 and calculates the current battery status
  //...
}

emkli
 
Posts: 43
Joined: Sun Mar 17, 2013 1:37 pm

Re: Low-power GPS logging - how to do it effectively?

by adafruit_support_rick on Wed Jan 11, 2017 4:33 pm

Ya know, I've never really liked that interrupt thing. Do it this way and see if that improves the situation:
Code: Select all | TOGGLE FULL SIZE
#define DEBUG_MODE true

#include <SPI.h>
#include <Wire.h>
#include <SD.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GPS.h>

#define VBATPIN A9

// Initialize the OLED Display + Extras
Adafruit_SSD1306 display = Adafruit_SSD1306();
#define BUTTON_A 9
#define BUTTON_B 6
#define BUTTON_C 5
#define LED      13

// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;
File logfile;
#define LOG_SDCS 4

// Initialize the GPS Module

// what's the name of the hardware serial port?
#define GPSSerial Serial1

// Connect to the GPS on the hardware port
Adafruit_GPS GPS(&GPSSerial);

//Periodic (sleep) mode command for GPS module
//2 is periodic standby mode
//5000 is the active time in milliseconds
//15000 is the sleep time in milliseconds
//Leave the mode alone. Change the times to fit your needs
// REMEMBER to CHANGE the CHECKSUM
// Checksum calculator is here:
//  http://www.hhhh.org/wiml/proj/nmeaxor.html
#define PMTK_PERIODIC_MODE "$PMTK225,2,5000,15000,0,0*18"
//#define PMTK_PERIODIC_MODE_223 "$PMTK223,1,25,180000,60000*38"
#define PMTK_NORMAL_MODE "$PMTK225,0*2B"

uint32_t dataTimeStamp; //time of most recent data received from GPS.
                        //  Sleep arduino when it's been more than 1 second
                        //  since the most recent data from GPS.

void setup() {
 
  // put your setup code here, to run once:
  if (DEBUG_MODE)
    while (!Serial);  // uncomment to have the sketch wait until Serial is ready

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  Serial.begin(115200);
  GPS.begin(9600);

  setup_GPS();
  setup_Display();
  setup_IO();
 
}

void setup_GPS() {
 
  // Initialize GPS
  //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); // RMC (recommended minimum) and GGA (fix data) including altitude
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_ALLDATA);
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
  //GPS.sendCommand(PMTK_SET_NMEA_UPDATE_100_MILLIHERTZ); // 10 sec. update rate
  GPS.sendCommand(PGCMD_ANTENNA); // Request updates on antenna status
  GPS.sendCommand(PMTK_NORMAL_MODE);
}

void setup_Display() {

  // ...
}

void setup_IO() {
  // Initialize buttons from OLED Feather Wing
  // ...
}

void loop() {
  if (GPSSerial.available())
    GPS.read();
   
  parseNMEA();
}

void parseNMEA() {
 
  if (GPS.newNMEAreceived()) {
    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
    else
      Serial.println(GPS.lastNMEA());
  }
}

float getBatVoltage() {
  // gets the voltage at Pin D9/A7 and calculates the current battery status
  //...
}

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Low-power GPS logging - how to do it effectively?

by emkli on Wed Jan 11, 2017 5:51 pm

Thank you for you quick reply! I've switched to loop mode and now I get all the data... Hoooowever... The sleep mode thing doesn't seem to work quite good.

I've switched to a bare minimum version of the code of your parsing_sleep sketch to just focus on the GPS relevant code:

Code: Select all | TOGGLE FULL SIZE
#include <Adafruit_GPS.h>
#include <avr/wdt.h>
#include <avr/sleep.h>

#define GPSSerial Serial1
Adafruit_GPS GPS(&GPSSerial);
#define GPSECHO true

//  http://www.hhhh.org/wiml/proj/nmeaxor.html
#define PMTK_PERIODIC_MODE "$PMTK225,2,5000,15000,0,0*18"
//#define PMTK_PERIODIC_MODE_223 "$PMTK223,1,25,180000,60000*38"
#define PMTK_NORMAL_MODE "$PMTK225,0*2B"

uint32_t dataTimeStamp; //time of most recent data received from GPS.
                        //  Sleep arduino when it's been more than 1 second
                        //  since the most recent data from GPS.

void setup() {
  while(!Serial);

  Serial.begin(115200);
  Serial.println("Adafruit GPS library basic test!");

  GPS.begin(9600);
  GPS.sendCommand(PMTK_NORMAL_MODE);
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_ALLDATA);
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
  GPS.sendCommand(PGCMD_ANTENNA);
  GPS.sendCommand(PMTK_PERIODIC_MODE);

  OCR0A = 0xAF;
  TIMSK0 |= _BV(OCIE0A);

  Serial.println(PMTK_Q_RELEASE);
}


// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(TIMER0_COMPA_vect) {
  }

void loop()                     // run over and over again
{
 
  //if (millis() - dataTimeStamp > 1100)
  //  sleepNow();

  char c = GPS.read();
  if (GPSECHO)
    if (c)
      Serial.print(c);
  if (c) dataTimeStamp = millis();
  if (GPS.newNMEAreceived()) {
    //Serial.println(GPS.lastNMEA());
    if (!GPS.parse(GPS.lastNMEA()))
      return;
  }
}

void sleepNow() {
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    sleep_mode();
    sleep_disable();
}


When I use sleep mode, I only get the sentence "Adafruit GPS library basic test!" from the beginning of the setup function. Then, nothing more seems to happen. The GPS, however, blinks correctly. It seems to receive the sleep command.

When I don't use the sleep mode(i.e. comment out the if (millis() - dataTimeStamp > 1100) line), and put the GPS into normal operating mode, the 32u4 receives all the data happily. Same applies when I put the GPS into sleep mode.

So it seems, the problem is not with the GPS, but with the 32u4. I've put a serial sentence after every step in the setup routine, echoing the issued command, and the output is this:

Code: Select all | TOGGLE FULL SIZE
Adafruit GPS library basic test!
GPS.begin(9600);
GPS.sendCommand(PMTK_NORMAL_MODE);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_ALLDATA);
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
GPS.sendCommand(PGCMD_ANTENNA);
GPS.sendCommand(PMTK_PERIODIC_MODE);
$PMTK605*31
Serial.println(PMTK_Q_RELEASE);


Initialization works obviously, and before receiving the first GPS data, the module spits out some status codes:

Code: Select all | TOGGLE FULL SIZE
$PMTK010,003*2C
$PMTK001,225,3*35
$PMTK001,314,3*36
$PMTK001,220,3*30
$PGACK,33,1*6F
$PMTK001,225,3*35


In the main loop, I've added:

Code: Select all | TOGGLE FULL SIZE
  if (millis() - dataTimeStamp > 1100) {
    Serial.println("Going to sleep now...");
    sleepNow();
  }


However, this string never gets printed. The Serial Monitor stops getting GPS data when the module goes to sleep, but the string is never get printed. And when the GPS module wakes up again, no data is printed anymore.

So my guess is, the 32u4 isn't happy with the sleep or the millis() loop somehow. Do you have any idea?

emkli
 
Posts: 43
Joined: Sun Mar 17, 2013 1:37 pm

Re: Low-power GPS logging - how to do it effectively?

by adafruit_support_rick on Wed Jan 11, 2017 8:33 pm

There's a thing about the USB serial port on a 32U4 and sleep. I've never been able to get any serial data out of one of them after it wakes up. But that doesn't mean it's not working. Try lighting the pin 13 LED when you wake and turn it off when you sleep. Then you'll be able to see what's going on.

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Low-power GPS logging - how to do it effectively?

by emkli on Thu Jan 12, 2017 1:12 pm

Thanks for your advice. However, the problem I'm facing seems to be a more fundamental one as the 32u4 still never comes back from sleep. I did the following changes in the sketch:

[*]Initialized pin 13 as output port and added a digitalWrite(led, HIGH) in the loop[/*]
[*]Put the LED off before going to sleep[/*]
[*]Enlarged the delay of the sleep condition to 1500 msecs[/*]
[*]Initialized dataTimeStamp to millis() at the end of the setup() routine (which was obivously a source of constant instability)[/*]

The GPS starts to send data, the 32u4 happily sends it over Serial. Then the GPS goes off - as does the 32u4 (LED is off). And that's it. The GPS comes back, but the 32u4 is "dead". No LED, no nothing.

Maybe there's something wrong with the sleepNow() function?

Code: Select all | TOGGLE FULL SIZE
void sleepNow() {
    Serial.println("Going to sleep now...");
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    sleep_mode();
    sleep_disable();
    Serial.println("Awake again!");
}

emkli
 
Posts: 43
Joined: Sun Mar 17, 2013 1:37 pm

Re: Low-power GPS logging - how to do it effectively?

by adafruit_support_rick on Thu Jan 12, 2017 4:38 pm

I've never done this with hardware serial. It's possible that it won't wake the processor. It may strictly need pin-change interrupt or an external interrupt, not a UART interrupt.

Try changing to software serial on pins 0 and 1. That ought to work, and it ought to wake the processor. RX is 0 and TX is 1.

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Low-power GPS logging - how to do it effectively?

by emkli on Thu Jan 12, 2017 4:55 pm

It's just these two lines, correct?

Code: Select all | TOGGLE FULL SIZE
SoftwareSerial mySerial(0, 1); // RX, TX

#define GPSSerial mySerial
//#define GPSSerial Serial1


Same effect: nothing. Guess, this solution is out.

emkli
 
Posts: 43
Joined: Sun Mar 17, 2013 1:37 pm

Re: Low-power GPS logging - how to do it effectively?

by adafruit_support_rick on Thu Jan 12, 2017 6:16 pm

Wait, wait ... This is my bad. I got confused and through that you could run Software Serial on pins 0 and 1. But you can't.
You can run software serial on pins 11 and 12, and this does work if you do that. I've just tested it, and the feather wakes up when the GPS wakes up.

Now, the problem is that the GPS featherwing is hardwired to pins 0 and 1. But you can change that. See this page of the tutorial.
https://learn.adafruit.com/adafruit-ult ... -data-pins
Cut the traces, and then run jumpers from TX to the hole next to 11 and from RX to the hole next to 12. That should put you in business.

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Low-power GPS logging - how to do it effectively?

by emkli on Sat Jan 14, 2017 11:30 am

Hmmm... Made the changes, but now, I'm getting absolutely nothing on Serial (and the module also won't go into sleep mode), not even in the plain EchoTest sketch. I wired RX to Pin 11 and TX to 12, as you proposed. I've also checked the physical connection - I have contact for both wires.

Is the following all the code that is needed to run the GPS module via SoftSerial?

Code: Select all | TOGGLE FULL SIZE
#include <SoftwareSerial.h>
SoftwareSerial mySerial(11, 12); // RX, TX
#define GPSSerial mySerial
Adafruit_GPS GPS(&GPSSerial);

emkli
 
Posts: 43
Joined: Sun Mar 17, 2013 1:37 pm

Re: Low-power GPS logging - how to do it effectively?

by adafruit_support_rick on Sat Jan 14, 2017 12:11 pm

Yes, that should work.

However, here's the thing I hacked up. It works for me. Note that you won't get anything in the Serial Monitor after the 32U4 sleeps and wakes up. That's just the way the world is ;)

Also note that I'm NOT using the interrupt.

Code: Select all | TOGGLE FULL SIZE
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
//
// This code shows how to listen to the GPS module in an interrupt
// which allows the program to have more 'freedom' - just parse
// when a new NMEA sentence is available! Then access data when
// desired.
//
// Tested and works great with the Adafruit Ultimate GPS module
// using MTK33x9 chipset
//    ------> http://www.adafruit.com/products/746
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada

#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
#include <avr/wdt.h>
#include <avr/sleep.h>

// If you're using a GPS module:
// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// If using software serial (sketch example default):
//   Connect the GPS TX (transmit) pin to Digital 3
//   Connect the GPS RX (receive) pin to Digital 2
// If using hardware serial (e.g. Arduino Mega):
//   Connect the GPS TX (transmit) pin to Arduino RX1, RX2 or RX3
//   Connect the GPS RX (receive) pin to matching TX1, TX2 or TX3

// If you're using the Adafruit GPS shield, change
// SoftwareSerial mySerial(3, 2); -> SoftwareSerial mySerial(8, 7);
// and make sure the switch is set to SoftSerial

// If using software serial, keep these lines enabled
// (you can change the pin numbers to match your wiring):
SoftwareSerial mySerial(11,12);
//#define mySerial Serial1
Adafruit_GPS GPS(&mySerial);

// If using hardware serial (e.g. Arduino Mega), comment
// out the above six lines and enable this line instead:

// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences.
#define GPSECHO  true

// 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

//Periodic (sleep) mode command for GPS module
//2 is periodic standby mode
//10000 is the active time in milliseconds
//50000 is the sleep time in milliseconds
//Leave the mode alone. Change the times to fit your needs
// REMEMBER to CHANGE the CHECKSUM
// Checksum calculator is here:
//  http://www.hhhh.org/wiml/proj/nmeaxor.html
#define PMTK_PERIODIC_MODE "$PMTK225,2,10000,50000,0,0*2D"

uint32_t dataTimeStamp; //time of most recent data received from GPS.
                        //  Sleep arduino when it's been more than 1 second
                        //  since the most recent data from GPS.

void setup()
{
while (!Serial);
  // 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!");
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  GPS.begin(9600);

  // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  // uncomment this line to turn on only the "minimum recommended" data
  //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);
  // For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since
  // the parser doesn't care about other sentences at this time

  // Set the update rate
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);   // 1 Hz update rate
  // For the parsing code to work nicely and have time to sort thru the data, and
  // print it out we don't suggest using anything higher than 1 Hz

  // Request updates on antenna status, comment out to keep quiet
  GPS.sendCommand(PGCMD_NOANTENNA);

  //Set GPS into Periodic Mode
  GPS.sendCommand(PMTK_PERIODIC_MODE);

  // the nice thing about this code is you can have a timer0 interrupt go off
  // every 1 millisecond, and read data from the GPS for you. that makes the
  // loop code a heck of a lot easier!
  useInterrupt(false);

  delay(1000);
  // Ask for firmware version
  mySerial.println(PMTK_Q_RELEASE);
  dataTimeStamp = millis();
}


// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(TIMER0_COMPA_vect) {
  char c = GPS.read();
  if (c)   dataTimeStamp = millis();
  // 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 (millis() - dataTimeStamp > 1100)
    sleepNow();
 
  // in case you are not using the interrupt above, you'll
  // need to 'hand query' the GPS, not suggested :(
  if (! usingInterrupt) {
    // read data from the GPS in the 'main loop'
    char c = GPS.read();
    // if you want to debug, this is a good time to do it!
    if (GPSECHO)
      if (c) Serial.print(c);
     
     if (c)   dataTimeStamp = millis();
  }

  // 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 > 1000) {
    timer = millis(); // reset the timer

    Serial.print("\nTime: ");
    Serial.print(GPS.hour, DEC); Serial.print(':');
    Serial.print(GPS.minute, DEC); Serial.print(':');
    Serial.print(GPS.seconds, DEC); Serial.print('.');
    Serial.println(GPS.milliseconds);
    Serial.print("Date: ");
    Serial.print(GPS.day, DEC); Serial.print('/');
    Serial.print(GPS.month, DEC); Serial.print("/20");
    Serial.println(GPS.year, DEC);
    Serial.print("Fix: "); Serial.print((int)GPS.fix);
    Serial.print(" quality: "); Serial.println((int)GPS.fixquality);
    if (GPS.fix) {
      Serial.print("Location: ");
      Serial.print(GPS.latitude, 4); Serial.print(GPS.lat);
      Serial.print(", ");
      Serial.print(GPS.longitude, 4); Serial.println(GPS.lon);

      Serial.print("Speed (knots): "); Serial.println(GPS.speed);
      Serial.print("Angle: "); Serial.println(GPS.angle);
      Serial.print("Altitude: "); Serial.println(GPS.altitude);
      Serial.print("Satellites: "); Serial.println((int)GPS.satellites);
    }
  }
}

void sleepNow() {
    digitalWrite(13, LOW);
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    sleep_mode();
    sleep_disable();
    digitalWrite(13, HIGH);
}

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Low-power GPS logging - how to do it effectively?

by emkli on Sat Jan 14, 2017 2:48 pm

Ok, I got success - partly at least. GPS is now in peroidic mode again. However, still nothing on the serial line. Really strange. Maybe I should attach an LED to the RX line to see if there is any data...

There is a couple of other questions that came to my mind during all the tinkering and heavy swearing.
1. Will the GPS lose fix when going into standby during periodic mode? It seemed to me like this, however, my antenna is only behind the window while I'm programming, so fix loss may be due to this as well.
2. Can I replace the coin cell by the LiPo voltage, i.e. soldering a wire from VBAT to the + pad of the battery holder? (If yes, does this also work for the battery on the RTC and Adalogger Featherwings?
3. I saw that there are functions to put the GPS into sleep mode manually and wake it up as well (this might be interesting in combination with the M0 for which i saw code to directly put it into sleep for a certain time). Is this "manual" standby identical to the periodic mode in terms of fix keeping and power save?

BTW: I ran into the same issue than one or more people from the turtle logger thread: the FIX LED is constantly lit sometimes. It seems to me that when the LED is on while the GPS goes to sleep, it will stay on during sleep.

emkli
 
Posts: 43
Joined: Sun Mar 17, 2013 1:37 pm

Re: Low-power GPS logging - how to do it effectively?

by adafruit_support_rick on Sat Jan 14, 2017 4:48 pm

emkli wrote:1. Will the GPS lose fix when going into standby during periodic mode?

Yes. That's why the turtle project has the GPS come alive for several seconds in periodic mode. You have to allow it time to regain the fix.
emkli wrote:2. Can I replace the coin cell by the LiPo voltage, i.e. soldering a wire from VBAT to the + pad of the battery holder? (If yes, does this also work for the battery on the RTC and Adalogger Featherwings?

Well, no. The voltage is wrong. You could use a line from the 3.3V output of the feather.
emkli wrote:3. I saw that there are functions to put the GPS into sleep mode manually and wake it up as well (this might be interesting in combination with the M0 for which i saw code to directly put it into sleep for a certain time). Is this "manual" standby identical to the periodic mode in terms of fix keeping and power save?

I haven't looked into that, but I assume it's the same as periodic mode. The problem is that if the processor is also sleeping, then you have no way to wake up, other than by setting a watchdog timer. The watchdog has a maximum interval of 8 seconds.
emkli wrote:It seems to me that when the LED is on while the GPS goes to sleep, it will stay on during sleep.

Yes, I noticed that, too. I don't know if there's any way around it. I kind of doubt that there is. You can always unsolder the LED.

emkli wrote:However, still nothing on the serial line. Really strange. Maybe I should attach an LED to the RX line to see if there is any data...

Which serial line? If the CPU is waking up, then there's data on the serial line from the GPS. There will be nothing on Serial Monitor after the CPU goes to sleep once.

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Low-power GPS logging - how to do it effectively?

by emkli on Sat Jan 14, 2017 5:05 pm

Thanks for all your advice. Ok, I've thought I saw someone put the VBAT to the GPS battery. Maybe I was wrong then.

adafruit_support_rick wrote:
emkli wrote:However, still nothing on the serial line. Really strange. Maybe I should attach an LED to the RX line to see if there is any data...

Which serial line? If the CPU is waking up, then there's data on the serial line from the GPS. There will be nothing on Serial Monitor after the CPU goes to sleep once.


Problem is: there still IS no wake up at all. I've used your sketch, but it won't work for me. The processor goes to sleep (as it obviously doesn't get any data from the GPS), even if the GPS is "alive" and never wakes up again (because there still is no data). The GPS itself seems to work as it should, with going into sleep mode etc. I think, the wiring is correct (the RX pin of the GPS goes to pin 11 and the TX pin to pin 12) and i tested it again with a multimeter: there IS a connection. No cold solder joints, nothing. However, no data for the 32u4.

EDIT: This is the serial monitor output:

Code: Select all | TOGGLE FULL SIZE
Adafruit GPS library basic test!

Time: 0:0:0.0
Date: 0/0/200
Fix: 0 quality: 0

Time: 0:0:0.0
Date: 0/0/200
Fix: 0 quality: 0


Then the 32u4 goes for a nap. No GPS data at all.

emkli
 
Posts: 43
Joined: Sun Mar 17, 2013 1:37 pm

Please be positive and constructive with your questions and comments.