Again thank you for the replies. I finally had time to look into this better, and have combined some of your suggestions to build this (there is some u8g2 stuff in there that is not directly needed for using the Serial monitor as output, but more on that later.)
#include <SoftwareSerial.h>
SoftwareSerial GPSModule(10,11); //RX.TX
#include <Arduino.h>
#include <U8g2lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
U8G2_ST7565_ERC12864_ALT_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 2, /* data=*/ 3, /* cs=*/ 4, /* dc=*/ 5, /* reset=*/ 6);
char messageArray[90] = {0};
bool newMessage = false;
bool charLimit = false;
bool timeOut = false;
void setup() {
Serial.begin(115200);
GPSModule.begin(9600);
delay(500);
Serial.println("start");
}
void loop(){
findReadUntil("$GNRMC",90,'\n',100); // Continually run the function: (pattern, char limit, terminating char, timeout in ms)
if(newMessage){
Serial.println("whole message:");
Serial.println(messageArray);
newMessage = false;
}
if(charLimit){
Serial.println("character limit. current messageArray:");
Serial.println(messageArray);
charLimit = false;
}
if(timeOut){
Serial.println("timedOut. current smessageArray:");
Serial.println(messageArray);
timeOut = false;
}
}
void findReadUntil(char pattern[], byte char_lim, char until_c, int timeout_ms){
static bool searching = true;
static bool controlledTimeout = false;
static bool timerRunning = false;
static byte msgIndex = 0;
static byte patternIndex = 0;
static unsigned long timerStart = 0;
if( GPSModule.available()>0 ){
char c = GPSModule.read();
timerStart = millis();
timerRunning = true;
if( searching ){ // are we searching or writing?
msgIndex = 0;
if( c == pattern[patternIndex] ){ // let's see if the character matches the expected pattern
patternIndex++; // if yes, let's continue searching
if( pattern[patternIndex] == '\0'){
searching = false; // found the end of the pattern, no longer searching
}
}else{
patternIndex = 0; // if no, let's start over the search
}
}else{ // we are writing
if( msgIndex < char_lim-1 ){ // have we not reached our character limit yet?
messageArray[msgIndex] = c; // then let's write
msgIndex++;
}else{ // if we reached the limit, we give an error and reset
charLimit = true;
searching = true;
controlledTimeout = true;
timerRunning = false;
patternIndex = 0;
msgIndex = 0;
}
if( c == until_c){ // found terminating char? then let's flag a new message, and start over
newMessage = true;
searching = true;
timerRunning = false;
patternIndex = 0;
msgIndex = 0;
}
}
}else{
if( millis() - timerStart > timeout_ms ){ //over time?
if( controlledTimeout ){ // we need to do a controlled timeout if we reached our limit.
controlledTimeout = false;
timerRunning = false;
}else if( timerRunning ){ // no controlled timeout and timer running? we timed out.
timeOut = true;
searching = true;
timerRunning = false;
patternIndex = 0;
msgIndex = 0;
}
}
}
}
This works well on my separate Arduino when tapping in to the TX stream of the GPS chip. It's also non-blocking, as far as I understand. The output (Serial Monitor) is nicely this:
start
whole message:
,101743.000,A,xxxx.xxxx4,N,0xxxx.xxxx2,E,0.00,79.16,110721,,,A*42
whole message:
,101744.000,A,xxxx.xxxx4,N,0xxxx.xxxx2,E,0.00,79.16,110721,,,A*45
whole message:
,101745.000,A,xxxx.xxxx5,N,0xxxx.xxxx3,E,0.00,79.16,110721,,,A*44
whole message:
,101746.000,A,xxxx.xxxx5,N,0xxxx.xxxx4,E,0.00,79.16,110721,,,A*40
whole message:
,101747.000,A,xxxx.xxxx5,N,0xxxx.xxxx4,E,0.00,79.16,110721,,,A*41
whole message:
,101748.000,A,xxxx.xxxx5,N,0xxxx.xxxx5,E,0.00,79.16,110721,,,A*4F
The device I'm building is not connected directly to computer, but rather runs an LCD screen, using the u8g2 library. Now when I paste the following code in the loop, to run the LCD screen:
if(millis() - prevScreen >= 50){
prevScreen = millis();
u8g2.firstPage();
do {
u8g2.setDrawColor(1);
u8g2.setFont(u8g2_font_u8glib_4_tr);
u8g2.setCursor(100,17);
u8g2.print("hello");
u8g2.setCursor(2,30);
u8g2.print("hello");
u8g2.setCursor(2,35);
u8g2.print("hello");
u8g2.setCursor(20,17);
u8g2.print("hello");
} while (u8g2.nextPage() );
}
The sketch remains functional when the message send to the Arduino is short, but when the message becomes longer (e.g. a whole GNRMC NMEA message, ~80 char max, problems start occurring.
The output is now:
start
timedOut. current messageArray:
,102053.000,A,xxxx.xxx54,N,0xxxx.xxxx5,E,0.00,79.16,⸮
whole message:
,102054.000,A,xxxx.xxx57,N,0xxxx.xxxx4,E,0.00,79.16,110721,,,A*46
timedOut. current messageArray:
,102055.000,A,xxxx.xxx59,N,0xxxx.xxxx3,E,0.00,79.16,11072
whole message:
,102056.000,A,xxxx.xxx61,N,0xxxx.xxxx2,E,0.29,79.16,110721,,,A*4C
timedOut. current messageArray:
,102057.000,A,xxxx.xxx70,N,0xxxx.xxxx9,E,0.00,79.16,11072
timedOut. current messageArray:
,102058.000,A,xxxx.xxx77,N,0xxxx.xxxx7,E,0.00,79.16,110721
timedOut. current messageArray:
,102059.000,A,xxxx.xxx84,N,0xxxx.xxxx6,E,0.00,79.16,110721,,,A*
whole message:
,102100.000,A,xxxx.xxx90,N,0xxxx.xxxx4,E,0.00,79.16,110721,,,A*45
Because these problems arise when I actually start using the LCD screen via the u8g2 library, I assume that something about using the u8g2 library in combination with this new non-blocking function is the problem here. Could the u8g2 library maybe be blocking, causing the execution of the function to be too late at times?
What would be a good solution to this?
Also, I am currently using SoftwareSerial, as I did not realize that the Hardware Serial would be much more efficient when designing this thing. Unfortunately I cannot change the current pcb I have made to use the Hardware Serial pins. But if I were to do this in a future design, would this only decrease the load on the cpu, or are there also other advantages to it? (which could potentially solve the issues described above?)