Ultimate Feather GPS Real time readout issues

Adafruit Ethernet, Motor, Proto, Wave, Datalogger, GPS Shields - etc!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
Danmorr56
 
Posts: 11
Joined: Thu Jun 01, 2023 2:33 pm

Ultimate Feather GPS Real time readout issues

Post by Danmorr56 »

Hi Folks,

I have my Ultimate GPS board mated with an Adalogger, and program running but GPS data looks corrupted, here is a example of the output
feather output.PNG
feather output.PNG (101.83 KiB) Viewed 703 times
Has anyone encountered this, any help appreciated. Tried resets and baud rate changes...
CODE:

Code: Select all

include <SPI.h>
#include <SD.h>

#define GPSSerial          Serial1
#define GPSSerialEvent     serialEvent1

#define GPS_BAUD_RATE_DEFAULT    115200   // original firmware
#define GPS_BAUD_RATE  115200

// time between GPS readings:
#define  GPS_UPDATE_INTERVAL  1000          // in milliseconds
// suported values: 1000 ms   (1 Hz rate)
//                  10000 ms  (100 mHz rate)
// ATTENTION: SD flush/close takes about 200 ms!!
//            Thus higher rates such as 5 Hz or 10 Hz may not comply.

// time to wait for the GPS to finish sending data:
#define  IDLE_THRESHOLD      10             // milliseconds
// It should be greater than 5 ms, which is 
// the maximum time between GPS serial events (determined experimentally).
// It should be smaller than GPS_UPDATE_INTERVAL, 
// otherwise the buffer might have to hold more than one block of GPS data.

#define  BLINK_LED_IF_OKAY  // give visual indication that writing is okay
//#define WAIT_FOR_VALID_GPS  // if not defined, we'll write to default file.

#define  GPS_BUFFER_SIZE_TYPICAL 512  // typical buffer size, for pre-allocation

#define MAX_BASENAME_LEN 8+1  // FAT limit: 8.3
#define MAX_FILENAME_LEN MAX_BASENAME_LEN-1+1+3+1  // basename-null+dot+ext+null

// check if USB serial port is open before printing:
#define serialPrint if(Serial)Serial.print
#define serialPrintln if(Serial)Serial.println

const int SDCard = 4;  // SD card ID
const char basenameDefault[] = "DEFAULT";
String GPSBuffer = "";   // to hold the incoming GPS data
unsigned long bufferTime = millis();  // timer to indicate when GPS was last read
const char fileDuration = 'D';     // Use 'H' to save by Hour or 'D' to save by Day

// battery settings:
#define BATTERY_UPDATE_INTERVAL  60000      // in milliseconds; zero to disable.
//#define BATTERY_UPDATE_INTERVAL  1000      // in milliseconds; zero to disable.
#define VBATPIN A9  // battery voltage pin
unsigned long batteryTimeEnd = millis();  // timer to indicate when battery was last checked
unsigned long batteryTimeBusy = 0;  // time duration spent busy writing battery

int numBlk=0;  // number of GPS data blocks per file
char basenameOld[MAX_BASENAME_LEN];  // last used basename

// function prototypes:
void configGPS(void);
bool initSD(void);
void datalog(const char basename[]);
void getBasename(char basename[], const char dateTime[], bool GPSActive);
bool getDateTime(char stringOriginal[], char dateTime[]);
void vbatlog(const char basename[], const char dateTime []);
void writeBatteryVoltage(const char basename[], const char dateTime[]);
float readBatteryVoltage (void);
void blinkLED(void);
const char* nth_strchr(const char* s, int c, int n);

void setup()
{
    Serial.begin(115200);

    initSD();
    configGPS();
    delay(1000);
    
    GPSBuffer.reserve(GPS_BUFFER_SIZE_TYPICAL);
    GPSSerial.flush();
}

/* Rationale: GPS keeps sending characters via a serial event;
 * We store the GPS characters in a string (GPSBuffer).
 * When the GPS stops talking, then we log the data to SD card.
 */

