Based on the discussion from this thread:
Here's a practical example that might be very useful. It creates a class for parsing a Stream
object like Serial
and extracting and acting on commands therein. There are two types of commands, one calls a function with the full string (sans the start and end markers), and the other will update the value in an int
variable.
In this example, if you send "<A>"
or anything in < and > that starts with 'A' then it will call the aFunction
with the string it parsed and print it. If you send 'I', 'J', or 'K' then it will parse the rest of the string with atoi for an int value and update the variable. "<I42>"
will change the value of i
to 42.
I'm going to come back and add some templates so the VariableCommand class will work with any type of variable. I broke this off of the earlier discussion so as not to pollute that thread with discussions of templates.
Example StreamHandler.ino
#include "StreamHandler.h"
// define some variables
int i = 2;
int j = 3;
int k = 4;
//define some functions to print them
void aFunction(char* str) {
Serial.print("\n ** A function - ");
Serial.println(str);
Serial.print(" - prints i - ");
Serial.println(i);
}
void bFunction(char* str) {
Serial.print("\n ** B function - ");
Serial.println(str);
Serial.print(" - prints j - ");
Serial.println(j);
}
void cFunction(char* str) {
Serial.print("\n ** C function - ");
Serial.println(str);
Serial.print(" - prints k - ");
Serial.println(k);
}
// define an array of function commands
// doesn't have to be an array but then they'd all need names
// you could also use new and create them on the heap.
FunctionCommand fcoms[] = {
{ 'A', aFunction },
{ 'B', bFunction },
{ 'C', cFunction }
};
// an array of variable commands
// same as above, doesn't have to be an array but this is easier.
VariableCommand vcoms[] = {
{ 'I', i },
{ 'J', j },
{ 'K', k }
};
// create a StreamHandler and connect to Serial
StreamHandler streamHandler(&Serial);
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n\n**** Starting StreamHandler.ino **** \n\n");
}
void loop() {
streamHandler.run(); // run the stream handler
}
Header StreamHandler.h
:
#ifndef STREAM_HANDLER_H
#define STREAM_HANDLER_H
#include "Arduino.h"
#ifndef STREAM_HANDLER_BUFFER_SIZE
#define STREAM_HANDLER_BUFFER_SIZE 64
#endif
/*
*
* Base class for commands
*
*/
class Command {
private:
char matchChar;
static Command* first;
Command* next = nullptr;
boolean match(char* com) {
return ((*com == matchChar));
}
Command(); // disallow default constructor
protected:
virtual void handle(char* str){};
Command(char c)
: matchChar(c) {
next = first;
first = this;
}
public:
Command(const Command& other) = delete;
Command& operator=(const Command& other) = delete;
~Command() {
for (Command** pptr = &first; *pptr != nullptr; pptr = &((*pptr)->next)) {
if (*pptr == this) {
*pptr = next;
break;
}
}
}
static void check(char* str);
};
/*
*
* Commands that call a function
* function must take a char* and return void
*/
class FunctionCommand : protected Command {
private:
void (*func)(char*);
FunctionCommand(); // disallow default constructor
protected:
virtual void handle(char* str) {
if (func) func(str);
};
public:
FunctionCommand(const FunctionCommand& other) = delete;
FunctionCommand& operator=(const FunctionCommand& other) = delete;
FunctionCommand(char c, void (*f)(char*))
: Command(c), func(f){};
};
/*
*
* Class for updating int variables
*
*/
class VariableCommand : protected Command {
private:
int& var;
VariableCommand(); // disallow default constructor
int parse(char*);
protected:
virtual void handle(char* str) {
var = parse(str);
}
public:
VariableCommand(const VariableCommand& other) = delete;
VariableCommand& operator=(const VariableCommand& other) = delete;
VariableCommand(char c, int& v)
: Command(c), var(v){};
};
/*
*
* SreamHandler class
* handles the actual parsing
*/
class StreamHandler {
private:
char _SPbuffer[STREAM_HANDLER_BUFFER_SIZE];
int index;
Stream* in;
char sop;
char eop;
boolean receiving = false;
boolean greedy = false;
void handleChar(char c);
public:
StreamHandler(Stream* aIn, char aSop, char aEop)
: index(0), in(aIn), sop(aSop), eop(aEop){};
StreamHandler(Stream* aIn)
: index(0), in(aIn), sop('<'), eop('>'){};
void run();
void setGreedy(bool);
bool getGreedy();
};
#endif //STREAM_HANDLER_H
Source StreamHandler.cpp
#include "StreamHandler.h"
Command* Command::first = nullptr; // initialize list head
// static function to check all commands agains the given string
void Command::check(char* str) {
for (Command* ptr = first; ptr != nullptr; ptr = ptr->next) {
// Start markers should be stripped.
if (ptr->match(str)) {
ptr->handle(str);
break;
}
}
}
// parses an integer from the string
int VariableCommand::parse(char* str) {
return atoi(str + 1); // skip command character
}
/*
*
* StreamHandler methods
*
*/
void StreamHandler::run() {
// if (receivingRaw) {
// handleRawData();
// } else
if (in->available()) {
do {
char c = in->read();
handleChar(c);
} while (in->available() && greedy);
}
}
void StreamHandler::handleChar(char c) {
if (c == sop) {
receiving = true;
index = 0;
_SPbuffer[0] = 0;
} else if (receiving) {
if (c == eop) {
receiving = false;
Command::check(_SPbuffer);
}
_SPbuffer[index] = c;
_SPbuffer[++index] = 0;
if (index >= STREAM_HANDLER_BUFFER_SIZE - 1) {
index--;
}
}
}
void StreamHandler::setGreedy(bool aBoo) {
greedy = aBoo;
}
bool StreamHandler::getGreedy() {
return greedy;
}