Serial command handler Improvements Help.

http://robotsbigdata.com/docs-arduino-serial-manager.html

I like the simplicity of how this code works and it is really reliable, the problem is i dont understand enough about the code itself inorder to modify how it works…

in short the code manages serial commands, in the format cmd:val i would like to know how to modify it to work with cmd:val:val:val:msg

i tried over a month ago a few times to get a hold of the original author but to no avail. like i say the code is really reliable for what i need it for anyway but it doesnt pass enough information.

// Arduino RBD Serial Manager Library v1.0.0-alpha.3 - A simple interface for serial communication.
// https://github.com/alextaujenis/RBD_SerialManager
// Copyright 2016 Alex Taujenis
// MIT License

#include <Arduino.h>
#include <RBD_SerialManager.h> // https://github.com/alextaujenis/RBD_SerialManager

namespace RBD {
  SerialManager::SerialManager() {}

  void SerialManager::start() {
    Serial.begin(115200);
  }

  bool SerialManager::onReceive() {
    if(Serial.available()) {
      _char = char(Serial.read());

      if(_char == _flag) {
        _value  = _buffer;
        _buffer = "";
        return true;
      }
      else {
        _buffer += _char;
        return false;
      }
    }
    else {
      return false;
    }
  }

  String SerialManager::getValue() {
    return _value;
  }

  void SerialManager::setFlag(char value) {
    _flag = value;
  }

  void SerialManager::setDelimiter(char value) {
    _delimiter = value;
  }

  String SerialManager::getCmd() {
    _position = getValue().indexOf(_delimiter);

    if(_position > -1) {
      return _value.substring(0, _position);
    }
    else {
      return getValue();
    }
  }

  String SerialManager::getParam() {
    _position = getValue().indexOf(_delimiter);

    if(_position > -1) {
      return _value.substring(_position + 1, _value.length());
    }
    else {
      return "";
    }
  }

  bool SerialManager::isCmd(String value) {
    return getCmd() == value;
  }

  bool SerialManager::isParam(String value) {
    return getParam() == value;
  }
}
// Arduino RBD Serial Manager Library v1.0.0-alpha.3 - A simple interface for serial communication.
// https://github.com/alextaujenis/RBD_SerialManager
// Copyright 2016 Alex Taujenis
// MIT License

#ifndef RBD_SERIAL_MANAGER
#define RBD_SERIAL_MANAGER

#include <Arduino.h>

namespace RBD {
  class SerialManager {
    public:
      SerialManager();
      void start();
      void setFlag(char value);
      void setDelimiter(char value);
      bool onReceive();
      String getValue();
      String getCmd();
      String getParam();
      bool isCmd(String value);
      bool isParam(String value);
      template <typename T> void print(T value){Serial.print(value);}
      template <typename T> void println(T value){Serial.println(value);}
    private:
      int _position;
      char _char;
      char _flag      = '\n'; // you must set the serial monitor to include a newline with each command
      char _delimiter = ':';
      String _buffer  = "";
      String _value   = "";
  };
}

#endif

i have code that i cobbled together that does what i need it to do but it is not as consistent or reliable… ill post below.

i cobbled this together from online examples it works to an extent, but not well enough :

const word numChars = 1024;
char receivedChars[numChars];
char SMsg[200] = {0};
int Commands[8] = {0};

char recvChar;
boolean newData = false;


//example string
//<0087:0002:22:11:33:14:Benson is in the hedges and he isnt hiding.:87>
void setup() {
  Serial.begin(115200);
  Serial.println("<Arduino is ready -->");
}

void loop() {
  recvWithStartEndMarkers();
  showNewData();
  doOtherStuff(); // <<=== NEW  --- This just simulates time spent elsewhere in a big program
}
void recvWithStartEndMarkers1() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  // if (Serial.available() > 0) {
  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'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}
void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  if (Serial.available() > 0) {
    while (Serial.available() > 0 && newData == false) { // <<== NEW - get all bytes from buffer
      rc = Serial.read();

      if (recvInProgress == true) {
        if (rc != endMarker) {
          receivedChars[ndx] = rc;
          ndx++;
          if (ndx >= numChars) {
            ndx = numChars - 1;
          }
        }
        else {
          receivedChars[ndx] = '\0'; // terminate the string
          recvInProgress = false;
          ndx = 0;
          newData = true;
          while (Serial.available() > 0) {
            Serial.read();
          }
        }
      }

      else if (rc == startMarker) {
        recvInProgress = true;
      }
    } // <<=== NEW
  }
}

