diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index fa4c9f8..65790c3 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -797,6 +797,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) break; #endif + case '/': // implemented in EXRAIL parser case 'L': // LCC interface implemented in EXRAIL parser break; // Will if not intercepted by EXRAIL diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index b39adff..55d922e 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -373,7 +373,7 @@ RMFT2::RMFT2(int progCtr) { speedo=0; forward=true; invert=false; - timeoutFlag=false; + blinkState=not_blink_task; stackDepth=0; onEventStartPosition=-1; // Not handling an ONxxx @@ -491,6 +491,23 @@ void RMFT2::loop() { void RMFT2::loop2() { if (delayTime!=0 && millis()-delayStart < delayTime) return; + // special stand alone blink task + if (compileFeatures & FEATURE_BLINK) { + if (blinkState==blink_low) { + IODevice::write(blinkPin,HIGH); + blinkState=blink_high; + delayMe(getOperand(1)); + return; + } + if (blinkState==blink_high) { + IODevice::write(blinkPin,LOW); + blinkState=blink_low; + delayMe(getOperand(2)); + return; + } + } + + // Normal progstep following tasks continue here. byte opcode = GET_OPCODE; int16_t operand = getOperand(0); @@ -560,39 +577,39 @@ void RMFT2::loop2() { break; case OPCODE_AT: - timeoutFlag=false; + blinkState=not_blink_task; if (readSensor(operand)) break; delayMe(50); return; case OPCODE_ATGTE: // wait for analog sensor>= value - timeoutFlag=false; + blinkState=not_blink_task; if (IODevice::readAnalogue(operand) >= (int)(getOperand(1))) break; delayMe(50); return; case OPCODE_ATLT: // wait for analog sensor < value - timeoutFlag=false; + blinkState=not_blink_task; if (IODevice::readAnalogue(operand) < (int)(getOperand(1))) break; delayMe(50); return; case OPCODE_ATTIMEOUT1: // ATTIMEOUT(vpin,timeout) part 1 timeoutStart=millis(); - timeoutFlag=false; + blinkState=not_blink_task; break; case OPCODE_ATTIMEOUT2: if (readSensor(operand)) break; // success without timeout if (millis()-timeoutStart > 100*getOperand(1)) { - timeoutFlag=true; + blinkState=at_timeout; break; // and drop through } delayMe(50); return; case OPCODE_IFTIMEOUT: // do next operand if timeout flag set - skipIf=!timeoutFlag; + skipIf=blinkState!=at_timeout; break; case OPCODE_AFTER: // waits for sensor to hit and then remain off for 0.5 seconds. (must come after an AT operation) @@ -624,13 +641,25 @@ void RMFT2::loop2() { break; case OPCODE_SET: + killBlinkOnVpin(operand); IODevice::write(operand,true); break; case OPCODE_RESET: + killBlinkOnVpin(operand); IODevice::write(operand,false); break; - + + case OPCODE_BLINK: + // Start a new task to blink this vpin + killBlinkOnVpin(operand); + { + auto newtask=new RMFT2(progCounter); + newtask->blinkPin=operand; + newtask->blinkState=blink_low; // will go high on first call + } + break; + case OPCODE_PAUSE: DCC::setThrottle(0,1,true); // pause all locos on the track pausingTask=this; @@ -1175,16 +1204,19 @@ int16_t RMFT2::getSignalSlot(int16_t id) { if (redpin) { bool redval=(rag==SIGNAL_RED || rag==SIMAMBER); if (!aHigh) redval=!redval; + killBlinkOnVpin(redpin); IODevice::write(redpin,redval); } if (amberpin) { bool amberval=(rag==SIGNAL_AMBER); if (!aHigh) amberval=!amberval; + killBlinkOnVpin(amberpin); IODevice::write(amberpin,amberval); } if (greenpin) { bool greenval=(rag==SIGNAL_GREEN || rag==SIMAMBER); if (!aHigh) greenval=!greenval; + killBlinkOnVpin(greenpin); IODevice::write(greenpin,greenval); } } @@ -1272,6 +1304,25 @@ void RMFT2::powerEvent(int16_t track, bool overload) { } } +// This function is used when setting pins so that a SET or RESET +// will cause any blink task on that pin to terminate. +// It will be compiled out of existence if no BLINK feature is used. +void RMFT2::killBlinkOnVpin(VPIN pin) { + if (!(compileFeatures & FEATURE_BLINK)) return; + + RMFT2 * task=loopTask; + while(task) { + if ( + (task->blinkState==blink_high || task->blinkState==blink_low) + && task->blinkPin==pin) { + task->kill(); + return; + } + task=task->next; + if (task==loopTask) return; + } +} + void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc) { // Check we dont already have a task running this handler RMFT2 * task=loopTask; diff --git a/EXRAIL2.h b/EXRAIL2.h index 707bfae..c116be9 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -41,6 +41,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ATGTE,OPCODE_ATLT, OPCODE_ATTIMEOUT1,OPCODE_ATTIMEOUT2, OPCODE_LATCH,OPCODE_UNLATCH,OPCODE_SET,OPCODE_RESET, + OPCODE_BLINK, OPCODE_ENDIF,OPCODE_ELSE, OPCODE_DELAY,OPCODE_DELAYMINS,OPCODE_DELAYMS,OPCODE_RANDWAIT, OPCODE_FON,OPCODE_FOFF,OPCODE_XFON,OPCODE_XFOFF, @@ -99,12 +100,21 @@ enum thrunger: byte { thrunge_lcd, // Must be last!! }; + +enum BlinkState: byte { + not_blink_task, + blink_low, // blink task running with pin LOW + blink_high, // blink task running with pin high + at_timeout // ATTIMEOUT timed out flag + }; + // Flag bits for compile time features. static const byte FEATURE_SIGNAL= 0x80; static const byte FEATURE_LCC = 0x40; static const byte FEATURE_ROSTER= 0x20; static const byte FEATURE_ROUTESTATE= 0x10; static const byte FEATURE_STASH = 0x08; + static const byte FEATURE_BLINK = 0x04; // Flag bits for status of hardware and TPL @@ -193,6 +203,7 @@ private: static LookList* LookListLoader(OPCODE op1, OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL); static uint16_t getOperand(int progCounter,byte n); + static void killBlinkOnVpin(VPIN pin); static RMFT2 * loopTask; static RMFT2 * pausingTask; void delayMe(long millisecs); @@ -245,10 +256,10 @@ private: union { unsigned long waitAfter; // Used by OPCODE_AFTER unsigned long timeoutStart; // Used by OPCODE_ATTIMEOUT + VPIN blinkPin; // Used by blink tasks }; - bool timeoutFlag; byte taskId; - + BlinkState blinkState; // includes AT_TIMEOUT flag. uint16_t loco; bool forward; bool invert; diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 187c4ca..7f3c88a 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -38,6 +38,7 @@ #undef ATTIMEOUT #undef AUTOMATION #undef AUTOSTART +#undef BLINK #undef BROADCAST #undef CALL #undef CLEAR_STASH @@ -199,6 +200,7 @@ #define ATTIMEOUT(sensor_id,timeout_ms) #define AUTOMATION(id,description) #define AUTOSTART +#define BLINK(vpin,onDuty,offDuty) #define BROADCAST(msg) #define CALL(route) #define CLEAR_STASH(id) diff --git a/EXRAIL2Parser.cpp b/EXRAIL2Parser.cpp index 2cbdd10..95375bb 100644 --- a/EXRAIL2Parser.cpp +++ b/EXRAIL2Parser.cpp @@ -183,12 +183,20 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { StringFormatter::send(stream, F("<* EXRAIL STATUS")); RMFT2 * task=loopTask; while(task) { + if ((compileFeatures & FEATURE_BLINK) + && (task->blinkState==blink_high || task->blinkState==blink_low)) { + StringFormatter::send(stream,F("\nID=%d,PC=%d,BLINK=%d"), + (int)(task->taskId),task->progCounter,task->blinkPin + ); + } + else { StringFormatter::send(stream,F("\nID=%d,PC=%d,LOCO=%d%c,SPEED=%d%c"), (int)(task->taskId),task->progCounter,task->loco, task->invert?'I':' ', task->speedo, task->forward?'F':'R' ); + } task=task->next; if (task==loopTask) break; } diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 0f4b3a8..10b0eb6 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -208,6 +208,8 @@ bool exrailHalSetup() { #define PICKUP_STASH(id) | FEATURE_STASH #undef STASH #define STASH(id) | FEATURE_STASH +#undef BLINK +#define BLINK(vpin,onDuty,offDuty) | FEATURE_BLINK const byte RMFT2::compileFeatures = 0 #include "myAutomation.h" @@ -457,6 +459,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define ATTIMEOUT(sensor_id,timeout) OPCODE_ATTIMEOUT1,0,0,OPCODE_ATTIMEOUT2,V(sensor_id),OPCODE_PAD,V(timeout/100L), #define AUTOMATION(id, description) OPCODE_AUTOMATION, V(id), #define AUTOSTART OPCODE_AUTOSTART,0,0, +#define BLINK(vpin,onDuty,offDuty) OPCODE_BLINK,V(vpin),OPCODE_PAD,V(onDuty),OPCODE_PAD,V(offDuty), #define BROADCAST(msg) PRINT(msg) #define CALL(route) OPCODE_CALL,V(route), #define CLEAR_STASH(id) OPCODE_CLEAR_STASH,V(id),