Teensy 4.0 serial buffer

Status
Not open for further replies.

Manu

Well-known member
Hello,

I'm currently try to use an Adafruit GPS with tinyGPS++. The sketch run on Arduino 1.8.3 / Teensyduino 1.5.3b2, a Teensy 4.0. I use Serial3 for the GPS.

The problem I have is that if I do anything else than pooling Serial3 in the loop, I miss many GPS frames and it make it unreliable. If I add a simple [delay (100);] in the loop the problem appear. Here is the relevant part of my code :

Code:
#include <EEPROM.h>
#include <SPI.h>
#include <RA8875.h>
#include <FlexCAN_T4.h>
#include <ADC.h>

#include <Encoder.h>
#include <Bounce2.h>

#include <WS2812Serial.h>
#define USE_WS2812SERIAL
#include <FastLED.h>

#include "Pins.h"
#include "Variables_Globales.h"
#include "Signal.h"
#include "Signaux.h"
#include "Capteurs.h"


#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;

// Les instances
Capteurs capteurs;

void setup() {
  Serial.begin(115200);
  while ((!Serial) && (millis() < 750) );
  Serial.println("Serial OK");
  delay(100);
}

void loop() {
  capteurs.refreshGPSData();
  //delay (100);
}


Capteurs.h :
Code:
#ifndef Capteurs_h
#define Capteurs_h

#include "Signaux.h"
#include "Reglages_Generaux.h"
#include "TinyGPS++.h"
#include "Time.h"

#define SECONDS_IN_ONE_HOUR 3600

class Capteurs {
  public:
    void begin();
    void startGPS();
    void refreshGPSData();
    void syncRtcWithGps();
    float getGPSSpeed();
    bool isDaylightSavingTime();
    TinyGPSPlus getGPS();

  private:
    bool rtcSyncWithGps;
};
#endif


Capteurs.cpp
Code:
#include "Capteurs.h"

TinyGPSPlus gps;

bool rtcSyncWithGps = false;

void Capteurs::begin() {
  startGPS();
}

void Capteurs::startGPS() {
  Serial3.begin(9600); // Le GPS est en mode 9600 au démarrage
  delay(10);
  // GPS COM = 38400 baud
  //Serial3.write("$PMTK251,9600*17\r\n");  //set baud rate to 9600
  //Serial3.write("$PMTK251,19200*22\r\n");  //set baud rate to 19200
  Serial3.write("$PMTK251,38400*27\r\n");  //set baud rate to 38400
  //Serial3.write("$PMTK251,57600*2C\r\n");  //set baud rate to 57600
  Serial3.flush();
  delay(10);
  Serial3.end();
  delay(10);
  Serial3.begin(38400);
  // Désactive tous les messages NMEA à l'exception de RMC, VTG, GGA and GSA. GSA est envoyé toutes les 5 transmission.
  Serial3.write("$PMTK314,0,1,1,1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2C\r\n");
  Serial3.flush();
  // Passe le GPS en mode 10Hz.
  Serial3.write("$PMTK220,100*2F\r\n");
  Serial3.flush();
}

void Capteurs::refreshGPSData() {
  //Serial.println ("refresh");

  bool nmeaReceived = false;
  while (!nmeaReceived && (Serial3.available() > 0)) {
    char c = Serial3.read();
    Serial.print(c);
    //nmeaReceived = gps.encode(Serial3.read());
    //if (nmeaReceived == true) Serial.println ("1");
  }

  // If a sentence is received, we can check the checksum, parse it...
  if (gps.location.isUpdated() && gps.time.age() < 500) {
    Signal objet = Signaux::trouveObjetParNom("LAT");
    objet.valeur = gps.location.lat();
    //item.value = ObjetsAffichables::litValeurUtilisateurAPartirValeurBrute(item);
    Signaux::ecritObjet(objet);

    objet = Signaux::trouveObjetParNom("LONG");
    objet.valeur = gps.location.lng();
    //item.value = ObjetsAffichables::litValeurUtilisateurAPartirValeurBrute(item);
    Signaux::ecritObjet(objet);

    objet = Signaux::trouveObjetParNom("ALT");
    objet.valeur = gps.altitude.kilometers();
    //item.value = ObjetsAffichables::litValeurUtilisateurAPartirValeurBrute(item);
    Signaux::ecritObjet(objet);

    objet = Signaux::trouveObjetParNom("VIT");
    objet.valeur = gps.speed.mps();
    //item.value = ObjetsAffichables::litValeurUtilisateurAPartirValeurBrute(item);
    Signaux::ecritObjet(objet);

    objet = Signaux::trouveObjetParNom("SAT");
    objet.valeur = gps.satellites.value();
    //item.value = ObjetsAffichables::litValeurUtilisateurAPartirValeurBrute(item);
    Signaux::ecritObjet(objet);

    //syncRtcWithGps();
  }
}

In Capteurs.cpp, I currently only write on serial (console) what is received on Serial3 (GPS). I get all frames OK if there is nothing else in the loop. If I do anything else in the loop I have bad frames.

I think it's a buffer size problem. Can someone point me to the right direction to solve this problem. I have many more to do in the loop than just parse GPS data ;-)

Thank to any one who will answer.
Manu
 
you have a couple options,

1) serialEvents function that fires from yield() whenever serial data appears

2) modify the core's serial buffer, default 64 bytes, but it restores after a reinstall of teensyduino

3) run serial in teensythreads timeslice, and your other code in loop()

4) using eventresponder
 
Indeed GPS data is just an ongoing spew from the device. Any delay() or failure to check before the buffer overflows will result in missed/corrupted data from GPS.

Calling delay() does call yield() which will call serialEvent3() in the sketch. So if GPS processing can happen on the fly as data arrives until a string completes that may work. yield() is also called after each loop() exit before it re-enters so with a loop() not held up with "work" the default buffer ( typically not big enough for a whole GPS message ) might be big enough without modifying the CORES default RX buffer size.

one quick check might be to put this in the sketch:
Code:
void serialEvent3() {
  refreshGPSData();
}

Generally best to avoid any use of delay() as it is just lost processing time that could be used elsewhere. Using elapsedMillis() or elapsedMicros() to watch the passing of time while handling other tasks is much better.

This was handled in a uNav thread some time back ( updates to 9DOF data took too long ) - not using serialEvent3() - but increasing the buffer (to hold no less than a complete GPS message) and making sure to pull in Serial3 (in this case) bytes from GPS when possible until a message was complete for processing. IIRC message transmission was a few ms at baud rates over 112K or higher - at 38400 it will take even longer so waiting during reading is bad - that seems to be covered in the above code to process incoming bytes and detect a complete message.`
 
You might also do something on T4 like:

Code:
#define EXTRA_BUFFER_SIZE 512
// Put somewhere global...
DMAMEM uint8_t serial3_extra_rx_buffer[EXTRA_BUFFER_SIZE ];

void setup() {
...
Serial3.addStorageForRead(serial3_extra_rx_buffer, sizeof(serial3_extra_rx_buffer));
Serial3.begin(...);
...
This adds a 512 byte buffer to the RX queue... You can always try smaller. Also does not need to be DMAMEM, but probably can be...

Note: the above method is only in the T4.x core HardwareSerial code... I never got it back into T3.x core.
 
Status
Not open for further replies.
Back
Top