Teensy LC and 3.1 slow usb serial reads

Status
Not open for further replies.

andydasmith

Active member
Hi, this is my first post, I've been searching all day for an answer to what must be a very simple problem without success so here goes.

I have an open-source arduino project that needs more power so I'm migrating to a Teensy LC.
It involves reading usb serial at 38400 baud from a GPS (five sentences, with around 150 characters in each sentence)
I can do this on the Nano with hardly any overhead so I'm free to read I2C devices and drive a stepper motor at 50 Hz.
When I transfer the code to the Teensy I loose the last two NMEA sentences.
To investigate the problem I threw out all my code and used the code below to run tests:
(its from a well known arduino reference)

const byte numChars = 200;
char receivedChars[numChars]; // an array to store the received data

boolean newData = false;

void setup() {
Serial.begin(38400);
Serial.println("<Arduino is ready>");
}

void loop() {

recvWithEndMarker();
showNewData();
}

void recvWithEndMarker() {
static byte ndx = 0;
char endMarker = '\n';
char rc;

while (Serial.available() > 0 && newData == false) {
rc = Serial.read();

if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
ndx = 0;
newData = true;
}
}
}
long time1;

void showNewData() {
if (newData == true) {
Serial.println(millis() - time1);
time1 = millis();
Serial.print("This just in ... ");
Serial.println(receivedChars);
newData = false;
}
}

In my application (Cotswold Vario sailplane vertical speed indicator) the Teensy or Nano communicates with an LK8000 open-source Flight navigation system which is able to display the serial output.

When this runs on my LC or 3.1s I see times of around 150 milli-sec to read 150 characters and I loose the last two sentences (same as my code).
When I load the same code on a Nano I see times around 15 milli-sec and I see all five sentences at 1 Hz (GPS outputs 5 sentences each second).
I thought this question must've come up a lot but have searched this forum and the web in vain.
I've picked this up from the PJRC site:

On a Teensy, the entire packet, up to 64 bytes, becomes available all at once. Sketches that do other work while receiving data might depend on slow reception behavior, where successive calls to Serial.available() are very unlikely to return true. On a Teensy receiving large amounts of data, it may be necessary to add a variable to count the number of bytes processed and limit the delay before other important work must be done.

Could this be the problem?
 
Well, I'm still stuck. can't believe that I'm the first person to try to read NMEA strings with a teensy. So the question must be so trivial that its not worth answering. I've tried using the work arounds on the PJRC site to avoid receive serial blocking by using the DateTime library to synch with a pc, but the library link loads a library that gives compile errors. I don't have a pc connected to the teensy so this was done in desperation to try to learn if this could really be the problem.
My usbotg host is a Kobo reader running LK8000, it has a serial terminal included
I would really appreciate help from someone who is able to read 200 characters at 38400 over usb in less than 200 milli-sec!
 
Do you have copies of the expected incoming data?

Can you reproduce the problem using only the Arduino Serial Monitor on a PC or Mac, where you send "send" button in the serial monitor to transmit the data.
 
Hi, this is my first post, I've been searching all day for an answer to what must be a very simple problem without success so here goes.

Copy and paste does never help.
The program was so full of bugs - I tried to fix them but, sorry, it was faster to write a new one.
Code:
const int numChars = 200;
char receivedChars[numChars + 1]; // an array to store the received data
long t;
int ndx = 0;
const char endMarker = '\n';

void setup() {
  //Serial.begin(38400); //<- pointless, not needed
  while (!Serial); // wait for PC to init usb-connection
  Serial.println("<Arduino is ready>");
}

void loop() {
  char rc;
  if (Serial.available()) {
    if (ndx == 0) t = micros();
    rc = Serial.read();
    if (rc == endMarker) {
      t = micros() - t;
      Serial.print("Received this:");
      Serial.println(receivedChars);
      Serial.print("in ");
      Serial.print(t);
      Serial.println(" microseconds.");
      ndx = 0;
    }
    else if (ndx < numChars) {
      receivedChars[ndx] = rc;
      receivedChars[ndx + 1] = 0;
      ndx++;
    }
  }
}
I don't say mine is bug-free - but, at least, it works ;)
And yes, the teensys are much faster than a 8bit arduino. I had to use microseconds, because it needs less than 1 millisecond on a T3.2
Try to practice a little more with simpler programs, and take time to find and fix the problems in your version before you claim that Teensys are slow next time.

Don't forget to set the Arduino Monitor to send NL (Newline)