void showNewData() {
  if (newData == true) {
        if (Commands[6] == 87){
        //Serial.print("This just in ... ");
        //Serial.println(receivedChars);
        parseData();
        showParsedData();
        newData = false;
      }
    }
}

  void parseData() {

    // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(receivedChars, ":"); // this continues where the previous call left off
    Commands[0] = atoi(strtokIndx);     // convert this part to an integer

    for (int x = 1; x <= 5; x++) {
      strtokIndx = strtok(NULL, ":"); // this continues where the previous call left off
      Commands[x] = atoi(strtokIndx);     // convert this part to an integer
    }

    strtokIndx = strtok(NULL, ":");      // get the first part - the string
    strcpy(SMsg, strtokIndx); // copy it to messageFromPC

    strtokIndx = strtok(NULL, ":"); // this continues where the previous call left off
    Commands[6] = atoi(strtokIndx);     // convert this part to an integer
    if (Commands[6] != 87){Serial.println("BadCheckSum");}
  }


  void showParsedData() {
    Serial.print("Message: ");
    Serial.println(SMsg);
    Serial.print("ACK: ");
    Serial.println(Commands[6]);
    Serial.print("Sender: ");
    Serial.println(Commands[0]);
    Serial.print("Route: ");
    Serial.println(Commands[1]);
    Serial.print("Command: ");
    Serial.println(Commands[2]);
    Serial.print("Modifier 1: ");
    Serial.println(Commands[3]);
    Serial.print("Modifier 2: ");
    Serial.println(Commands[4]);
    Serial.print("Modifier 3: ");
    Serial.println(Commands[5]);
  }

  void doOtherStuff() {
    delay(20); // <<===NEW - This just simulates time spent elsewhere in a big program
  }

it works to an extent

Can you please be more precise ?

tbillion: i cobbled this together from online examples it works to an extent, but not well enough :

Why did you modify Robin's recvWithStartEndMarkers code?

I guess this is the problem: Your parser sets commands[6]. The initial value of commands[6] is 0; this is not 87 and hence the parser will never be called from showNewData().

BTW, with a 1024 char buffer and a 200 byte buffer you're getting close to the limits of a 328 based board (Uno, Nano, ...); which board are you using?

UKHeliBob:
Can you please be more precise ?

<Arduino is ready -->

BadCheckSum
Message: Benson is in the hedges and he isnt hidi<0087
ACK: 2
Sender: 87
Route: 2
Command: 22
Modifier 1: 11
Modifier 2: 33
Modifier 3: 14

BadCheckSum
Message: Benson is in the hedges and he isnt hidi<0087
ACK: 2
Sender: 87
Route: 2
Command: 22
Modifier 1: 11
Modifier 2: 33
Modifier 3: 14

BadCheckSum
Message: Benson is in the hedges and he isnt hidi<0087
ACK: 2
Sender: 87
Route: 2
Command: 22
Modifier 1: 11
Modifier 2: 33
Modifier 3: 14

BadCheckSum
Message: Benson is in the hedges and he isnt hidi<0087
ACK: 2
Sender: 87
Route: 2
Command: 22
Modifier 1: 11
Modifier 2: 33
Modifier 3: 14

BadCheckSum
Message: 
ACK: 0
Sender: 7
Route: 0
Command: 0
Modifier 1: 0
Modifier 2: 0
Modifier 3: 0

Message: Benson is in the hedges and he isnt hiding.
ACK: 87
Sender: 87
Route: 2
Command: 22
Modifier 1: 11
Modifier 2: 33
Modifier 3: 14

out of 20 times submitting the same string of information it only got it right one time …

sterretje: Why did you modify Robin's recvWithStartEndMarkers code?

I guess this is the problem: Your parser sets commands[6]. The initial value of commands[6] is 0; this is not 87 and hence the parser will never be called from showNewData().

BTW, with a 1024 char buffer and a 200 byte buffer you're getting close to the limits of a 328 based board (Uno, Nano, ...); which board are you using?

i didnt modify that routine at all Robin did in the serial intro form those "New" parts are his, i only modified the parser. the parser sets commands[6] before it checks it for anything... even if i modify the above mentioned code, I still get serial vomit because it doesnt work right

