diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 2bd79e9..dab213b 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -67,7 +67,7 @@ Once a new OPCODE is decided upon, update this list. k, Reserved for future use - Potentially Railcom K, Reserved for future use - Potentially Railcom l, Loco speedbyte/function map broadcast - L, + L, Reserved for LCC interface (implemented in EXRAIL) m, M, Write DCC packet n, @@ -906,6 +906,9 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) break; #endif + case 'L': // LCC interface implemented in EXRAIL parser + break; // Will if not intercepted by EXRAIL + default: //anything else will diagnose and drop out to DIAG(F("Opcode=%c params=%d"), opcode, params); for (int i = 0; i < params; i++) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 321872d..c902708 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -85,7 +85,7 @@ RMFT2 * RMFT2::pausingTask=NULL; // Task causing a PAUSE. // when pausingTask is set, that is the ONLY task that gets any service, // and all others will have their locos stopped, then resumed after the pausing task resumes. byte RMFT2::flags[MAX_FLAGS]; - +Print * RMFT2::LCCSerial=0; LookList * RMFT2::sequenceLookup=NULL; LookList * RMFT2::onThrowLookup=NULL; LookList * RMFT2::onCloseLookup=NULL; @@ -176,23 +176,26 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { onCloseLookup=LookListLoader(OPCODE_ONCLOSE); onActivateLookup=LookListLoader(OPCODE_ONACTIVATE); onDeactivateLookup=LookListLoader(OPCODE_ONDEACTIVATE); - onRedLookup=LookListLoader(OPCODE_ONRED); - onAmberLookup=LookListLoader(OPCODE_ONAMBER); - onGreenLookup=LookListLoader(OPCODE_ONGREEN); onChangeLookup=LookListLoader(OPCODE_ONCHANGE); onClockLookup=LookListLoader(OPCODE_ONTIME); #ifndef IO_NO_HAL onRotateLookup=LookListLoader(OPCODE_ONROTATE); #endif onOverloadLookup=LookListLoader(OPCODE_ONOVERLOAD); + // onLCCLookup is not the same so not loaded here. // Second pass startup, define any turnouts or servos, set signals red // add sequences onRoutines to the lookups +if (compileFeatures & FEATURE_SIGNAL) { + onRedLookup=LookListLoader(OPCODE_ONRED); + onAmberLookup=LookListLoader(OPCODE_ONAMBER); + onGreenLookup=LookListLoader(OPCODE_ONGREEN); for (int sigslot=0;;sigslot++) { VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); if (sigid==0) break; // end of signal list doSignal(sigid & SIGNAL_ID_MASK, SIGNAL_RED); } + } int progCounter; for (progCounter=0;; SKIPOP){ @@ -343,13 +346,65 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16 reject=!parseSlash(stream,paramCount,p); opcode=0; break; - + case 'L': + if (compileFeatures & FEATURE_LCC) { + // This entire code block is compiled out if LLC macros not used + if (paramCount==0) { // LCC adapter introducing self + LCCSerial=stream; // now we know where to send events we raise + + // loop through all possible sent events + for (int progCounter=0;; SKIPOP) { + byte opcode=GET_OPCODE; + if (opcode==OPCODE_ENDEXRAIL) break; + if (opcode==OPCODE_LCC) StringFormatter::send(stream,F("\n"),getOperand(progCounter,0)); + if (opcode==OPCODE_LCCX) { // long form LCC + StringFormatter::send(stream,F("\n"), + getOperand(progCounter,1), + getOperand(progCounter,2), + getOperand(progCounter,3), + getOperand(progCounter,0) + ); + }} + + // we stream the hex events we wish to listen to + // and at the same time build the event index looku. + + + int eventIndex=0; + for (int progCounter=0;; SKIPOP) { + byte opcode=GET_OPCODE; + if (opcode==OPCODE_ENDEXRAIL) break; + if (opcode==OPCODE_ONLCC) { + onLCCLookup[eventIndex]=progCounter; // TODO skip... + StringFormatter::send(stream,F("\n"), + eventIndex, + getOperand(progCounter,1), + getOperand(progCounter,2), + getOperand(progCounter,3), + getOperand(progCounter,0) + ); + eventIndex++; + } + } + StringFormatter::send(stream,F("\n")); // Ready to rumble + opcode=0; + break; + } + if (paramCount==1) { // LCC event arrived from adapter + int16_t eventid=p[0]; + reject=eventid<0 || eventid>=countLCCLookup; + if (!reject) startNonRecursiveTask(F("LCC"),eventid,onLCCLookup[eventid]); + opcode=0; + } + } + break; + default: // other commands pass through break; } if (reject) { opcode=0; - StringFormatter::send(stream,F("")); + StringFormatter::send(stream,F("\n")); } } @@ -377,17 +432,19 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { if (flag & LATCH_FLAG) StringFormatter::send(stream,F(" LATCHED")); } } - // do the signals - // flags[n] represents the state of the nth signal in the table - for (int sigslot=0;;sigslot++) { - VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); - if (sigid==0) break; // end of signal list - byte flag=flags[sigslot] & SIGNAL_MASK; // obtain signal flags for this id - StringFormatter::send(stream,F("\n%S[%d]"), - (flag == SIGNAL_RED)? F("RED") : (flag==SIGNAL_GREEN) ? F("GREEN") : F("AMBER"), - sigid & SIGNAL_ID_MASK); - } - + + if (compileFeatures & FEATURE_SIGNAL) { + // do the signals + // flags[n] represents the state of the nth signal in the table + for (int sigslot=0;;sigslot++) { + VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); + if (sigid==0) break; // end of signal list + byte flag=flags[sigslot] & SIGNAL_MASK; // obtain signal flags for this id + StringFormatter::send(stream,F("\n%S[%d]"), + (flag == SIGNAL_RED)? F("RED") : (flag==SIGNAL_GREEN) ? F("GREEN") : F("AMBER"), + sigid & SIGNAL_ID_MASK); + } + } StringFormatter::send(stream,F(" *>\n")); return true; } @@ -1034,7 +1091,21 @@ void RMFT2::loop2() { invert=false; } break; - + + case OPCODE_LCC: // short form LCC + if ((compileFeatures & FEATURE_LCC) && LCCSerial) + StringFormatter::send(LCCSerial,F(""),(uint16_t)operand); + break; + + case OPCODE_LCCX: // long form LCC + if ((compileFeatures & FEATURE_LCC) && LCCSerial) + StringFormatter::send(LCCSerial,F("\n"), + getOperand(progCounter,1), + getOperand(progCounter,2), + getOperand(progCounter,3), + getOperand(progCounter,0) + ); + break; case OPCODE_SERVO: // OPCODE_SERVO,V(vpin),OPCODE_PAD,V(position),OPCODE_PAD,V(profile),OPCODE_PAD,V(duration) IODevice::writeAnalogue(operand,getOperand(1),getOperand(2),getOperand(3)); @@ -1072,6 +1143,7 @@ void RMFT2::loop2() { case OPCODE_SERVOTURNOUT: // Turnout definition ignored at runtime case OPCODE_PINTURNOUT: // Turnout definition ignored at runtime case OPCODE_ONCLOSE: // Turnout event catchers ignored here + case OPCODE_ONLCC: // LCC event catchers ignored here case OPCODE_ONTHROW: case OPCODE_ONACTIVATE: // Activate event catchers ignored here case OPCODE_ONDEACTIVATE: @@ -1141,6 +1213,7 @@ int16_t RMFT2::getSignalSlot(int16_t id) { } /* static */ void RMFT2::doSignal(int16_t id,char rag) { + if (!(compileFeatures & FEATURE_SIGNAL)) return; // dont compile code below if (diag) DIAG(F(" doSignal %d %x"),id,rag); // Schedule any event handler for this signal change. @@ -1208,6 +1281,7 @@ int16_t RMFT2::getSignalSlot(int16_t id) { } /* static */ bool RMFT2::isSignal(int16_t id,char rag) { + if (!(compileFeatures & FEATURE_SIGNAL)) return false; int16_t sigslot=getSignalSlot(id); if (sigslot<0) return false; return (flags[sigslot] & SIGNAL_MASK) == rag; @@ -1260,8 +1334,10 @@ void RMFT2::powerEvent(int16_t track, bool overload) { void RMFT2::handleEvent(const FSH* reason,LookList* handlers, int16_t id) { int pc= handlers->find(id); - if (pc<0) return; - + if (pc>=0) startNonRecursiveTask(reason,id,pc); +} + +void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc) { // Check we dont already have a task running this handler RMFT2 * task=loopTask; while(task) { diff --git a/EXRAIL2.h b/EXRAIL2.h index 7b0a9af..de14f11 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -66,6 +66,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ONTIME, OPCODE_TTADDPOSITION,OPCODE_DCCTURNTABLE,OPCODE_EXTTTURNTABLE, OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_WAITFORTT, + OPCODE_LCC,OPCODE_LCCX,OPCODE_ONLCC, OPCODE_ONOVERLOAD, // OPcodes below this point are skip-nesting IF operations @@ -94,7 +95,11 @@ enum thrunger: byte { thrunge_lcd, // Must be last!! }; - + // Flag bits for compile time features. + static const byte FEATURE_SIGNAL= 0x80; + static const byte FEATURE_LCC = 0x40; + static const byte FEATURE_ROSTER= 0x20; + // Flag bits for status of hardware and TPL static const byte SECTION_FLAG = 0x80; @@ -173,6 +178,7 @@ private: OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL); static void handleEvent(const FSH* reason,LookList* handlers, int16_t id); static uint16_t getOperand(int progCounter,byte n); + static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc); static RMFT2 * loopTask; static RMFT2 * pausingTask; void delayMe(long millisecs); @@ -191,6 +197,7 @@ private: static const HIGHFLASH byte RouteCode[]; static const HIGHFLASH int16_t SignalDefinitions[]; static byte flags[MAX_FLAGS]; + static Print * LCCSerial; static LookList * sequenceLookup; static LookList * onThrowLookup; static LookList * onCloseLookup; @@ -205,6 +212,10 @@ private: static LookList * onRotateLookup; #endif static LookList * onOverloadLookup; + + static const int countLCCLookup; + static int onLCCLookup[]; + static const byte compileFeatures; // Local variables - exist for each instance/task RMFT2 *next; // loop chain diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 03e310e..bdb6420 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -86,6 +86,8 @@ #undef LATCH #undef LCD #undef SCREEN +#undef LCC +#undef LCCX #undef LCN #undef MOVETT #undef ONACTIVATE @@ -94,6 +96,7 @@ #undef ONDEACTIVATE #undef ONDEACTIVATEL #undef ONCLOSE +#undef ONLCC #undef ONTIME #undef ONCLOCKTIME #undef ONCLOCKMINS @@ -221,7 +224,9 @@ #define INVERT_DIRECTION #define JOIN #define KILLALL -#define LATCH(sensor_id) +#define LATCH(sensor_id) +#define LCC(eventid) +#define LCCX(senderid,eventid) #define LCD(row,msg) #define SCREEN(display,row,msg) #define LCN(msg) @@ -236,6 +241,7 @@ #define ONDEACTIVATE(addr,subaddr) #define ONDEACTIVATEL(linear) #define ONCLOSE(turnout_id) +#define ONLCC(sender,event) #define ONGREEN(signal_id) #define ONRED(signal_id) #define ONROTATE(turntable_id) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index b85b68a..3322180 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -85,6 +85,30 @@ void exrailHalSetup() { #include "myAutomation.h" } +// Pass 1c detect compile time featurtes +#include "EXRAIL2MacroReset.h" +#undef SIGNAL +#define SIGNAL(redpin,amberpin,greenpin) | FEATURE_SIGNAL +#undef SIGNALH +#define SIGNALH(redpin,amberpin,greenpin) | FEATURE_SIGNAL +#undef SERVO_SIGNAL +#define SERVO_SIGNAL(vpin,redval,amberval,greenval) | FEATURE_SIGNAL +#undef DCC_SIGNAL +#define DCC_SIGNAL(id,addr,subaddr) | FEATURE_SIGNAL +#undef VIRTUAL_SIGNAL +#define VIRTUAL_SIGNAL(id) | FEATURE_SIGNAL + +#undef LCC +#define LCC(eventid) | FEATURE_LCC +#undef LCCX +#define LCCX(senderid,eventid) | FEATURE_LCC +#undef ONLCC +#define ONLCC(senderid,eventid) | FEATURE_LCC + +const byte RMFT2::compileFeatures = 0 + #include "myAutomation.h" +; + // Pass 2 create throttle route list #include "EXRAIL2MacroReset.h" #undef ROUTE @@ -278,6 +302,16 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #include "myAutomation.h" 0,0,0,0 }; +// Pass 9 ONLCC counter and lookup array +#include "EXRAIL2MacroReset.h" +#undef ONLCC +#define ONLCC(sender,event) +1 + +const int RMFT2::countLCCLookup=0 +#include "myAutomation.h" +; +int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; + // Last Pass : create main routes table // Only undef the macros, not dummy them. #define RMFT2_UNDEF_ONLY @@ -354,6 +388,11 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define JOIN OPCODE_JOIN,0,0, #define KILLALL OPCODE_KILLALL,0,0, #define LATCH(sensor_id) OPCODE_LATCH,V(sensor_id), +#define LCC(eventid) OPCODE_LCC,V(eventid), +#define LCCX(sender,event) OPCODE_LCCX,V(event),\ + OPCODE_PAD,V((((uint64_t)sender)>>32)&0xFFFF),\ + OPCODE_PAD,V((((uint64_t)sender)>>16)&0xFFFF),\ + OPCODE_PAD,V((((uint64_t)sender)>>0)&0xFFFF), #define LCD(id,msg) PRINT(msg) #define SCREEN(display,id,msg) PRINT(msg) #define LCN(msg) PRINT(msg) @@ -362,6 +401,10 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3), #define ONAMBER(signal_id) OPCODE_ONAMBER,V(signal_id), #define ONCLOSE(turnout_id) OPCODE_ONCLOSE,V(turnout_id), +#define ONLCC(sender,event) OPCODE_ONLCC,V(event),\ + OPCODE_PAD,V((((uint64_t)sender)>>32)&0xFFFF),\ + OPCODE_PAD,V((((uint64_t)sender)>>16)&0xFFFF),\ + OPCODE_PAD,V((((uint64_t)sender)>>0)&0xFFFF), #define ONTIME(value) OPCODE_ONTIME,V(value), #define ONCLOCKTIME(hours,mins) OPCODE_ONTIME,V((STRIP_ZERO(hours)*60)+STRIP_ZERO(mins)), #define ONCLOCKMINS(mins) ONCLOCKTIME(25,mins) diff --git a/StringFormatter.cpp b/StringFormatter.cpp index c475ef0..b40de1c 100644 --- a/StringFormatter.cpp +++ b/StringFormatter.cpp @@ -117,6 +117,7 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) { case 'o': stream->print(va_arg(args, int), OCT); break; case 'x': stream->print((unsigned int)va_arg(args, unsigned int), HEX); break; case 'X': stream->print((unsigned long)va_arg(args, unsigned long), HEX); break; + case 'h': printHex(stream,(unsigned int)va_arg(args, unsigned int)); break; case 'M': { // this prints a unsigned long microseconds time in readable format unsigned long time = va_arg(args, long); @@ -218,4 +219,15 @@ void StringFormatter::printPadded(Print* stream, long value, byte width, bool fo if (!formatLeft) stream->print(value, DEC); } - +// printHex prints the full 2 byte hex with leading zeros, unlike print(value,HEX) +const char FLASH hexchars[]="0123456789ABCDEF"; +void StringFormatter::printHex(Print * stream,uint16_t value) { + char result[5]; + for (int i=3;i>=0;i--) { + result[i]=GETFLASH(hexchars+(value & 0x0F)); + value>>=4; + } + result[4]='\0'; + stream->print(result); +} + \ No newline at end of file diff --git a/StringFormatter.h b/StringFormatter.h index 6923c10..1231d54 100644 --- a/StringFormatter.h +++ b/StringFormatter.h @@ -49,6 +49,7 @@ class StringFormatter static void lcd2(uint8_t display, byte row, const FSH* input...); static void printEscapes(char * input); static void printEscape( char c); + static void printHex(Print * stream,uint16_t value); private: static void send2(Print * serial, const FSH* input,va_list args); diff --git a/version.h b/version.h index 468971e..c2fd520 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.14" +#define VERSION "5.1.15" +// 5.1.15 - LCC/Adapter support and Exrail feature-compile-out. // 5.1.14 - Fixed IFTTPOSITION // 5.1.13 - Changed turntable broadcast from i to I due to server string conflict // 5.1.12 - Added Power commands <0 A> & <1 A> etc. and update to <=>