diff --git a/CVReader.ino b/CVReader.ino index 7979c12..9b2480d 100644 --- a/CVReader.ino +++ b/CVReader.ino @@ -17,6 +17,9 @@ void myCallback(int result) { DIAG(F("\n getting Loco Id callback result=%d"),result); } +DCCEXParser serialParser(Serial);; +DCCEXParser wifiParser(Serial1); + void setup() { Serial.begin(115200); DCC::begin(); @@ -31,5 +34,6 @@ void loop() { DCC::loop(); // required to keep locos running and check powwer // This line passes input on Serial to the DCCEXParser - StringParser::loop(Serial, DCCEXParser::parse); + serialParser.loop(); + wifiParser.loop(); } diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 33d1f9d..41c57f3 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1,4 +1,4 @@ -#include "StringParser.h" +#include "StringFormatter.h" #include "DCCEXParser.h" #include "DCC.h" #include "DCCWaveform.h" @@ -10,19 +10,92 @@ const char VERSION[]="99.666"; -// This is a JMRI command parser +int DCCEXParser::stashP[MAX_PARAMS]; + bool DCCEXParser::stashBusy; + Stream & DCCEXParser::stashStream=Serial; // keep compiler happy but ovevride in constructor + + +DCCEXParser::DCCEXParser(Stream & myStream) { + stream=myStream; +} + +// This is a JMRI command parser, one instance per incoming stream // It doesnt know how the string got here, nor how it gets back. // It knows nothing about hardware or tracks... it just parses strings and // calls the corresponding DCC api. // Non-DCC things like turnouts, pins and sensors are handled in additional JMRI interface classes. +void DCCEXParser::loop() { + while(stream.available()) { + if (bufferLength==MAX_BUFFER) { + bufferLength=0; + inCommandPayload=false; + } + char ch = stream.read(); + if (ch == '<') { + inCommandPayload = true; + bufferLength=0; + buffer[0]='\0'; + } + else if (ch == '>') { + buffer[bufferLength]='\0'; + parse(buffer); + inCommandPayload = false; + break; + } else if(inCommandPayload) { + buffer[bufferLength++]= ch; + } + } + } + + int DCCEXParser::splitValues( int result[MAX_PARAMS]) { + byte state=1; + byte parameterCount=0; + int runningValue=0; + const char * remainingCmd=buffer+1; // skips the opcode + bool signNegative=false; + + // clear all parameters in case not enough found + for (int i=0;i') return parameterCount; + state=2; + continue; + + case 2: // checking sign + signNegative=false; + runningValue=0; + state=3; + if (hot!='-') continue; + signNegative=true; + break; + case 3: // building a parameter + if (hot>='0' && hot<='9') { + runningValue=10*runningValue+(hot-'0'); + break; + } + result[parameterCount] = runningValue * (signNegative ?-1:1); + parameterCount++; + state=1; + continue; + } + remainingCmd++; + } + return parameterCount; +} - // See documentation on DCC class for info on this section -void DCCEXParser::parse(Stream & stream,const char *com) { +void DCCEXParser::parse(const char *com) { (void) EEPROM; // tell compiler not to warn thi is unused int p[MAX_PARAMS]; - int params=StringParser::parse(com+1,p,MAX_PARAMS); + int params=splitValues(p); // Functions return from this switch if complete, break from switch implies error to send @@ -30,7 +103,7 @@ void DCCEXParser::parse(Stream & stream,const char *com) { case 't': // THROTTLE DCC::setThrottle(p[1],p[2],p[3]); - StringParser::send(stream,F(""), p[0], p[2],p[3]); + StringFormatter::send(stream,F(""), p[0], p[2],p[3]); return; case 'f': // FUNCTION @@ -44,15 +117,15 @@ void DCCEXParser::parse(Stream & stream,const char *com) { return; case 'T': // TURNOUT - if (parseT(stream,params,p)) return; + if (parseT(params,p)) return; break; case 'Z': // OUTPUT - if (parseZ(stream,params,p)) return; + if (parseZ(params,p)) return; break; case 'S': // SENSOR - if (parseS(stream,params,p)) return; + if (parseS(params,p)) return; break; case 'w': // WRITE CV on MAIN @@ -81,17 +154,17 @@ void DCCEXParser::parse(Stream & stream,const char *com) { case '1': // POWERON <1> DCCWaveform::mainTrack.setPowerMode(POWERMODE::ON); DCCWaveform::progTrack.setPowerMode(POWERMODE::ON); - StringParser::send(stream,F("")); + StringFormatter::send(stream,F("")); return; case '0': // POWEROFF <0> DCCWaveform::mainTrack.setPowerMode(POWERMODE::OFF); DCCWaveform::progTrack.setPowerMode(POWERMODE::OFF); - StringParser::send(stream,F("")); + StringFormatter::send(stream,F("")); return; case 'c': // READ CURRENT - StringParser::send(stream,F(""), DCCWaveform::mainTrack.getLastCurrent()); + StringFormatter::send(stream,F(""), DCCWaveform::mainTrack.getLastCurrent()); return; case 'Q': // SENSORS @@ -99,7 +172,7 @@ void DCCEXParser::parse(Stream & stream,const char *com) { break; case 's': // - StringParser::send(stream,F(""), BOARD_NAME, VERSION, __DATE__, __TIME__ ); + StringFormatter::send(stream,F(""), BOARD_NAME, VERSION, __DATE__, __TIME__ ); // TODO send power status // TODO Send stats of speed reminders table // TODO send status of turnouts etc etc @@ -107,16 +180,16 @@ void DCCEXParser::parse(Stream & stream,const char *com) { case 'E': // STORE EPROM EEStore::store(); - StringParser::send(stream,F(""), EEStore::eeStore->data.nTurnouts, EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs); + StringFormatter::send(stream,F(""), EEStore::eeStore->data.nTurnouts, EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs); return; case 'e': // CLEAR EPROM EEStore::clear(); - StringParser::send(stream, F("")); + StringFormatter::send(stream, F("")); return; case ' ': // < > - StringParser::send(stream,F("\n")); + StringFormatter::send(stream,F("\n")); return; default: //anything else will drop out to @@ -125,10 +198,10 @@ void DCCEXParser::parse(Stream & stream,const char *com) { } // end of opcode switch // Any fallout here sends an - StringParser::send(stream, F("")); + StringFormatter::send(stream, F("")); } -bool DCCEXParser::parseZ(Stream & stream, int params, int p[]){ +bool DCCEXParser::parseZ( int params, int p[]){ switch (params) { @@ -138,7 +211,7 @@ bool DCCEXParser::parseZ(Stream & stream, int params, int p[]){ Output * o=Output::get(p[0]); if(o==NULL) return false; o->activate(p[1]); - StringParser::send(stream,F(""), p[0],p[1]); + StringFormatter::send(stream,F(""), p[0],p[1]); } return true; @@ -159,14 +232,14 @@ bool DCCEXParser::parseZ(Stream & stream, int params, int p[]){ //=================================== -bool DCCEXParser::parseT(Stream & stream, int params, int p[]) { +bool DCCEXParser::parseT( int params, int p[]) { switch(params){ case 0: // return (Turnout::showAll(stream)); break; case 1: // if (!Turnout::remove(p[0])) return false; - StringParser::send(stream,F("")); + StringFormatter::send(stream,F("")); return true; case 2: // @@ -176,7 +249,7 @@ bool DCCEXParser::parseT(Stream & stream, int params, int p[]) { case 3: // if (!Turnout::create(p[0],p[1],p[2])) return false; - StringParser::send(stream,F("")); + StringFormatter::send(stream,F("")); return true; default: @@ -184,7 +257,7 @@ bool DCCEXParser::parseT(Stream & stream, int params, int p[]) { } } -bool DCCEXParser::parseS(Stream & stream, int params, int p[]) { +bool DCCEXParser::parseS( int params, int p[]) { switch(params){ @@ -205,11 +278,9 @@ bool DCCEXParser::parseS(Stream & stream, int params, int p[]) { } return false; } - -int DCCEXParser::stashP[MAX_PARAMS]; -bool DCCEXParser::stashBusy=false; -Stream & DCCEXParser::stashStream=Serial; // only to keep compiler happy + + // CALLBACKS must be static bool DCCEXParser::stashCallback(Stream & stream, int p[MAX_PARAMS]) { if (stashBusy) return false; stashBusy=true; @@ -218,16 +289,16 @@ bool DCCEXParser::stashCallback(Stream & stream, int p[MAX_PARAMS]) { return true; } void DCCEXParser::callback_W(int result) { - StringParser::send(stashStream,F(""), stashP[2], stashP[3],stashP[0],result==1?stashP[1]:-1); + StringFormatter::send(stashStream,F(""), stashP[2], stashP[3],stashP[0],result==1?stashP[1]:-1); stashBusy=false; } void DCCEXParser::callback_B(int result) { - StringParser::send(stashStream,F(""), stashP[3],stashP[4], stashP[0],stashP[1],result==1?stashP[2]:-1); + StringFormatter::send(stashStream,F(""), stashP[3],stashP[4], stashP[0],stashP[1],result==1?stashP[2]:-1); stashBusy=false; } void DCCEXParser::callback_R(int result) { - StringParser::send(stashStream,F(""),stashP[1],stashP[2],stashP[0],result); + StringFormatter::send(stashStream,F(""),stashP[1],stashP[2],stashP[0],result); stashBusy=false; } diff --git a/DCCEXParser.h b/DCCEXParser.h index 42e26cd..a038d98 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -2,19 +2,28 @@ #define DCCEXParser_h struct DCCEXParser { - static void parse(Stream & stream,const char * command); - + DCCEXParser(Stream & myStream); + void loop(); private: - + static const int MAX_PARAMS=10; // longest command sent in + static const int MAX_BUFFER=50; // longest command sent in - static bool parseT(Stream & stream, int params, int p[]); - static bool parseZ(Stream & stream, int params, int p[]); - static bool parseS(Stream & stream, int params, int p[]); + Stream & stream; + byte bufferLength=0; + bool inCommandPayload=false; + char buffer[MAX_BUFFER]; + void parse(const char * command); + int splitValues( int result[MAX_PARAMS]); + + bool parseT(int params, int p[]); + bool parseZ(int params, int p[]); + bool parseS( int params, int p[]); + - static int stashP[MAX_PARAMS]; static bool stashBusy; static Stream & stashStream; + static int stashP[MAX_PARAMS]; static bool stashCallback(Stream & stream, int p[MAX_PARAMS]); static void callback_W(int result); static void callback_B(int result); diff --git a/DIAG.h b/DIAG.h index 8a75b62..e5d0213 100644 --- a/DIAG.h +++ b/DIAG.h @@ -1,9 +1,9 @@ #ifndef DIAG_h #define DIAG_h -#include "StringParser.h" +#include "StringFormatter.h" #ifndef DIAG_ENABLED #define DIAG_ENABLED true #endif -#define DIAG if (DIAG_ENABLED) StringParser::print +#define DIAG if (DIAG_ENABLED) StringFormatter::print #endif diff --git a/Outputs.cpp b/Outputs.cpp index fa7227e..80cf4c3 100644 --- a/Outputs.cpp +++ b/Outputs.cpp @@ -65,7 +65,7 @@ the state of any outputs being monitored or controlled by a separate interface o #include "Outputs.h" #include "EEStore.h" -#include "StringParser.h" +#include "StringFormatter.h" void Output::activate(int s){ data.oStatus=(s>0); // if s>0, set status to active, else inactive digitalWrite(data.pin,data.oStatus ^ bitRead(data.iFlag,0)); // set state of output pin to HIGH or LOW depending on whether bit zero of iFlag is set to 0 (ACTIVE=HIGH) or 1 (ACTIVE=LOW) @@ -106,14 +106,14 @@ bool Output::showAll(Stream & stream){ bool gotone=false; for(Output * tt=firstOutput;tt!=NULL;tt=tt->nextOutput){ gotone=true; - StringParser::send(stream,F(""), tt->data.id, tt->data.pin, tt->data.iFlag, tt->data.oStatus); + StringFormatter::send(stream,F(""), tt->data.id, tt->data.pin, tt->data.iFlag, tt->data.oStatus); } return gotone; } void Output::show(Stream & stream){ for(Output * tt=firstOutput;tt!=NULL;tt=tt->nextOutput){ - StringParser::send(stream,F(""), tt->data.id, tt->data.oStatus); + StringFormatter::send(stream,F(""), tt->data.id, tt->data.oStatus); } } diff --git a/Sensors.cpp b/Sensors.cpp index dcafb45..b9c6ee6 100644 --- a/Sensors.cpp +++ b/Sensors.cpp @@ -50,7 +50,7 @@ decide to ignore the return and only react to triggers. #include "Sensors.h" #include "EEStore.h" -#include "StringParser.h" +#include "StringFormatter.h" /////////////////////////////////////////////////////////////////////////////// @@ -62,10 +62,10 @@ void Sensor::check(Stream & stream){ if(!tt->active && tt->signal<0.5){ tt->active=true; - StringParser::send(stream,F(""), tt->data.snum); + StringFormatter::send(stream,F(""), tt->data.snum); } else if(tt->active && tt->signal>0.9){ tt->active=false; - StringParser::send(stream,F(""), tt->data.snum); + StringFormatter::send(stream,F(""), tt->data.snum); } } // loop over all sensors @@ -131,7 +131,7 @@ bool Sensor::remove(int n){ void Sensor::show(Stream & stream){ for(Sensor * tt=firstSensor;tt!=NULL;tt=tt->nextSensor){ - StringParser::send(stream, F(""), tt->data.snum, tt->data.pin, tt->data.pullUp); + StringFormatter::send(stream, F(""), tt->data.snum, tt->data.pin, tt->data.pullUp); } } @@ -139,7 +139,7 @@ void Sensor::show(Stream & stream){ void Sensor::status(Stream & stream){ for(Sensor * tt=firstSensor;tt!=NULL;tt=tt->nextSensor){ - StringParser::send(stream,F("<%s %d>"), tt->active?"Q":"q", tt->data.snum); + StringFormatter::send(stream,F("<%s %d>"), tt->active?"Q":"q", tt->data.snum); } } diff --git a/StringFormatter.cpp b/StringFormatter.cpp new file mode 100644 index 0000000..188d435 --- /dev/null +++ b/StringFormatter.cpp @@ -0,0 +1,40 @@ +#include "StringFormatter.h" +#include + + +void StringFormatter::print( const __FlashStringHelper* input...) { + va_list args; + va_start(args, input); + send(Serial,input,args); +} + +void StringFormatter::send(Stream & stream, const __FlashStringHelper* input...) { + va_list args; + va_start(args, input); + send(stream,input,args); +} + +void StringFormatter::send(Stream & stream,const __FlashStringHelper* format, va_list args) { + + // thanks to Jan Turoň https://arduino.stackexchange.com/questions/56517/formatting-strings-in-arduino-for-output + + char* flash=(char*)format; + for(int i=0; ; ++i) { + char c=pgm_read_byte_near(flash+i); + if (c=='\0') return; + if(c!='%') { stream.print(c); continue; } + i++; + c=pgm_read_byte_near(flash+i); + switch(c) { + case '%': stream.write('%'); break; + case 's': stream.print(va_arg(args, char*)); break; + case 'd': stream.print(va_arg(args, int), DEC); break; + case 'b': stream.print(va_arg(args, int), BIN); break; + case 'o': stream.print(va_arg(args, int), OCT); break; + case 'x': stream.print(va_arg(args, int), HEX); break; + case 'c': stream.write(va_arg(args, int)); break; + case 'f': stream.print(va_arg(args, double), 2); break; + } + } + va_end(args); +} diff --git a/StringParser.h b/StringFormatter.h similarity index 53% rename from StringParser.h rename to StringFormatter.h index 1d11bca..5994326 100644 --- a/StringParser.h +++ b/StringFormatter.h @@ -1,19 +1,15 @@ -#ifndef StringParser_h -#define StringParser_h +#ifndef StringFormatter_h +#define StringFormatter_h #include -class StringParser +class StringFormatter { public: - static void loop(Stream & stream, void (*callback)(Stream & stream, const char * data) ); static int parse(const char * com, int result[], byte maxResults); static void print( const __FlashStringHelper* input...); static void send(Stream & serial, const __FlashStringHelper* input...); private: static void send(Stream & serial, const __FlashStringHelper* input,va_list args); - static byte bufferLength; - static bool inCommandPayload; - static const byte MAX_BUFFER=100; - static char buffer[MAX_BUFFER]; + }; #endif diff --git a/StringParser.cpp b/StringParser.cpp deleted file mode 100644 index 46193f5..0000000 --- a/StringParser.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include "StringParser.h" -#include - -byte StringParser::bufferLength=0; -bool StringParser::inCommandPayload=false; -char StringParser::buffer[MAX_BUFFER]; - -void StringParser::loop(Stream & stream, void (*callback)(Stream & stream, const char * data) ) { - while(stream.available()) { - if (bufferLength==MAX_BUFFER) { - bufferLength=0; - inCommandPayload=false; - } - char ch = stream.read(); - if (ch == '<') { - inCommandPayload = true; - bufferLength=0; - buffer[0]='\0'; - } - else if (ch == '>') { - buffer[bufferLength]='\0'; - (*callback)(Serial, buffer); - inCommandPayload = false; - } else if(inCommandPayload) { - buffer[bufferLength++]= ch; - } - } - } -int StringParser::parse(const char * com, int result[], byte maxResults) { - byte state=1; - byte parameterCount=0; - int runningValue=0; - const char * remainingCmd=com; // skips the opcode - bool signNegative=false; - - // clear all parameters in case not enough found - for (int i=0;i') return parameterCount; - state=2; - continue; - - case 2: // checking sign - signNegative=false; - runningValue=0; - state=3; - if (hot!='-') continue; - signNegative=true; - break; - case 3: // building a parameter - if (hot>='0' && hot<='9') { - runningValue=10*runningValue+(hot-'0'); - break; - } - result[parameterCount] = runningValue * (signNegative ?-1:1); - parameterCount++; - state=1; - continue; - } - remainingCmd++; - } - return parameterCount; -} - - - -void StringParser::print( const __FlashStringHelper* input...) { - va_list args; - va_start(args, input); - send(Serial,input,args); -} - -void StringParser::send(Stream & stream, const __FlashStringHelper* input...) { - va_list args; - va_start(args, input); - send(stream,input,args); -} - -void StringParser::send(Stream & stream,const __FlashStringHelper* format, va_list args) { - - - // thanks to Jan Turoň https://arduino.stackexchange.com/questions/56517/formatting-strings-in-arduino-for-output - - char* flash=(char*)format; - for(int i=0; ; ++i) { - char c=pgm_read_byte_near(flash+i); - if (c=='\0') return; - if(c!='%') { stream.print(c); continue; } - i++; - c=pgm_read_byte_near(flash+i); - switch(c) { - case '%': stream.print('%'); break; - case 's': stream.print(va_arg(args, char*)); break; - case 'd': stream.print(va_arg(args, int), DEC); break; - case 'b': stream.print(va_arg(args, int), BIN); break; - case 'o': stream.print(va_arg(args, int), OCT); break; - case 'x': stream.print(va_arg(args, int), HEX); break; - case 'f': stream.print(va_arg(args, double), 2); break; - } - } - va_end(args); -} diff --git a/Turnouts.cpp b/Turnouts.cpp index f66ddd0..c7832d7 100644 --- a/Turnouts.cpp +++ b/Turnouts.cpp @@ -1,6 +1,6 @@ #include "Turnouts.h" #include "EEStore.h" -#include "StringParser.h" +#include "StringFormatter.h" bool Turnout::activate(int n,bool state){ Turnout * tt=get(n); @@ -43,7 +43,7 @@ bool Turnout::remove(int n){ void Turnout::show(Stream & stream, int n){ for(Turnout *tt=firstTurnout;tt!=NULL;tt=tt->nextTurnout){ if (tt->data.id==n) { - StringParser::send(stream,F(""), tt->data.id, tt->data.tStatus); + StringFormatter::send(stream,F(""), tt->data.id, tt->data.tStatus); return; } } @@ -52,7 +52,7 @@ void Turnout::show(Stream & stream, int n){ bool Turnout::showAll(Stream & stream){ bool gotOne=false; for(Turnout * tt=firstTurnout;tt!=NULL;tt=tt->nextTurnout){ - StringParser::send(stream,F(""), tt->data.id, tt->data.address, tt->data.subAddress, tt->data.tStatus); + StringFormatter::send(stream,F(""), tt->data.id, tt->data.address, tt->data.subAddress, tt->data.tStatus); gotOne=true; } return gotOne;