void loop()
{
    bool GPSActive;
    char dateTime[6+6+1];  // yymmddHHMMSS\0
    char basename[MAX_BASENAME_LEN];
    unsigned long startTime=millis();

    // time interval elapsed since last GPS serial read:
    unsigned long idleTime = startTime - bufferTime;

    // we will write only when there is no char coming from the GPS:
    if (idleTime < IDLE_THRESHOLD || GPSBuffer.length() < 3) { return; }

    // GPS serial went silent, now log data to SD card.
 
    serialPrintln("[DEBUG] starting.");
    serialPrint(GPSBuffer);

    GPSActive = getDateTime(GPSBuffer.c_str(), dateTime);
    if (!GPSActive)
    {
#ifdef WAIT_FOR_VALID_GPS
        serialPrintln("[DEBUG] waiting for GPS");
        return;
#endif
    }

    getBasename(basename, dateTime, GPSActive);
    
    datalog(basename);

    vbatlog(basename, dateTime);

    // calculate number of blocks per file:
    if (strcmp(basename, basenameOld) != 0)
    {
        strncpy(basenameOld, basename, strlen(basename)+1);
        numBlk=0;
    }
    numBlk++;
    serialPrintln("[DEBUG] number of blocks: " + String(numBlk));

    // clear the buffer and preallocate it:
    GPSBuffer = "";
    GPSBuffer.reserve(GPS_BUFFER_SIZE_TYPICAL);
 
    serialPrintln("[DEBUG] done; time ellapsed: " +
                   String(millis() - startTime) + " ms.\n");
}
    
void vbatlog(const char basename[], const char dateTime [])
{
    if( BATTERY_UPDATE_INTERVAL == 0 ) { return; }
    unsigned long batteryTimeStart = millis();
    unsigned long batteryTimeIdle = batteryTimeStart - batteryTimeEnd;
    unsigned long batteryTimeTol = BATTERY_UPDATE_INTERVAL - batteryTimeBusy*0.9;
    if( batteryTimeIdle < batteryTimeTol ) { return; }
    writeBatteryVoltage(basename, dateTime);
    batteryTimeEnd = millis();
    batteryTimeBusy = batteryTimeEnd - batteryTimeStart;
}

void writeBatteryVoltage (const char basename[], const char dateTime[])
{
    char filename[MAX_FILENAME_LEN];
    strncpy(filename, basename, strlen(basename)+1);
    strcat(filename, ".bat");
    serialPrintln("[DEBUG] filename (battery): " + String(filename));

    File file = SD.open(filename, FILE_WRITE);
    if (!file)
    {
        serialPrintln("[ERROR] Unable to open file '" + String(filename) + "'.");
        return;
    }

    file.print(dateTime);
    file.print("\t");
    file.print(readBatteryVoltage());
    file.println("");

    file.close();
}

float readBatteryVoltage (void)
{
    float measuredvbat = analogRead(VBATPIN);
    measuredvbat *= 2;    // we divided by 2, so multiply back
    measuredvbat *= 3.3;  // Multiply by 3.3V, our reference voltage
    measuredvbat /= 1024; // convert to voltage
    return measuredvbat;
}


// called in between loops, every time a new character appears at GPSSerial
void GPSSerialEvent()
{
    char c = GPSSerial.read();

    // store the caracter in the buffer
    GPSBuffer += c;

    // restart the idle timer count
    bufferTime = millis();
}


// configure the GPS settings
void configGPS(void)
{
    //GPSSerial.begin(GPS_BAUD_RATE);
    configGPSBaudRate();

    switch (GPS_UPDATE_INTERVAL)
    {
        case 100:
            //PMTK_SET_NMEA_UPDATE_10HZ
            GPSSerial.println("$PMTK220,100*2F\r\n");
            break;
        case 1000:
            // PMTK_SET_NMEA_UPDATE_1HZ
            GPSSerial.println("$PMTK220,1000*1F\r\n");
            break;
        case 10000:
            // PMTK_SET_NMEA_UPDATE_100_MILLIHERTZ
            GPSSerial.println("$PMTK220,10000*2F\r\n");
            break;
    }
    
    // GPSSerial.println("$PGCMD,33,1*6C");  // PGCMD_ANTENNA
    GPSSerial.println("$PGCMD,33,0*6D");  // PGCMD_NOANTENNA

    // PMTK_SET_NMEA_OUTPUT_RMCGGAGSV:
    GPSSerial.println("$PMTK314,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0*29");
}

void configGPSBaudRate(void) {
    if (GPS_BAUD_RATE == GPS_BAUD_RATE_DEFAULT) {
        GPSSerial.begin(GPS_BAUD_RATE);
        serialPrintln("[DEBUG] baud" + String(GPS_BAUD_RATE));
        return;
    }
    
    // open GPS at default baud rate (9600) then 
    // change it to the new rate (115200) and reconnect:
    // (if it was already at 115200, it will be ignored.)
    GPSSerial.begin(GPS_BAUD_RATE_DEFAULT);
    switch (GPS_BAUD_RATE)
    {
        case 57600:
            // PMTK_SET_BAUD_57600
            GPSSerial.println("$PMTK251,57600*2C");
            break;
        case 115200:
            // PMTK_SET_BAUD_115200
            GPSSerial.println("$PMTK251,9600*1F");
            break;
    }

    delay(1000);
    GPSSerial.begin(GPS_BAUD_RATE);
    serialPrintln("[DEBUG] baud" + String(GPS_BAUD_RATE));
}