void showNewData() {
  if (newData == true) {
     parseData();
        if (Commands[6] == 87){
        //Serial.print("This just in ... ");
        //Serial.println(receivedChars);
        //parseData();
        showParsedData();
        newData = false;
      }
    }
}
Sketch uses 2966 bytes (1%) of program storage space. Maximum is 253952 bytes.
Global variables use 1551 bytes (18%) of dynamic memory, leaving 6641 bytes for local variables. Maximum is 8192 bytes.

Robin’s code does not contain the first line in the below

 if (Serial.available() > 0) {
    while (Serial.available() > 0 && newData == false) { // <<== NEW - get all bytes from buffer

Maybe you did not show us your latest code but I based my conclusion on

void showNewData() {
  if (newData == true) {
    if (Commands[6] == 87) {
      //Serial.print("This just in ... ");
      //Serial.println(receivedChars);
      parseData();
      showParsedData();
      newData = false;
    }
  }
}

As commands[6] is set by the parser, it’s highly unlikely that if (Commands[6] == 87) will evaluate to true before the parser is called.

And looking a bit more at your code, don’t use delay to simulate in doOtherStuff(). At 115200 baud you have roughly 10 characters per millisecond. 20ms delay is basically 200 - 64 characters lost. The software buffer used by HardWare Serial is only 64 bytes; once that’s full, other data is lost.

Your other long duration process needs to be chopped into smaller chunks.

sterretje:
Robin’s code does not contain the first line in the below

  if (Serial.available() > 0) {

while (Serial.available() > 0 && newData == false) { // <<== NEW - get all bytes from buffer




Maybe you did not show us your latest code but I based my conclusion on


void showNewData() {
 if (newData == true) {
   if (Commands[6] == 87) {
     //Serial.print("This just in … ");
     //Serial.println(receivedChars);
     parseData();
     showParsedData();
     newData = false;
   }
 }
}



As commands[6] is set by the parser, it's highly unlikely that *if (Commands[6] == 87)* will evaluate to true before the parser is called.

And looking a bit more at your code, don't use delay to simulate in doOtherStuff(). At 115200 baud you have roughly 10 characters per millisecond. 20ms delay is basically 200 characters lost. The software buffer used by HardWare Serial is only 64 bytes; once that's full, other data is lost.

Your other long duration process needs to be chopped into smaller chunks.

how bout we get on the same page real quick and you go read this which IS ROBINS POST… http://forum.arduino.cc/index.php?topic=288234.msg2075585#msg2075585 cuz it kinda just feels a bit like your here to argue, instead of help … i was using the example code purely as it is presented in order to evaluate its effectiveness… IN A TUTORIAL POST BY ROBIN TO SHOW NON BLOCKING SERIAL CODE…

when i posted the code the second time i was showing what i had changed it to which makes it worse. I am not here to get help with the non blocking code. I would like to know how to modify that library to include one more value instead of just a single command and a single value. The more we talk about the sample code that i posted just to show what i had tried outside of that the more off topic we become and the more i fear for the fate of the human race as reading becomes only for leisure…

Your other long duration process needs to be chopped into smaller chunks.

that is another awesome reason why i was looking at the library that i took time out of my day to post both the header file and cpp file is because there are routines in my code that cannot be chopped into smaller blocks or made any faster than they are therefore the library manages the serial better with longer execution times. functions that take between 1 and 5 seconds to execute and that is without dead time…

tbillion:
how bout we get on the same page real quick

How about you read the very start of the Thread in http://forum.arduino.cc/index.php?topic=288234.0 and move on to the updated version of Serial Input Basics which everyone else is using. Then there would be a lot less confusion.

PLUS … take account of what @sterretje said about

As commands[6] is set by the parser, it’s highly unlikely that if (Commands[6] == 87) will evaluate to true before the parser is called.

…R

i read the entire thread.. as stated, that is not code i am trying to use, i tried it and moved on to something else that will work i would just prefer to not have it all hacky like ... idk what happened to this forum in the year i was gone but there are a lot more people on PMS than before. in the last week i have asked 3 concise questions and all people do is troll and argue and get pissy,,, with the exception of the guy who helped me with the progmem thing he was helpful in a normal old school arduino forum way ...

tbillion:

modifying that library to do what you want isn't trivial.

Are you locked into using start and end markers on the Serial messages?

id prefer hardware flow control.. but bluetooth modules dont do that... well hm06 doesnt anyway ... i dont particularily care how it is done, more or less it just cant be blocking, and if it missed part of the message it needs to chuc out the whole message ..

i dont care if its trivial or non trivial, i mean i aint asking for anyone to do it for me, i am tying to get information to understand it , i dont care if that jsut means i get pointed in the right direction.. how hacky it turns out when im done is the only difference.. lmao. i been sitting here playing with it for the last few hours as i have responded to these messages and yeah im not making progress because im not sure what exactly is going on .. ive also looked at the other 20 or so serial wrappers for arduino while ive sat here either they are not generic enough or they are too heavy/ dont work better than this does. ..

Robin2: How about you read the very start of the Thread in http://forum.arduino.cc/index.php?topic=288234.0 and move on to the updated version of Serial Input Basics which everyone else is using. Then there would be a lot less confusion.

PLUS ... take account of what @sterretje said about ...R

as i said to @sterretje with an example of the output from the terminal, with that part of the code, with out it, with it stationed on the moon, results may vary see store for details offers not valid in the UK or Canada, void where prohibited....

tbillion:
id prefer hardware flow control… but bluetooth modules dont do that… well hm06 doesnt anyway … i dont particularily care how it is done, more or less it just cant be blocking, and if it missed part of the message it needs to chuc out the whole message …

how hacky it turns out when im done is the only difference…

so this is a bit of overkill, but it will do what you want to do… which is pop out a message once you receive one…

In the constructor, define which Serial, the maximum message length, the start and end markers (use ‘\0’ or NULL for no start marker, though NULL will generate a compiler warning which you may ignore) and the callback function to handle parsing the message…

#include <SoftwareSerial.h>

#define MAX_SERIAL_PORTS 5 // ToDo: define by device type

class SerialMessenger {
  public:
    SerialMessenger(HardwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, void (*parseFunction)(const char*)) {
      startMkr = startMarker;
      endMkr = endMarker;
      instances[instanceCount++] = this; 
      hwStream = &device; 
      callback = parseFunction;
      bufferSize = maxMessageLength;
      incomingMessage = new char[bufferSize + 1];
    }
    SerialMessenger(SoftwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, void (*parseFunction)(const char*)) {
      startMkr = startMarker;
      endMkr = endMarker;
      instances[instanceCount++] = this; 
      swStream = &device; 
      callback = parseFunction;
      bufferSize = maxMessageLength;
      incomingMessage = new char[bufferSize + 1];
    }
    ~SerialMessenger(){ free(incomingMessage); incomingMessage = NULL;}
    void begin(uint32_t baudRate);
    char* checkForValidMessage(const char startMarker, const char endMarker);
    static void update(void);
    
  private:
    HardwareSerial* hwStream;
    SoftwareSerial* swStream;
    Stream* stream;
    void (*callback)(const char*);
    char* incomingMessage;
    size_t bufferSize;
    size_t idx = 0;
    char startMkr;
    char endMkr;
    static size_t instanceCount;
    static SerialMessenger* instances[MAX_SERIAL_PORTS];
    enum State{
      WAITING,
      PARSING,
    }state = WAITING;
};

size_t SerialMessenger::instanceCount = 0;
SerialMessenger* SerialMessenger::instances[MAX_SERIAL_PORTS] = {nullptr};

void SerialMessenger::update()
{
  for (size_t i = 0; i < instanceCount; i++)
  {
    if (const char* message = instances[i]->checkForValidMessage(instances[i]->startMkr, instances[i]->endMkr))
    {
      instances[i]->callback(message);
    }
  }
}

char* SerialMessenger::checkForValidMessage(const char startMarker, const char endMarker) //, const char checksumToken)
{
  stream = !hwStream? (Stream*)swStream : hwStream;
  if (stream->available())
  {
    if (state == WAITING)
    {
      if(startMkr)
      {
        if (stream->read() == startMarker)
        {
          state = PARSING;
        }
        return nullptr;
      }
      else
      {
        state = PARSING;
      }
    }
    else if (state == PARSING)
    {
      incomingMessage[idx] = stream->read();
      if (incomingMessage[idx] == endMarker)
      {
        incomingMessage[idx] = '\0';
        idx = 0;
        if(startMkr) 
        {
          state = WAITING;
        }
        return incomingMessage;
      }
      else
      {
        idx++;
        if (idx > bufferSize - 1)
        {
          stream->println(F("Error, mssg length"));  //<< Error back to sender
          idx = 0;
          incomingMessage[idx] = '\0';
          if(startMkr)
          {
            state = WAITING;
          }
        }
      } 
    }
  }
  return nullptr;
}

void SerialMessenger::begin(uint32_t baudRate)
{
  if (hwStream)
  {
    hwStream->begin(baudRate);
  }
  else
  {
    swStream->begin(baudRate);
  }
}

/***********************************************************/
/**************** Class Definition Complete ****************/
/***********************************************************/

void parseMssg(const char* mssg);
//void softSerialMssg(const char* mssg);

SoftwareSerial softSerial(4,5);

SerialMessenger serial(Serial, 16, '<', '>', parseMssg);  
//SerialMessenger sw(softSerial, '\0', '\n', softSerialMssg);
//SerialMessenger serial1(Serial1);

void setup()
{
  serial.begin(57600);
  //sw.begin(19200);
  //serial1.begin(57600);
}


void loop()
{
  SerialMessenger::update();
}

void parseMssg(const char* mssg)
{
  Serial.println(mssg);
}

//void softSerialMssg(const char* mssg)
//{
//  Serial.println(mssg);
//}

Test it in your Serial monitor using current configuration and sending:

<Hello World!>

into the Serial Monitor

let me know if that gets you somewhere…

tbillion: i read the entire thread.. as stated, that is not code i am trying to use, i tried it and moved on to something else that will work

id prefer hardware flow control.. but bluetooth modules dont do that... well hm06 doesnt anyway ... i dont particularily care how it is done, more or less it just cant be blocking,

Very strange ... My code works fine for me and lots of other folks here. It works with Bluetooth, it doesn't block and it doesn't miss characters.

as i said to @sterretje with an example of the output from the terminal, with that part of the code, with out it, with it stationed on the moon, results may vary see store for details offers not valid in the UK or Canada, void where prohibited....

Can we all have some of that good stuff you are enjoying?

...R

Robin2:
Very strange …
My code works fine for me and lots of other folks here. It works with Bluetooth, it doesn’t block and it doesn’t miss characters.
Can we all have some of that good stuff you are enjoying?

…R

sarcasm and frustration are sold anywhere you see the arduino logo help yourself, glad to share.

as for your code, i know it works. i have seen it in the examples dozens of times, i probably have more than one arduino that runs it in multiple variations, however, in this instance it wont work, i havent reall delved into the why i was more looking for something that i didnt have to mess with on a 1v1 sort of relationship.

thus the library, in the code i am working on the serial communication plays second fiddle to a bunch of other things. mainly the driving of the matrix display. to scroll a message on that thing really consumes alot of time and that could be why i have issues… However…

when i tested the code from the forum topic all that was in the sketch was the serial code, i tried it as typed at first and it would still miss at times, once i changed the filters it would miss more. that is when i put the qulifier in there and moved it around to several different places before i decided on using some premade library. when the library worked i wanted to know how to expand it so that i dont have to spend 3 minutes processing if i have to use code that will only pass one command and one variable then it becomes a serious tree seeing as i have 32 adc channels, 128 digittal i/O a dht 11, ds 1302, bluetooth and a 20x8 hard wired matrix led.

roughly that would be 200 statements vs maybe a case select and a few functions if the serial would act right, im not trying to make the serial algorithem the primary focus of the sketch even though that is proving more and more what i will need to do and if that is the case i will jsut go back to virtuino that way i dont have to mess with it …

well now any serial code i put in it works, i moved the “checkserial” routine into the heart of the Matrix display code, which had 2 affects, 1 my display is super dim now because the switching time is too fast for the current to saturate, the leds (power saving perk, lmao) which sucks and is ok at the same time because this will be installed in a room with no windows ao you should still be able to see it, and the second is that every thing from virtuino works to control and display all my values. no out of box thinking required … just wish i didn’t have to trade one thing for another. …

I can't make sense of the last 3 paragraphs of Reply #15 or any of Reply #16.

Maybe it is just me ...

...R

Maybe it is just me ...

It isn't.