If anyone is interested, here is my final demonstration code for capturing, parsing, and displaying multiple string message formats using Class-based OOP. Here I demo it with one port and alternately capture one message string type and then the other. If one were using a chip with multiple serial ports, the input of each string type could be assigned to a dedicated port and share the code.
THANK YOU TO ALL WHO HELPED ME GET ON TRACK HERE!!
/*
Demonstration of serial input string handler using Classes to
allow for capture and parsing of different formats or serial messages
This is based on the non-Class examples in the (classy nonetheless) tutorials by J Haskell found at:
http://jhaskellsblog.blogspot.com/2011/05/serial-comm-fundamentals-on-arduino.html and
http://jhaskellsblog.blogspot.com/2011/06/parsing-quick-guide-to-strtok-there-are.html
Here GPS and IMU strings are handled. "Parsing" here is just the breaking out and printing of string fields.
Further usage of the data could be done with additional coding to capture the values as for use later as intergers, floats, char*, etc
The GPS module might be this one: http://www.sparkfun.com/products/465
The IMU might be this one: http://www.sparkfun.com/products/9623
Here are some test strings you can copy and paste into the Serial Monitor
NOTE: Be sure to set the Serial Monitor to add either CR or both NL/CR
$GPGGA,161229.487,3723.2475,N,12158.3416,W,1,07,1.0,9.0,M,,,,0000*18
$GPGLL,3723.2475,N,12158.3416,W,161229.487,A*2C
$GPGSA,A,3,07,02,26,27,09,04,15,,,,,,1.8,1.0,1.5*33
!ANG:320,33,191
!ANG:0,320,90
!ANG:0,0,0
While this example uses only one serial port, one could assign GPS input to one port and IMU input to another and add a member variable
to the Messages class for the port to use (eg: byte m_inputPort; where, for example, GPS input is found serial port 1 and IMU input is
found on serial port 2) and then use different ports in the getString method based on the passing of m_inputPort. By keeping all variables (
except incomingbyte as member variables, the code is reentrant in that multiple strings can be captured, each from a separate port.
The Messages Class is set-up here to accept one, two, or three field delimiters in the constructor for each instance. Here we use two for
the GPS and three for the IMU strings.
// Copyright (c) 2020 by Peter Dubler
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License
// may be found here: <http://www.gnu.org/licenses/>.
*/
class Messages {
public:
uint8_t m_dataBufferSize;
private:
char m_startChar;
char m_endChar;
char m_delimiters[3] {0, 0, 0}; // set default values to null character
char* m_dataBuffer; // if you want to access the dataBuffer (as GPS.m_dataBuffer for example)with non-class functions, change this to public:
// m_dataBuffer is "seeded" with a maximum-sized (for the string to be captured) string literal in the constructor
// this assures stability of the data fields.
bool m_storeString;
uint8_t m_dataBufferIndex;
public:
// here we will create overloads of the constructor specifications for one, two, or three field delimiters
// single delimiter instance
Messages(uint8_t DBS, char SC, char EC, char DL0, char* DB)
{
m_dataBufferSize = DBS;
m_startChar = SC;
m_endChar = EC;
m_delimiters[0] = DL0;
m_dataBuffer = DB; // DB should be a string literal "abc..." the length of DBS
}
// two delimter instance
Messages(uint8_t DBS, char SC, char EC, char DL0, char DL1, char* DB)
{
m_dataBufferSize = DBS;
m_startChar = SC;
m_endChar = EC;
m_delimiters[0] = DL0;
m_delimiters[1] = DL1;
m_dataBuffer = DB; // DB should be a string literal "abc..." the length of DBS
}
// three delimter instance
Messages(uint8_t DBS, char SC, char EC, char DL0, char DL1, char DL2, char* DB)
{
m_dataBufferSize = DBS;
m_startChar = SC;
m_endChar = EC;
m_delimiters[0] = DL0;
m_delimiters[1] = DL1;
m_delimiters[2] = DL2;
m_dataBuffer = DB; // DB should be a string literal "abc..." the length of DBS
}
bool getSerialString() { // Captures serial input starting with m_startChar and ending with m_endChar, upto a length of m_dataBufferSize.
// Returns true when full string has been captured.
//static byte dataBufferIndex = 0;
while (Serial.available() > 0) {
char incomingbyte = Serial.read();
if (incomingbyte == m_startChar) {
m_dataBufferIndex = 0; //Initialize our dataBufferIndex variable
m_storeString = true;
}
if (m_storeString) {
//Let's check our index here, and abort if we're outside our buffer size
if (m_dataBufferIndex == m_dataBufferSize) {
//Oops, our index is pointing to an array element outside our buffer.
m_dataBufferIndex = 0;
m_storeString = false; //reset flag so that next time through method will check for m_startChar
break;
}
if (incomingbyte == m_endChar) {
m_dataBuffer[m_dataBufferIndex] = 0; //null terminate the C string
m_storeString = false; //reset flag so that next time through method will check for m_startChar
//Our data string is complete. return true
return true;
}
else {
m_dataBuffer[m_dataBufferIndex++] = incomingbyte;
m_dataBuffer[m_dataBufferIndex] = 0; //null terminate the C string
}
}
else {
}
}
//We've read in all the available Serial data, and don't have a valid string yet, so return false
return false;
}
void parseString() {
char* valPosition;
//This initializes strtok with our string to tokenize
valPosition = strtok(m_dataBuffer, m_delimiters);
while (valPosition != NULL) {
Serial.print(valPosition); Serial.print(" ");
//Here we pass in a NULL value, which tells strtok to continue working with the previous string
valPosition = strtok(NULL, m_delimiters);
}
Serial.println('\n');//Serial.println();
}
};
#define GPSbufferSeed "12345678901234567890123456789012345678901234567890123456789012345678901234567890" // 80 character string literal
#define IMUbufferSeed "123456789012345678" // 18 character string literal
//Invoke GPS and IMU instances
//Messages InstanceName(int DBS, char SC, char EC, char DL1,(DL2),(DL3), char * bufferSeed) )
Messages GPS{80, '$', '\r', ',', '$', GPSbufferSeed};
Messages IMU{18, '!', '\r', ':', ',', '!', IMUbufferSeed};
void setup() {
Serial.begin(115200);
Serial.println("Serial port activated");
delay(200);
}
void loop() { // Ask for, receive, parse, and display parsed GPS string, then IMU string, and repeat
Serial.println("Waiting for GPS string");
bool gotString = false;
while (!gotString) {
if (GPS.getSerialString()) {
Serial.print("got GPS string: ");
GPS.parseString();
gotString = true;
}
}
Serial.println("Waiting for IMU string");
gotString = false;
while (!gotString) {
if (IMU.getSerialString()) {
Serial.print("got IMU string: ");
IMU.parseString();
gotString = true;
}
}
}