// initialize SD card
bool initSD(void)
{
    // WARNING: SD.begin() can only be called once due to a bug at the
    // Arduino SD.h standard library

    if (!SD.begin(SDCard))
    {
        serialPrintln("[ERROR] Unable to initialized SD card.");
        return false;
    }

    return true;
}

// write data do SD card
void datalog(const char basename[])
{
    char filename[MAX_FILENAME_LEN];
    strncpy(filename, basename, strlen(basename)+1);
    strcat(filename, ".log");
    serialPrintln("[DEBUG] filename (GPS): " + String(filename));
    
    // open the file
    File file = SD.open(filename, FILE_WRITE);
    if (!file)
    {
        serialPrintln("[ERROR] Unable to open file '" + String(filename) + "'.");
        //return;
    }

    // get the size of string to check latter if it has been all written:
    byte len1 = GPSBuffer.length();
    
    // write whole GPS buffer on the SD file at once:
    byte len2 = file.print(GPSBuffer);
    
    // insert a blank line between blocks:
    file.println();

    // closing the file to guarantee integrity:
    // (it costs about 200 ms, same as flush())
    //return true;
    file.close();

    if (len2 == len1) { blinkLED(); }
    String prefix = "[DEBUG]";
    if (len2 != len1) { prefix = "[ERROR]"; }
    serialPrintln(prefix + " " + (100*len2/len1) + "%" + " of " + String(len1) + " bytes written to SD file '" + String(filename) + "'");
}

// blink the light emitting diode:
void blinkLED(void) {
#ifndef BLINK_LED_IF_OKAY
    return;
#endif
    digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(10);              // wait a little
    digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
}

// obtain basename:
void getBasename(char basename[], const char dateTime[], bool GPSActive)
{
    if(!GPSActive)
    {
        // use default basename:
        strncpy(basename, basenameDefault, strlen(basenameDefault)+1);
        return;
    }

    // decide how many characters to copy:
    int num;
    switch (fileDuration)
    {
    case 'H':  // hour
        num = 8;
        break;
    case 'D':  // day
        num = 6;
        break;
    default:
        serialPrintln("[ERROR] Filename duration not recognized; assuming hourly.");
        num = 8;
    }

    // copy date/time (YYMMDDhh) for the basename:
    strncpy(basename, dateTime, num);
    
    // null-terminate string:
    basename[num]='\0';
    
    serialPrintln("[DEBUG] basename: " + String(basename));
}

// fills a string with date and time from the GPS:
bool getDateTime(const char stringOriginal[], char dateTimeOut[])
{
    char  strTemp[82];  // as per <https://en.wikipedia.org/wiki/NMEA_0183>
    char* limInf=NULL;
    char* limSup=NULL;
    const char *dateIn;
    const char *timeIn;
    int len;

    /* here we handle a string like this: 
     *    $GPRMC,144016.000,A,3001.2778,S,05113.2839,W,0.01,37.24,090318,,,A*59
     * then find the date (090318) and time (144016, discarding decimal seconds),
     * and finally reorder it as YYMMDDhhmmss: 180309124016.
    */
    
    if(strlen(stringOriginal)==0)
    {
        serialPrintln("[DEBUG] Empty GPS string.");
        return false;
    }

    // find beginning of $GPRMC sentence:
    limInf=strstr(stringOriginal, "$GPRMC");
    if(!limInf)
    {
        serialPrintln("[DEBUG] $GPRMC not found.");
        return false;
    }
    serialPrint(limInf);

    // verify if GPS is active:
    if((limInf[18]!='A'))
    {
        serialPrintln("[DEBUG] GPS not valid.");
        return false;
    }

    // find end of $GPRMC sentence:
    limSup=strchr(limInf, '\n');
    len=(limSup-limInf)+1;
    strncpy(strTemp, limInf, min(len, sizeof(strTemp)-1));
    strTemp[len]='\0';

    // extract date, reordering components (DDMMYY -> YYMMDD):
    dateIn = nth_strchr(strTemp, ',', 9) + 1;
    dateTimeOut[0] = dateIn[4];  // year
    dateTimeOut[1] = dateIn[5];  // year
    dateTimeOut[2] = dateIn[2];  // month
    dateTimeOut[3] = dateIn[3];  // month
    dateTimeOut[4] = dateIn[0];  // day
    dateTimeOut[5] = dateIn[1];  // day

    // extract time, keeping the order (hhmmss):
    timeIn = &strTemp[7];
    strncpy(&dateTimeOut[6], timeIn, 6);

    // terminate string:
    dateTimeOut[12]='\0';
    serialPrintln("[DEBUG] dateTime: " + String(dateTimeOut));

    return true;
}

