Has anyone else experienced errant bluetooth characters when sending/receiving?

I have modified Robin2's Serial code for bluetooth serial send and receive, in the function below. At times, I will see btwrite|initGUI=trqe instead of btwrite|initGUI=true and I'm not sure what could cause that. It's unfortunately sporadic and not reproduceable consistently.

The app and the Arduino and the hc05 are all using a baud rate of 9600. The app bluetooth client has character encoding of UTF-8, a delimiter byte of 10 and a polling rate of 10mS.

The serial window will output:

22:13:05.285 -> serialInCharsBT: btwrite|initGUI=trqe

What could be causing a "u" to be interpreted by my function as a "q" ? This serial out snippet is from the bottom of the function recvWithStartEndMarkersBT() , with the line of code that is Serial.print("serialInCharsBT: "); Serial.println(serialInCharsBT); . Does anything in the code stick out as problematic, that could cause this issue? I'll entertain theoretical thoughts if not, I need to overcome this unreliability if so, even if a CRC is the only answer.

(I'm happy to post a more complete sample project if that is helpful, however given that it is communicating with an app on a phone, narrowing it down to the function may be more useful?)

#define BTSerial Serial1

void setup() 
{  
    Serial.begin(9600);
    BTSerial.begin(9600);
    
    while (!Serial)
    {
      ; // Wait for serial to connect
    }

    while (!BTSerial)
    {
      ; // Wait for BTSerial to connect
    }
}
    const byte numCharsBT = 100;
    char receivedCharsBT[numCharsBT];
    bool newDataBT = false;
    void recvWithStartEndMarkersBT() 
    {
        static boolean recvInProgress = false;
        static byte ndx = 0;
        char startMarker = '<';
        char endMarker = '>';
        char rc;

        while (BTSerial.available() > 0 && newDataBT == false) 
        {
                rc = BTSerial.read();
                if (recvInProgress == true) 
                {
                    if (rc != endMarker) {
                        receivedCharsBT[ndx] = rc;
                        ndx++;
                        if (ndx >= numCharsBT) {
                            ndx = numCharsBT - 1;
                        }
                    }
                    else {
                        receivedCharsBT[ndx] = '\0';
                        recvInProgress = false;
                        ndx = 0;
                        newDataBT = true;
                    }
                }
                else if (rc == startMarker) 
                {
                    recvInProgress = true;
                }
        }

        if (newDataBT == true) 
        {
            char serialInCharsBT[strlen(receivedCharsBT)];
            strlcpy(serialInCharsBT, receivedCharsBT, (strlen(receivedCharsBT) + 1));
            Serial.print("serialInCharsBT: "); Serial.println(serialInCharsBT);
            newDataBT = false;

            processSerialIn(serialInCharsBT);
        }

    } // recvWithStartEndMarkersBT

void loop() 
{
    currentTimeStamp = millis();

    recvWithStartEndMarkers();

    recvWithStartEndMarkersBT();
}
#define BTSerial Serial1

void setup()
{
  Serial.begin(9600);
  BTSerial.begin(9600);

  while (!Serial)
  {
    ; // Wait for serial to connect
  }

  while (!BTSerial)
  {
    ; // Wait for BTSerial to connect
  }
}
const byte numCharsBT = 100;
char receivedCharsBT[numCharsBT];

void recvWithStartEndMarkersBT(){
  static boolean recvInProgress = false;
  static byte ndx = 0;
  const char startMarker = '<';
  const char endMarker = '>';

  while (BTSerial.available() > 0 )  {
    char rc = BTSerial.read();
    if (recvInProgress == true)    {
      if (rc != endMarker) {
        receivedCharsBT[ndx] = rc;
        ndx++;
        if (ndx >= numCharsBT)ndx = numCharsBT - 1;
      }
      else {
        receivedCharsBT[ndx] = '\0';
        recvInProgress = false;
        ndx = 0;
        processSerialIn(receivedCharsBT);
      }
    }
    else if (rc == startMarker)recvInProgress = true;
  }

} // recvWithStartEndMarkersBT

void loop() {
  currentTimeStamp = millis();
  recvWithStartEndMarkers();
  recvWithStartEndMarkersBT();
}

Hi, thanks for the reply. For my own knowledge/learning, can you explain why moving the char rc inside of the loop and removing newDataBT and removing the strlcpy will fix my issue, or what is the root cause is? Does the extra code create a timing issue or does char rc need to be reinstantiated each time for reliability?

I updated to your code and I am still getting errant characters. One strange thing I've noticed, although the reproduction is inconsistent, the pattern on the characters that are wrong always has the 6th bit flipped for that byte/character?

13:26:38.048 -> serialInCharsBT: btwrite|v@C=false
13:26:38.087 -> receivedCharsBT: btwrite|v@C=false
13:26:38.160 -> (processSerialIn) serialInChars: btwrite|v@C=false

13:29:14.876 -> (bluetoothWriteCmd) valveDefaultClosed = false
13:29:16.352 -> serialInCharsBT: btwrite|v@C=true
13:29:16.384 -> receivedCharsBT: btwrite|v@C=true
13:29:16.418 -> (processSerialIn) serialInChars: btwrite|v@C=true

13:30:46.666 -> serialInCharsBT: ftwrite|vCC=true
13:30:46.692 -> receivedCharsBT: ftwrite|vCC=true
13:30:46.728 -> (processSerialIn) serialInChars: ftwrite|vCC=true

full code pls, and schematic

It's a Mega2560. I'm using the hardware setup as pictured below. The HC-05 is currently set up for AT+UART=9600,1,0 . I have also tried AT+UART=9600,0,0 . Data is being sent back and forth between over the HC-05 between the 2560 and an Android application.

I'm working on the .ino file for you Kolaha.

Here is a link to the .ino file, once you've downloaded it let me know and I will remove the file. Thank you

you can connect Serial with serial1 and check how messages arrive

//#define analogVin A3          // Analog voltage input to A0
int readAdcBitValue(int analogVin){
  int adcReadBitValue = analogRead(analogVin);

  return adcReadBitValue;
} // readAdcBitValue

really?

No it isn't my goal. This forum stops you from editing your posts after a time period and there are a few people that are trying to do what I'm doing and that's ok as long as they aren't plagiarizing my 100s of hours of work.

The link works for anyone that has the direct link, I hope it's understandable.

That used to also house a float that was converted to a voltage value, that's why it's separate. I will take your point and remove the redundancy lol

#define BTSerial Serial

void setup()
{
  Serial.begin(9600);
  //BTSerial.begin(9600);

  while (!Serial)
  {
    ; // Wait for serial to connect
  }

  //while (!BTSerial)    ; // Wait for BTSerial to connect
  
  //BTSerial.println(1);
  Serial.println(2);
}
const byte numCharsBT = 100;
char receivedCharsBT[numCharsBT];

void recvWithStartEndMarkersBT(){
  static boolean recvInProgress = false;
  static byte ndx = 0;
  const char startMarker = '<';
  const char endMarker = '>';

  while (BTSerial.available() > 0 )  {
    char rc = BTSerial.read();
    if (recvInProgress)    {
      if (rc != endMarker) {
        receivedCharsBT[ndx] = rc;
        ndx++;
        if (ndx >= numCharsBT)ndx = numCharsBT - 1;
      }
      else {
        receivedCharsBT[ndx] = '\0';
        recvInProgress = false;
        ndx = 0;
        BTSerial.println(receivedCharsBT);
      }
    }
    else if (rc == startMarker)recvInProgress = true;
  }

} // recvWithStartEndMarkersBT

void loop() {
  recvWithStartEndMarkersBT();
}

this piece of code works flawless. if it still make errors, try speed 115200 and/or Serial.flush()

Would you like me to send it to you in a private message?

Ok, that's reasonable, here you are





#include <EEPROM.h>
#include <FreqMeasure.h> // uses pin 49 as an rpm/frequency input
#include <Adafruit_MCP4725.h>
//#include <SoftwareSerial.h> // required to use the TX and RX pins of the Mega2560


#define BTSerial Serial1
#define DAC_RESOLUTION    (8)

Adafruit_MCP4725 MCP4725;

#pragma region Troubleshooting

    bool ts = false; // global troubleshooting variable

    void whatCharacterAmI(char chr)
    {
        for (int i = 0; i < 513; i++)
        {
            if (chr == i)
            {
                Serial.print(" *** (whatCharacterAmI) Character match found to digit: "); Serial.print(i); Serial.println(" *** ");
                break;
            }
        } 
    }

#pragma endregion Troubleshooting


#pragma region Variable instantiation

    // uC Input pins

    int id_engineRpm = 49;




    // Variables

    unsigned long currentTimeStamp;
    unsigned long serialLastWriteTimeStamp;
    unsigned long sendBluetoothDataTimeStamp;
    unsigned long getRPMTimeStamp;
    char settingNameArr[100];
    int aODV = 0;   
    bool vOE = true;
    bool vCC = true;
    
 
#pragma endregion Variable instantiation


#pragma region Engine RPM

    unsigned long lastRpmUpdateTime;
    double sum = 0;
    int count = 0;
    int lastRpmUpdateTimeOut = 2000; // equates to zero rpm if more than two seconds elapse without a frequency
    bool engineRunning = false;
    int engineRpm = 0;
    int globalEngineRpm = 0;
    int getRpm()
    {
        //Serial.println("Inside of getRpm()");
        int rpmHz = 0;
        int rpm = 0;
        if (FreqMeasure.available())
        {
            //Serial.print("        Inside of FreqMeasure.available(), count is: "); Serial.println(count);
            sum = sum + FreqMeasure.read();
            count = count + 1;
            if (count > 10)  // when set to 100, at 400rpm, the update was too slow for the timeout period
            {
                rpmHz = FreqMeasure.countToFrequency(sum / count);
                sum = 0;
                count = 0;
                lastRpmUpdateTime = (millis() + lastRpmUpdateTimeOut);
                //Serial.print("getRpm(): "); Serial.println(millis());
                //Serial.print("getRpm() lastRpmUpdateTime: "); Serial.println(lastRpmUpdateTime);
                rpm = (rpmHz * 5);
                //Serial.print("(getRpm()) rpm: "); Serial.println(rpm);
                globalEngineRpm = rpm;
                return rpm;
            }        
        }
        else if ((currentTimeStamp - lastRpmUpdateTimeOut) < 1500)
        {
            Serial.println("(getRpm) Waiting");
        }
        else
        {
            Serial.print(" ** FreqMeasure NOT available, count is: "); Serial.println(count); // USE THIS FOR TROUBLESHOOTING
            lastRpmUpdateTime = 0;
            globalEngineRpm = rpm;
            return rpm;
        }
    }

#pragma endregion Engine RPM


#pragma region Serial data

    boolean newData = false;
    const byte numChars = 100;
    char receivedChars[numChars];
    void recvWithStartEndMarkers() 
    {
        static boolean recvInProgress = false;
        static byte ndx = 0;
        char startMarker = '<';
        char endMarker = '>';
        char rc;
    
        while (Serial.available() > 0 && newData == false) 
        {
            rc = Serial.read();

            if (recvInProgress == true) 
            {
                if (rc != endMarker) {
                    receivedChars[ndx] = rc;
                    ndx++;
                    if (ndx >= numChars) 
                    {
                        ndx = numChars - 1;
                    }
                }
                else 
                {
                    receivedChars[ndx] = '\0';
                    recvInProgress = false;
                    ndx = 0;
                    newData = true;
                }
            }
            else if (rc == startMarker) 
            {
                recvInProgress = true;
            }
        }

        if (newData == true) 
        {
            //Serial.print("receivedChars length: "); Serial.println(strlen(receivedChars));
            char serialInChars[strlen(receivedChars)];
            strlcpy(serialInChars, receivedChars, (strlen(receivedChars) + 1));
            //Serial.print("This just in ... ");
            //Serial.println(receivedChars);
            //Serial.print("serialInChars length: "); Serial.println(strlen(serialInChars));
            //Serial.print("serialInChars: "); Serial.println(serialInChars);
            newData = false;

            processSerialIn(serialInChars);
        }
    } // void recvWithStartEndMarkers

    const byte numCharsBT = 100;
    char receivedCharsBT[numCharsBT];
    bool newDataBT = false;

    void recvWithStartEndMarkersBT()
    {
        static boolean recvInProgress = false;
        static byte ndx = 0;
        const char startMarker = '<';
        const char endMarker = '>';

        while (BTSerial.available() > 0 && newDataBT == false)  
        {
            char rc = BTSerial.read();
            if (recvInProgress == true)    
            {
              if (rc != endMarker) 
              {
                  receivedCharsBT[ndx] = rc;
                  ndx++;
                  if (ndx >= numCharsBT)ndx = numCharsBT - 1;
              }
              else 
              {
                  receivedCharsBT[ndx] = '\0';
                  recvInProgress = false;
                  ndx = 0;
                  if (ts == true) { Serial.print("receivedCharsBT length: "); Serial.println(strlen(receivedCharsBT)); }
                  char serialInCharsBT[strlen(receivedCharsBT)];
                  strlcpy(serialInCharsBT, receivedCharsBT, (strlen(receivedCharsBT) + 1));
                  //Serial.print("This just in ... ");
                  //Serial.println(receivedCharsBT);
                  if (ts == true) { Serial.print("serialInCharsBT length: "); Serial.println(strlen(serialInCharsBT)); }
                  Serial.print("serialInCharsBT: "); Serial.println(serialInCharsBT);
                  Serial.print("receivedCharsBT: "); Serial.println(receivedCharsBT);
                  processSerialIn(receivedCharsBT);
              }
            }
            else if (rc == startMarker)recvInProgress = true;
        }
    }   

    void processSerialIn(char* serialInChars)  // examples: <read=automation> or <write:automation=off>
    {
        Serial.print("     (processSerialIn) serialInChars: "); Serial.println(serialInChars);
        Serial.print("     strlen(serialInChars): "); Serial.println(strlen(serialInChars));

        char settingTempArr[100];
        memset(settingTempArr, '\0', strlen(settingTempArr));
        char settingNameArr[100];
        memset(settingNameArr, '\0', strlen(settingNameArr));
        char settingValueArr[100];
        memset(settingValueArr, '\0', strlen(settingValueArr));
        char temp[100];
        bool containsDelimiter = false;
        bool readCommand = false; // not needed? disabled 6/21/24
        char command[15];
        int commandLength = 0;
        int charIndexer = 0;    

        for(int i = 0; i < (strlen(serialInChars) + 1); i++)
        {
            settingTempArr[charIndexer] = serialInChars[i];
            //Serial.print("initial settingTempArr: "); Serial.println(settingTempArr);
            if (serialInChars[i] == '|')
            {
                settingTempArr[charIndexer + 1] = '\0'; // overwrite the deliniating character
                
                if(strcmp(settingTempArr, "write|") == 0)
                {
                    //writeCommand = true; // not needed? disabled 6/21/24
                    settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                    strcpy(command, settingTempArr);
                    commandLength = strlen(command);
                    memset(settingTempArr, '\0', strlen(settingTempArr));
                    charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                    settingTempArr[100];
                }
                else if(strcmp(settingTempArr, "btwrite|") == 0)
                {
                    //btWriteCommand = true; // not needed? disabled 6/21/24
                    settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                    strcpy(command, settingTempArr);
                    commandLength = strlen(command);
                    memset(settingTempArr, '\0', strlen(settingTempArr));
                    charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                    settingTempArr[100];
                }
                else if(strcmp(settingTempArr, "send|") == 0)
                {
                    //sendCommand = true; // not needed? disabled 6/21/24
                    settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                    strcpy(command, settingTempArr);
                    commandLength = strlen(command);
                    memset(settingTempArr, '\0', strlen(settingTempArr));
                    charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                    settingTempArr[100];
                }
                else if(strcmp(settingTempArr, "btsend|") == 0)
                {
                    //btSendCommand = true; // not needed? disabled 6/21/24
                    settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                    strcpy(command, settingTempArr);
                    commandLength = strlen(command);
                    memset(settingTempArr, '\0', strlen(settingTempArr));
                    charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                    settingTempArr[100];
                }
                else if(strcmp(settingTempArr, "read|") == 0)
                {
                    readCommand = true; // not needed? disabled 6/21/24
                    settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                    strcpy(command, settingTempArr);
                    commandLength = strlen(command);
                    memset(settingTempArr, '\0', strlen(settingTempArr));
                    charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                    settingTempArr[100];
                }
                else if(strcmp(settingTempArr, "btread|") == 0)
                {
                    readCommand = true; // not needed? disabled 6/21/24
                    settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                    strcpy(command, settingTempArr);
                    commandLength = strlen(command);
                    memset(settingTempArr, '\0', strlen(settingTempArr));
                    charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                    settingTempArr[100];
                }
                else
                {
                    Serial.println(" *** Unknown command ***");
                    Serial.println(settingTempArr);
                    Serial.println(serialInChars[i]);
                    whatCharacterAmI(serialInChars[i]);
                }            
            }
            else if (serialInChars[i] == '=')
            {
                settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                strcpy(settingNameArr, settingTempArr);
                int settingNameArrLength = strlen(settingNameArr);
                memset(settingTempArr, '\0', strlen(settingTempArr));
                charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                settingTempArr[100];
            }
            else if (serialInChars[i] == '\f')
            {
                Serial.print("  \ f was found: "); Serial.print(i); Serial.print(" - "); Serial.println(serialInChars[i]);
            }
            else if (serialInChars[i] == '\n')
            {
                Serial.print("  \ n was found: "); Serial.print(i); Serial.print(" - "); Serial.println(serialInChars[i]);
            }
            else if (serialInChars[i] == '\0')
            {
                settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                if (readCommand == true)
                {
                    strcpy(settingNameArr, settingTempArr);
                    int settingNameArrLength = strlen(settingNameArr);
                }
                else
                {
                    strcpy(settingValueArr, settingTempArr);
                    int settingValueArrLength = strlen(settingValueArr);
                }

                memset(settingTempArr, '\0', strlen(settingTempArr));
                charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                settingTempArr[100];
            }
            else
            {
                if (ts == true) { Serial.print(i); Serial.print(" - "); Serial.println(serialInChars[i]); }
            }        

            charIndexer++; // allows a different indexing than the for loop, for when string splits occur
        }

        processSerialCmd(command, settingNameArr, settingValueArr);

    } // void processSerialIn

    void processSerialCmd(char* cmdType, char* cmdName, char* cmdValue)  // examples: <read|automation> or <write|automation=off> or <send|test=true>
    {
        Serial.println("");
        Serial.print("     cmdType:     "); Serial.print(cmdType); Serial.print(", cmdType strlen: "); Serial.println(strlen(cmdType));
        Serial.print("     cmdName:     "); Serial.print(cmdName); Serial.print(", cmdName strlen: "); Serial.println(strlen(cmdName));
        Serial.print("    cmdValue:     "); Serial.print(cmdValue); Serial.print(", cmdValue strlen: "); Serial.println(strlen(cmdValue));
        Serial.println("");
        
        if (strcmp(cmdType,"btsend") == 0)
        {
            bluetoothSendCmd(cmdName, cmdValue);
        }

        if (strcmp(cmdType,"btread") == 0)
        {
            bluetoothReadCmd(cmdName, cmdValue);
        } 

        if (strcmp(cmdType,"btwrite") == 0)
        {
            bluetoothWriteCmd(cmdName, cmdValue);
        } 

    } // void processSerialCmd


    void bluetoothSendCmd(char* btSendCmdName, char* btSendCmdValue)
    {
        BTSerial.print(btSendCmdName); BTSerial.print("="); BTSerial.println(btSendCmdValue);
        Serial.print("    (bluetoothSendCmd) btSendCmdName: "); Serial.print(btSendCmdName); Serial.print(", btSendCmdValue: "); Serial.println(btSendCmdValue);
    } // void bluetoothSendCmd(char* btSendCmdName, char* btSendCmdValue)

    void bluetoothReadCmd(char* btReadCmdName, char* btReadCmdValue) // btReadCmdValue is not currently used
    {
        if (strcmp(btReadCmdName,"vOE") == 0)
        {
            if ((vOE != 0) && (vOE != NULL))
            {
                BTSerial.print("vOE="); BTSerial.println(vOE);
            }

            Serial.print("    (bluetoothReadCmd) btReadCmdName: "); Serial.print(btReadCmdName); Serial.print(", value: "); Serial.println(vOE);
        }
        else if (strcmp(btReadCmdName,"aODV") == 0)
        {
            if ((aODV != 0) && (aODV != NULL))
            {
                BTSerial.print("aODV="); BTSerial.println(aODV);
            }

            Serial.print("    (bluetoothReadCmd) btReadCmdName: "); Serial.print(btReadCmdName); Serial.print(", value: "); Serial.println(aODV);
        }
    } // void bluetoothReadCmd(char* btReadCmdName, char* btReadCmdValue)

    void bluetoothWriteCmd(char* btWriteCmdName, char* btWriteCmdValue)
    {

        if (((strcmp(btWriteCmdName,"initializeGUI") == 0) && (strcmp(btWriteCmdValue,"true") == 0)) || ((strcmp(btWriteCmdName,"initGUI") == 0) && (strcmp(btWriteCmdValue,"true") == 0)))
        {
            initializeGui();
        }
        
        if ((strcmp(btWriteCmdName,"vCC") == 0) && (strcmp(btWriteCmdValue,"true") == 0))
        {
            vCC = true;
            Serial.println("(bluetoothWriteCmd) vCC = true");
        }
        else if ((strcmp(btWriteCmdName,"vCC") == 0) && (strcmp(btWriteCmdValue,"false") == 0))
        {
            vCC = false;
            Serial.println("(bluetoothWriteCmd) vCC = false");
        }
        
    } // void bluetoothWriteCmd(char* btWriteCmdName, char* btWriteCmdValue)

#pragma endregion Serial data


#pragma Bluetooth Send

    void initializeGui()
    {
        BTSerial.print("currentRpm="); BTSerial.println("Init_1234");
        BTSerial.print("manualControlState="); BTSerial.println("Init_disabled");

        Serial.println("    initializeGui() complete");
    } // initializeGui()

    void sendBluetoothData()
    {
            //engineRpm = getRpm(); // needs a timer to throttle data being sent
            if (engineRpm >= 400)
            {
                //BTSerial.print("currentRpm="); BTSerial.println(engineRpm);
            }

            if (globalEngineRpm >= 400)
            {
                BTSerial.print("currentRpm="); BTSerial.println(globalEngineRpm);
            }
            else
            {
                BTSerial.print("currentRpm="); BTSerial.println("0");
            }
        //}
    } // sendBluetoothData()

#pragma endregion Bluetooth Send


void setup() 
{  
    Serial.begin(9600);
    BTSerial.begin(9600);
    
    while (!Serial)
    {
      ; // Wait for serial to connect
    }

    while (!BTSerial)
    {
      ; // Wait for BTSerial to connect
    }

    if (BTSerial)
    {
        Serial.println("BTSerial connected");
    }

    MCP4725.begin(0x60); // could be 0x61 or 0x62

    delay(1000);

  
    serialLastWriteTimeStamp = millis();
    sendBluetoothDataTimeStamp = millis();
    // engine rpm
    FreqMeasure.begin();
    lastRpmUpdateTime = 0;
    engineRpm = 0;


    
    // Input pins

    pinMode(id_engineRpm, INPUT); 
}

void loop() 
{
    currentTimeStamp = millis();

    recvWithStartEndMarkers();

    recvWithStartEndMarkersBT();

    if ((currentTimeStamp - serialLastWriteTimeStamp) > 7000)
    {
        //Serial.print("getExhaustValveAngleADC: "); Serial.println(getExhaustValveAngleADC());
        //engineRpm = getRpm();
        Serial.print("\tglobalEngineRpm: "); Serial.println(globalEngineRpm);
        serialLastWriteTimeStamp = millis();
    }

}


I think I have made the changes you've suggested with this function, I will paste a new version of the code (with the old code commented out for reference) to ensure I understood correctly.

This was eye opening, I was able to remove a lot of code with this. It is for your review below.

I also thought I was using more memory than the size and complexity of my program, but was not sure. Dynamic memory is at 19% usage.

I believe I was able to eliminate all of that, per your strtok suggestion.

The only part I wasn't sure about was when you said " You could be working directly with receivedChars or receivedCharsBT " . Did you mean that I could elimated using char* serialInChars in the function void processSerialIn(char* serialInChars) { ... } ?


#include <EEPROM.h>
#include <FreqMeasure.h> // uses pin 49 as an rpm/frequency input
#include <Adafruit_MCP4725.h>
//#include <SoftwareSerial.h> // required to use the TX and RX pins of the Mega2560


#define BTSerial Serial1
#define DAC_RESOLUTION    (8)

Adafruit_MCP4725 MCP4725;

#pragma region Troubleshooting

    bool ts = false; // global troubleshooting variable

    void whatCharacterAmI(char chr)
    {
        for (int i = 0; i < 513; i++)
        {
            if (chr == i)
            {
                Serial.print(" *** (whatCharacterAmI) Character match found to digit: "); Serial.print(i); Serial.println(" *** ");
                break;
            }
        } 
    }

#pragma endregion Troubleshooting


#pragma region Variable instantiation

    // uC Input pins

    int id_engineRpm = 49;




    // Variables

    unsigned long currentTimeStamp;
    unsigned long serialLastWriteTimeStamp;
    unsigned long sendBluetoothDataTimeStamp;
    unsigned long getRPMTimeStamp;
    //char settingNameArr[100];
    int aODV = 0;   
    bool vOE = true;
    bool vCC = true;
    
 
#pragma endregion Variable instantiation


#pragma region Engine RPM

    unsigned long lastRpmUpdateTime;
    double sum = 0;
    int count = 0;
    int lastRpmUpdateTimeOut = 2000; // equates to zero rpm if more than two seconds elapse without a frequency
    bool engineRunning = false;
    int engineRpm = 0;
    int globalEngineRpm = 0;
    int getRpm()
    {
        //Serial.println("Inside of getRpm()");
        int rpmHz = 0;
        int rpm = 0;
        if (FreqMeasure.available())
        {
            //Serial.print("        Inside of FreqMeasure.available(), count is: "); Serial.println(count);
            sum = sum + FreqMeasure.read();
            count = count + 1;
            if (count > 10)  // when set to 100, at 400rpm, the update was too slow for the timeout period
            {
                rpmHz = FreqMeasure.countToFrequency(sum / count);
                sum = 0;
                count = 0;
                lastRpmUpdateTime = (millis() + lastRpmUpdateTimeOut);
                //Serial.print("getRpm(): "); Serial.println(millis());
                //Serial.print("getRpm() lastRpmUpdateTime: "); Serial.println(lastRpmUpdateTime);
                rpm = (rpmHz * 5);
                //Serial.print("(getRpm()) rpm: "); Serial.println(rpm);
                globalEngineRpm = rpm;
                //return rpm;
            }        
        }
        else if ((currentTimeStamp - lastRpmUpdateTimeOut) < 1500)
        {
            Serial.println("(getRpm) Waiting");
        }
        else
        {
            Serial.print(" ** FreqMeasure NOT available, count is: "); Serial.println(count); // USE THIS FOR TROUBLESHOOTING
            lastRpmUpdateTime = 0;
            globalEngineRpm = rpm;
            
        }

        return rpm;
    }

#pragma endregion Engine RPM


#pragma region Serial data

    boolean newData = false;
    const byte numChars = 100;
    char receivedChars[numChars];
    void recvWithStartEndMarkers() 
    {
        static boolean recvInProgress = false;
        static byte ndx = 0;
        char startMarker = '<';
        char endMarker = '>';
        char rc;
    
        while (Serial.available() > 0 && newData == false) 
        {
            rc = Serial.read();

            if (recvInProgress == true) 
            {
                if (rc != endMarker) {
                    receivedChars[ndx] = rc;
                    ndx++;
                    if (ndx >= numChars) 
                    {
                        ndx = numChars - 1;
                    }
                }
                else 
                {
                    receivedChars[ndx] = '\0';
                    recvInProgress = false;
                    ndx = 0;
                    newData = true;
                }
            }
            else if (rc == startMarker) 
            {
                recvInProgress = true;
            }
        }

        if (newData == true) 
        {
            //Serial.print("receivedChars length: "); Serial.println(strlen(receivedChars));
            char serialInChars[strlen(receivedChars)];
            strlcpy(serialInChars, receivedChars, (strlen(receivedChars) + 1));
            //Serial.print("This just in ... ");
            //Serial.println(receivedChars);
            //Serial.print("serialInChars length: "); Serial.println(strlen(serialInChars));
            //Serial.print("serialInChars: "); Serial.println(serialInChars);
            newData = false;

            processSerialIn(serialInChars);
        }
    } // void recvWithStartEndMarkers

    const byte numCharsBT = 100;
    char receivedCharsBT[numCharsBT];
    bool newDataBT = false;

    void recvWithStartEndMarkersBT()
    {
        static boolean recvInProgress = false;
        static byte ndx = 0;
        const char startMarker = '<';
        const char endMarker = '>';

        while (BTSerial.available() > 0 && newDataBT == false)  
        {
            char rc = BTSerial.read();
            if (recvInProgress == true)    
            {
              if (rc != endMarker) 
              {
                  receivedCharsBT[ndx] = rc;
                  ndx++;
                  if (ndx >= numCharsBT)ndx = numCharsBT - 1;
              }
              else 
              {
                  receivedCharsBT[ndx] = '\0';
                  recvInProgress = false;
                  ndx = 0;
                  if (ts == true) { Serial.print("receivedCharsBT length: "); Serial.println(strlen(receivedCharsBT)); }
                  char serialInCharsBT[strlen(receivedCharsBT)];
                  //strlcpy(serialInCharsBT, receivedCharsBT, (strlen(receivedCharsBT) + 1));
                  strncpy(serialInCharsBT, receivedCharsBT, (strlen(receivedCharsBT) + 1));
                  //Serial.print("This just in ... ");
                  //Serial.println(receivedCharsBT);
                  if (ts == true) { Serial.print("serialInCharsBT length: "); Serial.println(strlen(serialInCharsBT)); }
                  Serial.print("serialInCharsBT: "); Serial.println(serialInCharsBT);
                  Serial.print("receivedCharsBT: "); Serial.println(receivedCharsBT);
                  processSerialIn(receivedCharsBT);
              }
            }
            else if (rc == startMarker)recvInProgress = true;
        }
    }   

    void processSerialIn(char* serialInChars)  // examples: <read=automation> or <write:automation=off>
    {
        Serial.print("     (processSerialIn) serialInChars: "); Serial.println(serialInChars);
        Serial.print("     strlen(serialInChars): "); Serial.println(strlen(serialInChars));

        char *token;
        const char *delimiter ="|=";
        token = strtok(serialInChars, delimiter);        

        char settingCmdArr[100] = {0};
        char settingNameArr[100] = {0};
        char settingValueArr[100] = {0};

        int whileLoopCount = 0;
        while (token != NULL) 
        {
            switch (whileLoopCount) 
            {
                case 0:
                    strncpy(settingCmdArr, token, strlen(token));
                    break;
                case 1:
                    strncpy(settingNameArr, token, strlen(token));
                    break;
                case 2:
                    strncpy(settingValueArr, token, strlen(token));
                    break;
                default:
                  // statements
                    Serial.println(" *** (processSerialIn) while(token != NULL failure) default:");
                    break;
            }

            token = strtok(NULL, delimiter);
            whileLoopCount++;
        }

        if(strcmp(settingCmdArr, "write") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else if(strcmp(settingCmdArr, "btwrite") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else if(strcmp(settingCmdArr, "send") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else if(strcmp(settingCmdArr, "btsend") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else if(strcmp(settingCmdArr, "read") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else if(strcmp(settingCmdArr, "btread") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else
        {
            Serial.println("    *** Unknown command ***");
            Serial.println(settingCmdArr);
            //Serial.println(serialInChars[i]);
            //whatCharacterAmI(serialInChars[i]);
        }

        processSerialCmd(settingCmdArr, settingNameArr, settingValueArr);

    } // void processSerialIn

/*
    void processSerialIn(char* serialInChars)  // examples: <read=automation> or <write:automation=off>
    {
        Serial.print("     (processSerialIn) serialInChars: "); Serial.println(serialInChars);
        Serial.print("     strlen(serialInChars): "); Serial.println(strlen(serialInChars));

        char settingTempArr[100];
        memset(settingTempArr, '\0', strlen(settingTempArr));
        char settingNameArr[100];
        memset(settingNameArr, '\0', strlen(settingNameArr));
        char settingValueArr[100];
        memset(settingValueArr, '\0', strlen(settingValueArr));
        char temp[100];
        bool containsDelimiter = false;
        bool readCommand = false; // not needed? disabled 6/21/24
        char command[15];
        int commandLength = 0;
        int charIndexer = 0;    

        for(int i = 0; i < (strlen(serialInChars) + 1); i++)
        {
            settingTempArr[charIndexer] = serialInChars[i];
            //Serial.print("initial settingTempArr: "); Serial.println(settingTempArr);
            if (serialInChars[i] == '|')
            {
                settingTempArr[charIndexer + 1] = '\0'; // overwrite the deliniating character
                
                if(strcmp(settingTempArr, "write|") == 0)
                {
                    //writeCommand = true; // not needed? disabled 6/21/24
                    settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                    strcpy(command, settingTempArr);
                    commandLength = strlen(command);
                    memset(settingTempArr, '\0', strlen(settingTempArr));
                    charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                    settingTempArr[100];
                }
                else if(strcmp(settingTempArr, "btwrite|") == 0)
                {
                    //btWriteCommand = true; // not needed? disabled 6/21/24
                    settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                    strcpy(command, settingTempArr);
                    commandLength = strlen(command);
                    memset(settingTempArr, '\0', strlen(settingTempArr));
                    charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                    settingTempArr[100];
                }
                else if(strcmp(settingTempArr, "send|") == 0)
                {
                    //sendCommand = true; // not needed? disabled 6/21/24
                    settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                    strcpy(command, settingTempArr);
                    commandLength = strlen(command);
                    memset(settingTempArr, '\0', strlen(settingTempArr));
                    charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                    settingTempArr[100];
                }
                else if(strcmp(settingTempArr, "btsend|") == 0)
                {
                    //btSendCommand = true; // not needed? disabled 6/21/24
                    settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                    strcpy(command, settingTempArr);
                    commandLength = strlen(command);
                    memset(settingTempArr, '\0', strlen(settingTempArr));
                    charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                    settingTempArr[100];
                }
                else if(strcmp(settingTempArr, "read|") == 0)
                {
                    readCommand = true; // not needed? disabled 6/21/24
                    settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                    strcpy(command, settingTempArr);
                    commandLength = strlen(command);
                    memset(settingTempArr, '\0', strlen(settingTempArr));
                    charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                    settingTempArr[100];
                }
                else if(strcmp(settingTempArr, "btread|") == 0)
                {
                    readCommand = true; // not needed? disabled 6/21/24
                    settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                    strcpy(command, settingTempArr);
                    commandLength = strlen(command);
                    memset(settingTempArr, '\0', strlen(settingTempArr));
                    charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                    settingTempArr[100];
                }
                else
                {
                    Serial.println(" *** Unknown command ***");
                    Serial.println(settingTempArr);
                    Serial.println(serialInChars[i]);
                    whatCharacterAmI(serialInChars[i]);
                }            
            }
            else if (serialInChars[i] == '=')
            {
                settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                strcpy(settingNameArr, settingTempArr);
                int settingNameArrLength = strlen(settingNameArr);
                memset(settingTempArr, '\0', strlen(settingTempArr));
                charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                settingTempArr[100];
            }
            else if (serialInChars[i] == '\f')
            {
                Serial.print("  \ f was found: "); Serial.print(i); Serial.print(" - "); Serial.println(serialInChars[i]);
            }
            else if (serialInChars[i] == '\n')
            {
                Serial.print("  \ n was found: "); Serial.print(i); Serial.print(" - "); Serial.println(serialInChars[i]);
            }
            else if (serialInChars[i] == '\0')
            {
                settingTempArr[charIndexer] = '\0'; // overwrite the deliniating character
                if (readCommand == true)
                {
                    strcpy(settingNameArr, settingTempArr);
                    int settingNameArrLength = strlen(settingNameArr);
                }
                else
                {
                    strcpy(settingValueArr, settingTempArr);
                    int settingValueArrLength = strlen(settingValueArr);
                }

                memset(settingTempArr, '\0', strlen(settingTempArr));
                charIndexer = -1; // the charIndexer++ at the bottom of the for loop will put this to 0
                settingTempArr[100];
            }
            else
            {
                if (ts == true) { Serial.print(i); Serial.print(" - "); Serial.println(serialInChars[i]); }
            }        

            charIndexer++; // allows a different indexing than the for loop, for when string splits occur
        }

        processSerialCmd(command, settingNameArr, settingValueArr);

    } // void processSerialIn
*/

    void processSerialCmd(char* cmdType, char* cmdName, char* cmdValue)  // examples: <read|automation> or <write|automation=off> or <send|test=true>
    {
        Serial.println("");
        Serial.print("     cmdType:     "); Serial.print(cmdType); Serial.print(", cmdType strlen: "); Serial.println(strlen(cmdType));
        Serial.print("     cmdName:     "); Serial.print(cmdName); Serial.print(", cmdName strlen: "); Serial.println(strlen(cmdName));
        Serial.print("    cmdValue:     "); Serial.print(cmdValue); Serial.print(", cmdValue strlen: "); Serial.println(strlen(cmdValue));
        Serial.println("");
        
        if (strcmp(cmdType,"btsend") == 0)
        {
            bluetoothSendCmd(cmdName, cmdValue);
        }

        if (strcmp(cmdType,"btread") == 0)
        {
            bluetoothReadCmd(cmdName, cmdValue);
        } 

        if (strcmp(cmdType,"btwrite") == 0)
        {
            bluetoothWriteCmd(cmdName, cmdValue);
        } 

    } // void processSerialCmd


    void bluetoothSendCmd(char* btSendCmdName, char* btSendCmdValue)
    {
        BTSerial.print(btSendCmdName); BTSerial.print("="); BTSerial.println(btSendCmdValue);
        Serial.print("    (bluetoothSendCmd) btSendCmdName: "); Serial.print(btSendCmdName); Serial.print(", btSendCmdValue: "); Serial.println(btSendCmdValue);
    } // void bluetoothSendCmd(char* btSendCmdName, char* btSendCmdValue)

    void bluetoothReadCmd(char* btReadCmdName, char* btReadCmdValue) // btReadCmdValue is not currently used
    {
        if (strcmp(btReadCmdName,"vOE") == 0)
        {
            if ((vOE != 0) && (vOE != NULL))
            {
                BTSerial.print("vOE="); BTSerial.println(vOE);
            }

            Serial.print("    (bluetoothReadCmd) btReadCmdName: "); Serial.print(btReadCmdName); Serial.print(", value: "); Serial.println(vOE);
        }
        else if (strcmp(btReadCmdName,"aODV") == 0)
        {
            if ((aODV != 0) && (aODV != NULL))
            {
                BTSerial.print("aODV="); BTSerial.println(aODV);
            }

            Serial.print("    (bluetoothReadCmd) btReadCmdName: "); Serial.print(btReadCmdName); Serial.print(", value: "); Serial.println(aODV);
        }
    } // void bluetoothReadCmd(char* btReadCmdName, char* btReadCmdValue)

    void bluetoothWriteCmd(char* btWriteCmdName, char* btWriteCmdValue)
    {

        if (((strcmp(btWriteCmdName,"initializeGUI") == 0) && (strcmp(btWriteCmdValue,"true") == 0)) || ((strcmp(btWriteCmdName,"initGUI") == 0) && (strcmp(btWriteCmdValue,"true") == 0)))
        {
            initializeGui();
        }
        
        if ((strcmp(btWriteCmdName,"vCC") == 0) && (strcmp(btWriteCmdValue,"true") == 0))
        {
            vCC = true;
            Serial.println("(bluetoothWriteCmd) vCC = true");
        }
        else if ((strcmp(btWriteCmdName,"vCC") == 0) && (strcmp(btWriteCmdValue,"false") == 0))
        {
            vCC = false;
            Serial.println("(bluetoothWriteCmd) vCC = false");
        }
        
    } // void bluetoothWriteCmd(char* btWriteCmdName, char* btWriteCmdValue)

#pragma endregion Serial data


#pragma region Bluetooth Send

    void initializeGui()
    {
        BTSerial.print("currentRpm="); BTSerial.println("Init_1234");
        BTSerial.print("manualControlState="); BTSerial.println("Init_disabled");

        Serial.println("    initializeGui() complete");
    } // initializeGui()

    void sendBluetoothData()
    {
            //engineRpm = getRpm(); // needs a timer to throttle data being sent
            if (engineRpm >= 400)
            {
                //BTSerial.print("currentRpm="); BTSerial.println(engineRpm);
            }

            if (globalEngineRpm >= 400)
            {
                BTSerial.print("currentRpm="); BTSerial.println(globalEngineRpm);
            }
            else
            {
                BTSerial.print("currentRpm="); BTSerial.println("0");
            }
        //}
    } // sendBluetoothData()

#pragma endregion Bluetooth Send


void setup() 
{  
    Serial.begin(9600);
    BTSerial.begin(9600);
    
    while (!Serial)
    {
      ; // Wait for serial to connect
    }

    while (!BTSerial)
    {
      ; // Wait for BTSerial to connect
    }

    if (BTSerial)
    {
        Serial.println("BTSerial connected");
    }

    MCP4725.begin(0x60); // could be 0x61 or 0x62

    delay(1000);

  
    serialLastWriteTimeStamp = millis();
    sendBluetoothDataTimeStamp = millis();
    // engine rpm
    FreqMeasure.begin();
    lastRpmUpdateTime = 0;
    engineRpm = 0;


    
    // Input pins

    pinMode(id_engineRpm, INPUT);
    
    char* serialInChars = "write|w_name=value";
    processSerialIn(serialInChars);

    serialInChars = "read|r_name=value";
    processSerialIn(serialInChars);

    serialInChars = "send|s_name=value";
    processSerialIn(serialInChars);

    serialInChars = "btwrite|btw_name=value";
    processSerialIn(serialInChars);

    serialInChars = "btread|btr_name=value";
    processSerialIn(serialInChars);

    serialInChars = "btsend|bts_name=value";
    processSerialIn(serialInChars);
}

void loop() 
{
    currentTimeStamp = millis();

    recvWithStartEndMarkers();

    recvWithStartEndMarkersBT();

    if ((currentTimeStamp - serialLastWriteTimeStamp) > 7000)
    {
        //Serial.print("getExhaustValveAngleADC: "); Serial.println(getExhaustValveAngleADC());
        //engineRpm = getRpm();
        //Serial.print("\tglobalEngineRpm: "); Serial.println(globalEngineRpm);
        serialLastWriteTimeStamp = millis();
    }

}

I have to admit, I'm struggling with this.

I tried void processSerialIn(char[] serialInChars) (which didn't work) to try to call the processSerialIn function and then blank out receivedCharsBT inside of recvWithStartEndMarkersBT() after calling the function processSerialIn, and the same for recWithStartEndMarkers().

I think you are saying to do something like this

void recvWithStartEndMarkers()
{
    // do stuff
    processSerialIn();
    receivedChars[100] = {0};
}

void recvWithStartEndMarkersBT()
{
    // do stuff
    processSerialIn();
    receivedCharsBT[100] = {0};
}

However I'm not sure how I can call processSerialIn() without a parameter, if I'm calling processSerialIn from both functions void recvWithStartEndMarkers() and void recvWithStartEndMarkersBT() ? Can you give me a hint as to what to websearch so I can learn?

As an update, I did what I think you were advising, however I created a second function so that I could handle the global variables , so void processSerialIn will handle receivedChars and void processSerialInBT will handle receivedCharsBT .

An interesting thing happened supporting what you speculated. The characters came in correctly via char rc = BTSerial.read(); and printed to the terminal, however even with me initializing the char array with receivedCharsBT[100] = {0};, even though within the same function, there was an extra "b" at the beginning of the command. Should I try and figure out the memory address of each variable?

17:10:45.995 -> globalEngineRpm: 0
17:10:52.993 -> globalEngineRpm: 0
17:10:57.321 -> rc: <
17:10:57.321 -> rc: b
17:10:57.321 -> rc: t
17:10:57.321 -> rc: w
17:10:57.321 -> rc: r
17:10:57.321 -> rc: i
17:10:57.321 -> rc: t
17:10:57.321 -> rc: e
17:10:57.321 -> rc: |
17:10:57.321 -> rc: m
17:10:57.321 -> rc: C
17:10:57.321 -> rc: =
17:10:57.321 -> rc: f
17:10:57.781 -> rc: a
17:10:57.781 -> rc: l
17:10:57.781 -> rc: s
17:10:57.781 -> rc: e
17:10:57.781 -> rc: >
17:10:57.781 -> receivedCharsBT: bbtwrite|mC=false
17:10:57.781 -> (processSerialInBT) receivedCharsBT: bbtwrite|mC=false
17:10:57.781 -> strlen(receivedCharsBT): 17
17:10:57.781 -> *** Unknown command ***
17:10:57.781 -> bbtwrite
17:10:57.781 ->
17:10:57.781 -> cmdType: bbtwrite, cmdType strlen: 8
17:10:57.781 -> cmdName: mC, cmdName strlen: 2
17:10:57.781 -> cmdValue: false, cmdValue strlen: 5
17:10:57.781 ->
17:11:00.648 -> globalEngineRpm: 0
17:11:07.685 -> globalEngineRpm: 0


    const byte numChars = 100;
    char receivedChars[numChars];
    boolean newData = false;

    void recvWithStartEndMarkers() 
    {
        static boolean recvInProgress = false;
        static byte ndx = 0;
        char startMarker = '<';
        char endMarker = '>';
    
        while (Serial.available() > 0 && newData == false) 
        {
            char rc = Serial.read();

            if (recvInProgress == true) 
            {
                if (rc != endMarker) {
                    receivedChars[ndx] = rc;
                    ndx++;
                    if (ndx >= numChars) 
                    {
                        ndx = numChars - 1;
                    }
                }
                else 
                {
                    receivedChars[ndx] = '\0';
                    recvInProgress = false;
                    ndx = 0;
                    if (ts == true) { Serial.print("receivedChars length: "); Serial.println(strlen(receivedChars)); }
                    //char serialInChars[strlen(receivedChars)];
                    //strlcpy(serialInChars, receivedChars, (strlen(receivedChars) + 1));
                    //Serial.print("This just in ... ");
                    //Serial.println(receivedChars);
                    //if (ts == true) { Serial.print("serialInChars length: "); Serial.println(strlen(serialInChars)); }
                    //Serial.print("serialInCharsBT: "); Serial.println(serialInCharsBT);
                    Serial.print("receivedChars: "); Serial.println(receivedChars);
                    processSerialIn();
                }
            }
            else if (rc == startMarker) 
            {
                recvInProgress = true;
            }
        }
        receivedChars[100] = {0};
    } // void recvWithStartEndMarkers


    const byte numCharsBT = 100;
    char receivedCharsBT[numCharsBT];
    bool newDataBT = false;

    void recvWithStartEndMarkersBT()
    {
        static boolean recvInProgress = false;
        static byte ndx = 0;
        const char startMarker = '<';
        const char endMarker = '>';

        while (BTSerial.available() > 0 && newDataBT == false)  
        {
            char rc = BTSerial.read();
            Serial.print("  rc: "); Serial.println(rc);
            if (recvInProgress == true)    
            {
              if (rc != endMarker) 
              {
                  receivedCharsBT[ndx] = rc;
                  ndx++;
                  if (ndx >= numCharsBT)ndx = numCharsBT - 1;
              }
              else 
              {
                  receivedCharsBT[ndx] = '\0';
                  recvInProgress = false;
                  ndx = 0;
                  if (ts == true) { Serial.print("receivedCharsBT length: "); Serial.println(strlen(receivedCharsBT)); }
                  //char serialInCharsBT[strlen(receivedCharsBT)];
                  //strlcpy(serialInCharsBT, receivedCharsBT, (strlen(receivedCharsBT) + 1));
                  //Serial.print("This just in ... ");
                  //Serial.println(receivedCharsBT);
                  //if (ts == true) { Serial.print("serialInCharsBT length: "); Serial.println(strlen(serialInCharsBT)); }
                  //Serial.print("serialInCharsBT: "); Serial.println(serialInCharsBT);
                  Serial.print("receivedCharsBT: "); Serial.println(receivedCharsBT);
                  processSerialInBT();
              }
            }
            else if (rc == startMarker)
            {
                recvInProgress = true;
            }
        }
        receivedCharsBT[100] = {0};
    }

The accompanying functions

    void processSerialIn()
    {
        Serial.print("     (processSerialIn) receivedChars: "); Serial.println(receivedChars);
        Serial.print("     strlen(receivedChars): "); Serial.println(strlen(receivedChars));

        char *token;
        const char *delimiter ="|=";
        token = strtok(receivedChars, delimiter);        

        char settingCmdArr[100] = {0};
        char settingNameArr[100] = {0};
        char settingValueArr[100] = {0};

        int whileLoopCount = 0;
        while (token != NULL) 
        {
            switch (whileLoopCount) 
            {
                case 0:
                    strncpy(settingCmdArr, token, strlen(token));
                    break;
                case 1:
                    strncpy(settingNameArr, token, strlen(token));
                    break;
                case 2:
                    strncpy(settingValueArr, token, strlen(token));
                    break;
                default:
                  // statements
                    Serial.println(" *** (processSerialIn) while(token != NULL failure) default:");
                    break;
            }

            token = strtok(NULL, delimiter);
            whileLoopCount++;
        }

        if(strcmp(settingCmdArr, "write") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else if(strcmp(settingCmdArr, "send") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else if(strcmp(settingCmdArr, "read") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else
        {
            Serial.println("    *** Unknown command ***");
            Serial.println(settingCmdArr);
            //Serial.println(receivedChars[i]);
            //whatCharacterAmI(receivedChars[i]);
        }

        processSerialCmd(settingCmdArr, settingNameArr, settingValueArr);

    } // void processSerialIn

    void processSerialInBT()
    {        
        Serial.print("     (processSerialInBT) receivedCharsBT: "); Serial.println(receivedCharsBT);
        Serial.print("     strlen(receivedCharsBT): "); Serial.println(strlen(receivedCharsBT));

        char *token;
        const char *delimiter ="|=";
        token = strtok(receivedCharsBT, delimiter);        

        char settingCmdArr[100] = {0};
        char settingNameArr[100] = {0};
        char settingValueArr[100] = {0};

        int whileLoopCount = 0;
        while (token != NULL) 
        {
            switch (whileLoopCount) 
            {
                case 0:
                    strncpy(settingCmdArr, token, strlen(token));
                    break;
                case 1:
                    strncpy(settingNameArr, token, strlen(token));
                    break;
                case 2:
                    strncpy(settingValueArr, token, strlen(token));
                    break;
                default:
                  // statements
                    Serial.println(" *** (processSerialInBT) while(token != NULL failure) default:");
                    break;
            }

            token = strtok(NULL, delimiter);
            whileLoopCount++;
        }

        if(strcmp(settingCmdArr, "btwrite") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else if(strcmp(settingCmdArr, "btsend") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else if(strcmp(settingCmdArr, "btread") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else
        {
            Serial.println("    *** Unknown command ***");
            Serial.println(settingCmdArr);
            //Serial.println(receivedCharsBT[i]);
            //whatCharacterAmI(receivedCharsBT[i]);
        }

        processSerialCmd(settingCmdArr, settingNameArr, settingValueArr);

    } // void processSerialInBT

Did you want me to have two separate functions, void processSerialIn() and void processSerialInBT() ?

I've made the adjustments you've pointed out, and now I'm getting empty receivedCharsBT. Could the memset be causing that?

  const byte numCharsBT = 100;
    char receivedCharsBT[numCharsBT];
    bool newDataBT = false;

    void recvWithStartEndMarkersBT()
    {
        static boolean recvInProgress = false;
        static byte ndx = 0;
        const char startMarker = '<';
        const char endMarker = '>';

        while (BTSerial.available() > 0 && newDataBT == false)  
        {
            char rc = BTSerial.read();
            Serial.print("  rc: "); Serial.println(rc);
            if (recvInProgress == true)    
            {
              if (rc != endMarker) 
              {
                  receivedCharsBT[ndx] = rc;
                  ndx++;
                  if (ndx >= numCharsBT)ndx = numCharsBT - 1;
              }
              else 
              {
                  receivedCharsBT[ndx] = '\0';
                  recvInProgress = false;
                  ndx = 0;
                  if (ts == true) { Serial.print("receivedCharsBT length: "); Serial.println(strlen(receivedCharsBT)); }
                  //char serialInCharsBT[strlen(receivedCharsBT)];
                  //strlcpy(serialInCharsBT, receivedCharsBT, (strlen(receivedCharsBT) + 1));
                  //Serial.print("This just in ... ");
                  //Serial.println(receivedCharsBT);
                  //if (ts == true) { Serial.print("serialInCharsBT length: "); Serial.println(strlen(serialInCharsBT)); }
                  //Serial.print("serialInCharsBT: "); Serial.println(serialInCharsBT);
                  Serial.print("receivedCharsBT: "); Serial.println(receivedCharsBT);
                  processSerialInBT();
              }
            }
            else if (rc == startMarker)
            {
                recvInProgress = true;
            }
        }
        
        memset(receivedCharsBT, '\0', numCharsBT);
    }   
    void processSerialInBT()
    {        
        Serial.print("     (processSerialInBT) receivedCharsBT: "); Serial.println(receivedCharsBT);
        Serial.print("     strlen(receivedCharsBT): "); Serial.println(strlen(receivedCharsBT));

        char *token;
        const char *delimiter ="|=";
        token = strtok(receivedCharsBT, delimiter);        

        char settingCmdArr[100] = {0};
        char settingNameArr[100] = {0};
        char settingValueArr[100] = {0};

        int whileLoopCount = 0;
        while (token != NULL) 
        {
            switch (whileLoopCount) 
            {
                case 0:
                    // strncpy(settingCmdArr, token, strlen(token));
                    strcpy(settingCmdArr, token);
                    break;
                case 1:
                    // strncpy(settingNameArr, token, strlen(token));
                    strcpy(settingNameArr, token);
                    break;
                case 2:
                    // strncpy(settingValueArr, token, strlen(token));
                    strcpy(settingValueArr, token);
                    break;
                default:
                  // statements
                    Serial.println(" *** (processSerialInBT) while(token != NULL failure) default:");
                    break;
            }

            token = strtok(NULL, delimiter);
            whileLoopCount++;
        }

        if(strcmp(settingCmdArr, "btwrite") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else if(strcmp(settingCmdArr, "btsend") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else if(strcmp(settingCmdArr, "btread") == 0)
        {
            // I know this is verbose, I will condense this later on after some thought
        }
        else
        {
            Serial.println("    *** Unknown command ***");
            Serial.println(settingCmdArr);
            //Serial.println(receivedCharsBT[i]);
            //whatCharacterAmI(receivedCharsBT[i]);
        }

        processSerialCmd(settingCmdArr, settingNameArr, settingValueArr);

    } // void processSerialInBT

00:42:55.489 -> globalEngineRpm: 0
00:42:55.521 -> rc: <
00:42:55.521 -> rc: b
00:42:55.555 -> rc: t
00:42:55.555 -> rc: w
00:42:55.764 -> rc: r
00:42:55.764 -> rc: i
00:42:55.764 -> rc: t
00:42:55.796 -> rc: e
00:42:55.796 -> rc: |
00:42:55.796 -> rc: m
00:42:55.829 -> rc: C
00:42:55.829 -> rc: =
00:42:55.829 -> rc: t
00:42:55.829 -> rc: r
00:42:55.862 -> rc: u
00:42:55.862 -> rc: e
00:42:55.862 -> rc: >
00:42:55.862 -> receivedCharsBT:
00:42:55.894 -> (processSerialInBT) receivedCharsBT:
00:42:55.927 -> strlen(receivedCharsBT): 0
00:42:55.959 -> *** Unknown command ***
00:42:55.992 ->
00:42:55.992 ->
00:42:56.025 -> cmdType: , cmdType strlen: 0
00:42:56.057 -> cmdName: , cmdName strlen: 0
00:42:56.090 -> cmdValue: , cmdValue strlen: 0
00:42:56.128 ->
00:43:02.808 -> globalEngineRpm: 0

00:44:13.746 -> globalEngineRpm: 0
00:44:14.468 -> rc: <
00:44:14.468 -> rc: b
00:44:14.468 -> rc: t
00:44:14.513 -> rc: w
00:44:14.696 -> rc: r
00:44:14.730 -> rc: i
00:44:14.730 -> rc: t
00:44:14.730 -> rc: e
00:44:14.730 -> rc: |
00:44:14.762 -> rc: m
00:44:14.762 -> rc: C
00:44:14.762 -> rc: =
00:44:14.795 -> rc: t
00:44:14.795 -> rc: r
00:44:14.795 -> rc: u
00:44:14.795 -> rc: e
00:44:14.828 -> rc: >
00:44:14.828 -> receivedCharsBT:
00:44:14.860 -> (processSerialInBT) receivedCharsBT:
00:44:14.893 -> strlen(receivedCharsBT): 0
00:44:14.926 -> *** Unknown command ***
00:44:14.958 ->
00:44:14.958 ->
00:44:14.958 -> cmdType: , cmdType strlen: 0
00:44:14.991 -> cmdName: , cmdName strlen: 0
00:44:15.057 -> cmdValue: , cmdValue strlen: 0
00:44:15.102 ->
00:44:15.755 -> globalEngineRpm: 0

I tried another troubleshooting test where I print the received the incoming test bluetooth character rc and then also print as it builds receivedCharsBT

The third one down looks like it did not erase the memory addresses, but maybe starts to replace the first 7 characters with the same characters? Does that lead towards any clue on what is happening? Or there is two instances of the function competing against itself?

01:05:12.568 -> rc: < -
01:05:12.568 -> rc: b -
01:05:12.568 -> rc: t - b
01:05:12.599 -> rc: w - bt
01:05:12.599 -> rc: r - btw
01:05:12.633 -> rc: i - btwr
01:05:12.633 -> rc: t - btwri
01:05:12.666 -> rc: e - btwrit
01:05:12.666 -> rc: | - btwrite
01:05:12.699 -> rc: m - btwrite|
01:05:12.731 -> rc: C - btwrite|m
01:05:12.731 -> rc: = - btwrite|mC
01:05:12.764 -> rc: f - btwrite|mC=
01:05:12.797 -> rc: a - btwrite|mC=f
01:05:12.797 -> rc: l - btwrite|mC=fa
01:05:12.830 -> rc: s - btwrite|mC=fal
01:05:12.862 -> rc: e - btwrite|mC=fals
01:05:12.895 -> rc: > - btwrite|mC=false
01:05:12.927 -> ndx: 16
01:05:12.927 -> receivedCharsBT: btwrite|mC=false
01:05:12.961 -> (processSerialInBT) receivedCharsBT: btwrite|mC=false
01:05:13.060 -> strlen(receivedCharsBT): 16
01:05:13.061 ->
01:05:13.061 -> cmdType: btwrite, cmdType strlen: 7
01:05:13.125 -> cmdName: mC, cmdName strlen: 2
01:05:13.158 -> cmdValue: false, cmdValue strlen: 5
01:05:13.952 -> rc: < -
01:05:13.952 -> rc: b -
01:05:13.985 -> rc: t - b
01:05:13.985 -> rc: w - bt
01:05:13.985 -> rc: r - btw
01:05:14.020 -> rc: i - btwr
01:05:14.020 -> rc: t - btwri
01:05:14.090 -> rc: e - btwrit
01:05:14.090 -> rc: | - btwrite
01:05:14.124 -> rc: m - btwrite|
01:05:14.124 -> rc: C - btwrite|m
01:05:14.124 -> rc: = - btwrite|mC
01:05:14.157 -> rc: t - btwrite|mC=
01:05:14.194 -> rc: r - btwrite|mC=t
01:05:14.194 -> rc: u - btwrite|mC=tr
01:05:14.226 -> rc: e - btwrite|mC=tru
01:05:14.260 -> rc: > - btwrite|mC=true
01:05:14.292 -> ndx: 15
01:05:14.292 -> receivedCharsBT: btwrite|mC=true
01:05:14.324 -> (processSerialInBT) receivedCharsBT: btwrite|mC=true
01:05:14.393 -> strlen(receivedCharsBT): 15
01:05:14.423 ->
01:05:14.423 -> cmdType: btwrite, cmdType strlen: 7
01:05:14.490 -> cmdName: mC, cmdName strlen: 2
01:05:14.523 -> cmdValue: true, cmdValue strlen: 4
01:05:14.622 -> rc: < - btwrite
01:05:14.653 -> rc: b - btwrite
01:05:14.653 -> rc: t - btwrite
01:05:14.689 -> rc: w - btwrite
01:05:14.720 -> rc: r - btwrite
01:05:14.754 -> rc: i - btwrite
01:05:14.754 -> rc: t - btwrite
01:05:14.790 -> rc: e - btwrite
01:05:14.790 -> rc: | - btwrite
01:05:14.790 -> rc: m - btwrite|mC
01:05:14.823 -> rc: C - btwrite|mC
01:05:14.858 -> rc: = - btwrite|mC
01:05:14.858 -> rc: f - btwrite|mC=true
01:05:14.891 -> rc: a - btwrite|mC=frue
01:05:14.925 -> rc: l - btwrite|mC=faue
01:05:14.962 -> rc: s - btwrite|mC=fale
01:05:15.039 -> rc: e - btwrite|mC=fals
01:05:15.039 -> rc: > - btwrite|mC=false
01:05:15.039 -> ndx: 16
01:05:15.077 -> receivedCharsBT: btwrite|mC=false
01:05:15.218 -> (processSerialInBT) receivedCharsBT: btwrite|mC=false
01:05:15.218 -> strlen(receivedCharsBT): 16
01:05:15.288 ->
01:05:15.288 -> cmdType: btwrite, cmdType strlen: 7
01:05:15.389 -> cmdName: mC, cmdName strlen: 2
01:05:15.389 -> cmdValue: false, cmdValue strlen: 5
01:05:15.389 ->
01:08:10.585 -> globalEngineRpm: 0
01:08:39.083 -> rc: < -
01:08:39.116 -> rc: b -
01:08:39.116 -> rc: t - b
01:08:39.116 -> rc: w - bt
01:08:39.149 -> rc: r - btw
01:08:39.149 -> rc: i - btwr
01:08:39.182 -> rc: t - btwri
01:08:39.182 -> rc: e - btwrit
01:08:39.217 -> rc: | - btwrite
01:08:39.249 -> rc: m - btwrite|
01:08:39.249 -> rc: C - btwrite|m
01:08:39.284 -> rc: = - btwrite|mC
01:08:39.328 -> rc: t - btwrite|mC=
01:08:39.366 -> rc: r - btwrite|mC=t
01:08:39.366 -> rc: u - btwrite|mC=tr
01:08:39.366 -> rc: e - btwrite|mC=tru
01:08:39.406 -> rc: > - btwrite|mC=true
01:08:39.440 -> ndx: 15

Ok, I understand what you mean now. I thought putting it outside of the loop was the way to handle it when it was finishing receiving data, however I didn't consider that it was being called every time the main loop was cycling.

I ran the test 101 times and all 101 times it was accurate. I'm going to continue to test and ensure it stays consistent, but that is promising, I appreciate your critique and I've learned a lot here.

Do you think anything else needs addressing? I'd like to put the serial and bluetooth code somewhere for others if it is acceptable, and hopefully save someone else from the mistakes I made.