Edit: The previous version stored the newline in the array - i think you don't want that. i've put the new version here.
 
Last edited:
Hi, thanks for the suggestions. I've tried the code and it shows much faster read times but still has the same problem of dropping complete NMEA sentences.
I'm reading 4 sentences of about 65 characters. With the teensy 3.1 or LC I always miss the 3rd sentence. This happens with my original code, the code I included in my post and the code kindly suggested by Frank B.
My first thought was that it must be the serial buffer overflowing in the teensy and there would be a simple fix (64 bytes obviously can't hold my 5 GPS messages). Lots of searching on the internet and in this forum didn't turn anything up on changing the size of the buffer so I asked this question. Subsequent searches have come up with a possible modification of the Wire library for I2c communication (https://forum.pjrc.com/threads/45379-Serial-buffer-size-increase-Teensy-3-2) but I don't think this is relevant in my case.
I'm trying to read 4 sentences with around 65 characters separated by returns (/n) over the usb port transmitted once per second. The code works perfectly on the arduino Nano so I'm trying to find how to configure the teensy so it can do the same job. If I can make it work I can take advantage of the additional capability the teensy offers.
I'm sure I can't be the first person to read GPS data over teensy usb.

$GPRMC,135833.00,A,5128.66840,N,00235.80440,W,0.950,,090220,,,A*66
$GPGGA,135833.00,5128.66840,N,00235.80440,W,1,05,2.51,52.0,M,48.6,M,,*71
$GPGSA,A,3,17,24,19,10,32,,,,,,,,3.93,2.51,3.02*04
$PFLAU,0,1,2,1,0,0,2,0,2147483647*68
$GPRMC,135834.00,A,5128.66856,N,00235.80437,W,0.339,,090220,,,A*63
$GPGGA,135834.00,5128.66856,N,00235.80437,W,1,05,2.51,52.4,M,48.6,M,,*75
$GPGSA,A,3,17,24,19,10,32,,,,,,,,3.93,2.51,3.02*04

3rd sentence $GPGSA is always missing
 
Asking again, can you try just sending a copy of the 5 GPS messages to Teensy using the Arduino Serial Monitor?

The idea is to figure out whether this problem really is something wrong with the code on Teensy, which does the same wrong thing regardless of whether the USB host is your computer or that LK8000 product.

If it works with a PC but not with LK8000, then pouring a lot of effort into buffer sizes and other work on Teensy probably isn't worthwhile. Then the question is what's different between the two hosts, if both send the same data but different things happen when Teensy runs the same code.
 
Can you show the code you are using?

It can't be just echoing the input. Or, what does the LK8000 do with the echoed characters?
 
The messages here are the 4 when the system is at its basic mode
$GPRMC,135833.00,A,5128.66840,N,00235.80440,W,0.950,,090220,,,A*66
$GPGGA,135833.00,5128.66840,N,00235.80440,W,1,05,2.51,52.0,M,48.6,M,,*71
$GPGSA,A,3,17,24,19,10,32,,,,,,,,3.93,2.51,3.02*04
$PFLAU,0,1,2,1,0,0,2,0,2147483647*68
$GPRMC,135834.00,A,5128.66856,N,00235.80437,W,0.339,,090220,,,A*63
$GPGGA,135834.00,5128.66856,N,00235.80437,W,1,05,2.51,52.4,M,48.6,M,,*75
$GPGSA,A,3,17,24,19,10,32,,,,,,,,3.93,2.51,3.02*04

An additional 2 messages are added as the device detects additional targets, again the nano has no problem dealing with these
 
LK8000 receives the NMEA stream and has the capability to pass it through for use by connected usb devices
LK8000 runs on a Kobo linux as host.
I have the teensy and the GPS source connected to two terminal of an MTT usb hub
 
II wrote your GPS data to a textfile, opened a Serial Terminal Program (I used Coolterm) and sent the file to the teensy.
The output is as expected:
Code:
<Arduino is ready>
Received this:$GPRMC,135833.00,A,5128.66840,N,00235.80440,W,0.95 0,,090220,,,A*66
in 244 microseconds.
Received this:$GPGGA,135833.00,5128.66840,N,00235.80440,W,1,05,2 .51,52.0,M,48.6,M,,*71
in 266 microseconds.
Received this:$GPGSA,A,3,17,24,19,10,32,,,,,,,,3.93,2.51,3.02*04
in 182 microseconds.
Received this:$PFLAU,0,1,2,1,0,0,2,0,2147483647*68
in 128 microseconds.
Received this:$GPRMC,135834.00,A,5128.66856,N,00235.80437,W,0.33 9,,090220,,,A*63
in 235 microseconds.
Received this:$GPGGA,135834.00,5128.66856,N,00235.80437,W,1,05,2 .51,52.4,M,48.6,M,,*75
in 260 microseconds.
Received this:$GPGSA,A,3,17,24,19,10,32,,,,,,,,3.93,2.51,3.02*04
in 182 microseconds.
Nothing is missing. So - either your device is not sending what you think - or it has problems displaying the the text (maybe the device is too slow?) - or you modified the sketch.
Again, it would be useful to know *why* you send the gps back? (I suspect you don't and use an *unknown* other Sketch)
 
I've isolated the problem by replacing my teensy code with that suggested by Frank B above. All three code approaches I've tried behave the same way, third sentence is dropped
 
Thanks, I can try compiling a pc version of LK8000 and running that as host in place of the Kobo. I'll let you know how I get on
 
next test: add a delay :
Code:
const int numChars = 200;
char receivedChars[numChars + 1]; // an array to store the received data
long t;
int ndx = 0;
const char endMarker = '\n';

void setup() {
  //Serial.begin(38400); //<- pointless, not needed
  while (!Serial); // wait for PC to init usb-connection
  Serial.println("<Arduino is ready>");
}

void loop() {
  char rc;
  if (Serial.available()) {
    if (ndx == 0) t = micros();
    rc = Serial.read();
    if (rc == endMarker) {
      t = micros() - t;
      Serial.print("Received this:");
      Serial.println(receivedChars);
      Serial.print("in ");
      Serial.print(t);
      Serial.println(" microseconds.");
      ndx = 0;
[COLOR=#ff0000][B]      delay(100);[/B][/COLOR]
    }
    else if (ndx < numChars) {
      receivedChars[ndx] = rc;
      receivedChars[ndx + 1] = 0;
      ndx++;
    }
  }
}
What happens?

(edit: you can verify my result if you just use a terminal program which can send a textfile - you'll see, everything works)
 
That 300 was just to check if the lineending is really "\n" ;-) the output would be a little different, then ;)
Try the delay. I suspect your kobo is too slow. maybe try delay(200) and delay(50) too.
The Teensys can send so fast, that sometimes a PC with Windows has problems to display all..
 
Last edited:
..i've run out of ideas, if the delay does not help.
maybe a bug on the kobo or your software. As you've seen above (Terminal-Software) it's not a Teensy problem.
 
Screenshot:
2020-02-09 16_00_39-Start.png
Nothing is missing.
The data was send with the "send file" function - so, as fast as possible.
 
Sorry, I am unsure from all of the descriptions on where the problem is? Is it the Teensy is not receiving the data or the data output from the Teensy is not seen on the other end?
Also it might help to know which version of Arduino, which version of Teensyduino...

If it were me, I would probably try a few more things. Again from everything I have seen I think it should already work like Frank has it...

But a few things I would try slightly different to see if you get any more hints...
Code:
const int numChars = 200;
char receivedChars[numChars + 1]; // an array to store the received data
long t;
int ndx = 0;
int line_number = 0;
const char endMarker = '\n';

void setup() {
  //Serial.begin(38400); //<- pointless, not needed
  while (!Serial); // wait for PC to init usb-connection
  Serial.println("<Arduino is ready>");
}

void loop() {
  char rc;
  if (Serial.available()) {
    if (ndx == 0) t = micros();
    rc = Serial.read();
    if (rc == endMarker) {
      t = micros() - t;
      line_number++;
      Serial.print(line_number, DEC);
      Serial.print(" Received this:");
      Serial.println(receivedChars);
      Serial.print("in ");
      Serial.print(t);
      Serial.println(" microseconds.");
      ndx = 0;
      //delay(100);
      Serial.flush();  // See if forcing one line out helps? 
    }
    else if (ndx < numChars) {
      receivedChars[ndx] = rc;
      receivedChars[ndx + 1] = 0;
      ndx++;
    }
  }
}

The main changes is that keep a running line number so if you see output line 2 followed by line 4, you know the teensy tried to output line 3 earlier...

Also added Serial.flush(), as to force out each line without a delay...

Again I don't imagine that this would make much of a difference, but that is some of the next steps I would take.
 
Status
Not open for further replies.
Back
Top