// returns a pointer to the nth character in a string:
const char* nth_strchr(const char* s, int c, int n)
{
    int c_count;
    char* nth_ptr;

    for (c_count=1,nth_ptr=strchr(s,c);
         nth_ptr != NULL && c_count < n && c!=0;
         c_count++)
    {
         nth_ptr = strchr(nth_ptr+1, c);
    }

    return nth_ptr;
}
Last edited by adafruit_support_carter on Mon Jun 05, 2023 3:20 pm, edited 1 time in total.
Reason: added [code] tags

User avatar
adafruit_support_carter
 
Posts: 29056
Joined: Tue Nov 29, 2016 2:45 pm

Re: Ultimate Feather GPS Real time readout issues

Post by adafruit_support_carter »

Looks like a baud rate mismatch.

Where did that code originate? There's some logic in configGPSBaudRate() that will attempt to set the GPS baud rate two different ways.

User avatar
Danmorr56
 
Posts: 11
Joined: Thu Jun 01, 2023 2:33 pm

Re: Ultimate Feather GPS Real time readout issues

Post by Danmorr56 »

Hi Carter, this is some code to measure GNSS-R reflectivity, but I even get this output with the Adafruit echo test program
Attachments
Echotest.PNG
Echotest.PNG (105.99 KiB) Viewed 669 times

User avatar
adafruit_support_carter
 
Posts: 29056
Joined: Tue Nov 29, 2016 2:45 pm

Re: Ultimate Feather GPS Real time readout issues

Post by adafruit_support_carter »

Make sure the Arduino Serial Monitor is set to 9600 baud to match the sketch.

Keep in mind there are two baud rates in play. One is between the Arduino board and the GPS module - this is GPSSerial (Serial1) in the code. The other is the one viewed with the Arduino Serial Monitor - this is Serial in the code.

User avatar
Danmorr56
 
Posts: 11
Joined: Thu Jun 01, 2023 2:33 pm

Re: Ultimate Feather GPS Real time readout issues

Post by Danmorr56 »

In this run, have the program set to 9600, and the gps serial also at 9600, monitoring at 9600, still having issues....using echo test program
Attachments
9600 echotest.PNG
9600 echotest.PNG (122.7 KiB) Viewed 651 times

User avatar
adafruit_support_carter
 
Posts: 29056
Joined: Tue Nov 29, 2016 2:45 pm

Re: Ultimate Feather GPS Real time readout issues

Post by adafruit_support_carter »

Just to verify the GPS module being used - it's one of these?
https://www.adafruit.com/product/746

User avatar
Danmorr56
 
Posts: 11
Joined: Thu Jun 01, 2023 2:33 pm

Re: Ultimate Feather GPS Real time readout issues

Post by Danmorr56 »

Hi Team!

It's the feather wing, with headers to an Adalogger
https://www.adafruit.com/product/3133

User avatar
Danmorr56
 
Posts: 11
Joined: Thu Jun 01, 2023 2:33 pm

Re: Ultimate Feather GPS Real time readout issues

Post by Danmorr56 »

Feather GPS with Logger
Attachments
20230609_104309.jpg
20230609_104309.jpg (65.9 KiB) Viewed 642 times

User avatar
adafruit2
 
Posts: 22111
Joined: Fri Mar 11, 2005 7:36 pm

Re: Ultimate Feather GPS Real time readout issues

Post by adafruit2 »

that is a bit odd - what if you set the GPSSerial.begin(9600) to GPSSerial.begin(115200) or 57600 or 19200
also, did you change the GPS firmware or put it into binary mode?

User avatar
Danmorr56
 
Posts: 11
Joined: Thu Jun 01, 2023 2:33 pm

Re: Ultimate Feather GPS Real time readout issues

Post by Danmorr56 »

Hi Team,

I have tried many combinations, and I have not done anything the GPS firmware, just running right out of the package!

User avatar
adafruit2
 
Posts: 22111
Joined: Fri Mar 11, 2005 7:36 pm

Re: Ultimate Feather GPS Real time readout issues

Post by adafruit2 »

we can try replacing it - email support@adafruit for a new GPS Feather - be sure to run the basic echo demo first before installing any other firmware.

User avatar
Danmorr56
 
Posts: 11
Joined: Thu Jun 01, 2023 2:33 pm

Re: Ultimate Feather GPS Real time readout issues

Post by Danmorr56 »

Will do, and will make sure to run echo test first!

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

Return to “Arduino Shields from Adafruit”