1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-12-24 13:21:23 +01:00

BLINK command

This commit is contained in:
Asbelos 2024-04-03 15:17:40 +01:00
parent c7ed47400d
commit e69b777a2f
6 changed files with 86 additions and 10 deletions

View File

@ -797,6 +797,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
break; break;
#endif #endif
case '/': // implemented in EXRAIL parser
case 'L': // LCC interface implemented in EXRAIL parser case 'L': // LCC interface implemented in EXRAIL parser
break; // Will <X> if not intercepted by EXRAIL break; // Will <X> if not intercepted by EXRAIL

View File

@ -373,7 +373,7 @@ RMFT2::RMFT2(int progCtr) {
speedo=0; speedo=0;
forward=true; forward=true;
invert=false; invert=false;
timeoutFlag=false; blinkState=not_blink_task;
stackDepth=0; stackDepth=0;
onEventStartPosition=-1; // Not handling an ONxxx onEventStartPosition=-1; // Not handling an ONxxx
@ -491,6 +491,23 @@ void RMFT2::loop() {
void RMFT2::loop2() { void RMFT2::loop2() {
if (delayTime!=0 && millis()-delayStart < delayTime) return; 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; byte opcode = GET_OPCODE;
int16_t operand = getOperand(0); int16_t operand = getOperand(0);
@ -560,39 +577,39 @@ void RMFT2::loop2() {
break; break;
case OPCODE_AT: case OPCODE_AT:
timeoutFlag=false; blinkState=not_blink_task;
if (readSensor(operand)) break; if (readSensor(operand)) break;
delayMe(50); delayMe(50);
return; return;
case OPCODE_ATGTE: // wait for analog sensor>= value case OPCODE_ATGTE: // wait for analog sensor>= value
timeoutFlag=false; blinkState=not_blink_task;
if (IODevice::readAnalogue(operand) >= (int)(getOperand(1))) break; if (IODevice::readAnalogue(operand) >= (int)(getOperand(1))) break;
delayMe(50); delayMe(50);
return; return;
case OPCODE_ATLT: // wait for analog sensor < value case OPCODE_ATLT: // wait for analog sensor < value
timeoutFlag=false; blinkState=not_blink_task;
if (IODevice::readAnalogue(operand) < (int)(getOperand(1))) break; if (IODevice::readAnalogue(operand) < (int)(getOperand(1))) break;
delayMe(50); delayMe(50);
return; return;
case OPCODE_ATTIMEOUT1: // ATTIMEOUT(vpin,timeout) part 1 case OPCODE_ATTIMEOUT1: // ATTIMEOUT(vpin,timeout) part 1
timeoutStart=millis(); timeoutStart=millis();
timeoutFlag=false; blinkState=not_blink_task;
break; break;
case OPCODE_ATTIMEOUT2: case OPCODE_ATTIMEOUT2:
if (readSensor(operand)) break; // success without timeout if (readSensor(operand)) break; // success without timeout
if (millis()-timeoutStart > 100*getOperand(1)) { if (millis()-timeoutStart > 100*getOperand(1)) {
timeoutFlag=true; blinkState=at_timeout;
break; // and drop through break; // and drop through
} }
delayMe(50); delayMe(50);
return; return;
case OPCODE_IFTIMEOUT: // do next operand if timeout flag set case OPCODE_IFTIMEOUT: // do next operand if timeout flag set
skipIf=!timeoutFlag; skipIf=blinkState!=at_timeout;
break; break;
case OPCODE_AFTER: // waits for sensor to hit and then remain off for 0.5 seconds. (must come after an AT operation) 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; break;
case OPCODE_SET: case OPCODE_SET:
killBlinkOnVpin(operand);
IODevice::write(operand,true); IODevice::write(operand,true);
break; break;
case OPCODE_RESET: case OPCODE_RESET:
killBlinkOnVpin(operand);
IODevice::write(operand,false); IODevice::write(operand,false);
break; 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: case OPCODE_PAUSE:
DCC::setThrottle(0,1,true); // pause all locos on the track DCC::setThrottle(0,1,true); // pause all locos on the track
pausingTask=this; pausingTask=this;
@ -1175,16 +1204,19 @@ int16_t RMFT2::getSignalSlot(int16_t id) {
if (redpin) { if (redpin) {
bool redval=(rag==SIGNAL_RED || rag==SIMAMBER); bool redval=(rag==SIGNAL_RED || rag==SIMAMBER);
if (!aHigh) redval=!redval; if (!aHigh) redval=!redval;
killBlinkOnVpin(redpin);
IODevice::write(redpin,redval); IODevice::write(redpin,redval);
} }
if (amberpin) { if (amberpin) {
bool amberval=(rag==SIGNAL_AMBER); bool amberval=(rag==SIGNAL_AMBER);
if (!aHigh) amberval=!amberval; if (!aHigh) amberval=!amberval;
killBlinkOnVpin(amberpin);
IODevice::write(amberpin,amberval); IODevice::write(amberpin,amberval);
} }
if (greenpin) { if (greenpin) {
bool greenval=(rag==SIGNAL_GREEN || rag==SIMAMBER); bool greenval=(rag==SIGNAL_GREEN || rag==SIMAMBER);
if (!aHigh) greenval=!greenval; if (!aHigh) greenval=!greenval;
killBlinkOnVpin(greenpin);
IODevice::write(greenpin,greenval); 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) { void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc) {
// Check we dont already have a task running this handler // Check we dont already have a task running this handler
RMFT2 * task=loopTask; RMFT2 * task=loopTask;

View File

@ -41,6 +41,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
OPCODE_ATGTE,OPCODE_ATLT, OPCODE_ATGTE,OPCODE_ATLT,
OPCODE_ATTIMEOUT1,OPCODE_ATTIMEOUT2, OPCODE_ATTIMEOUT1,OPCODE_ATTIMEOUT2,
OPCODE_LATCH,OPCODE_UNLATCH,OPCODE_SET,OPCODE_RESET, OPCODE_LATCH,OPCODE_UNLATCH,OPCODE_SET,OPCODE_RESET,
OPCODE_BLINK,
OPCODE_ENDIF,OPCODE_ELSE, OPCODE_ENDIF,OPCODE_ELSE,
OPCODE_DELAY,OPCODE_DELAYMINS,OPCODE_DELAYMS,OPCODE_RANDWAIT, OPCODE_DELAY,OPCODE_DELAYMINS,OPCODE_DELAYMS,OPCODE_RANDWAIT,
OPCODE_FON,OPCODE_FOFF,OPCODE_XFON,OPCODE_XFOFF, OPCODE_FON,OPCODE_FOFF,OPCODE_XFON,OPCODE_XFOFF,
@ -99,12 +100,21 @@ enum thrunger: byte {
thrunge_lcd, // Must be last!! 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. // Flag bits for compile time features.
static const byte FEATURE_SIGNAL= 0x80; static const byte FEATURE_SIGNAL= 0x80;
static const byte FEATURE_LCC = 0x40; static const byte FEATURE_LCC = 0x40;
static const byte FEATURE_ROSTER= 0x20; static const byte FEATURE_ROSTER= 0x20;
static const byte FEATURE_ROUTESTATE= 0x10; static const byte FEATURE_ROUTESTATE= 0x10;
static const byte FEATURE_STASH = 0x08; static const byte FEATURE_STASH = 0x08;
static const byte FEATURE_BLINK = 0x04;
// Flag bits for status of hardware and TPL // Flag bits for status of hardware and TPL
@ -193,6 +203,7 @@ private:
static LookList* LookListLoader(OPCODE op1, static LookList* LookListLoader(OPCODE op1,
OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL); OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL);
static uint16_t getOperand(int progCounter,byte n); static uint16_t getOperand(int progCounter,byte n);
static void killBlinkOnVpin(VPIN pin);
static RMFT2 * loopTask; static RMFT2 * loopTask;
static RMFT2 * pausingTask; static RMFT2 * pausingTask;
void delayMe(long millisecs); void delayMe(long millisecs);
@ -245,10 +256,10 @@ private:
union { union {
unsigned long waitAfter; // Used by OPCODE_AFTER unsigned long waitAfter; // Used by OPCODE_AFTER
unsigned long timeoutStart; // Used by OPCODE_ATTIMEOUT unsigned long timeoutStart; // Used by OPCODE_ATTIMEOUT
VPIN blinkPin; // Used by blink tasks
}; };
bool timeoutFlag;
byte taskId; byte taskId;
BlinkState blinkState; // includes AT_TIMEOUT flag.
uint16_t loco; uint16_t loco;
bool forward; bool forward;
bool invert; bool invert;

View File

@ -38,6 +38,7 @@
#undef ATTIMEOUT #undef ATTIMEOUT
#undef AUTOMATION #undef AUTOMATION
#undef AUTOSTART #undef AUTOSTART
#undef BLINK
#undef BROADCAST #undef BROADCAST
#undef CALL #undef CALL
#undef CLEAR_STASH #undef CLEAR_STASH
@ -199,6 +200,7 @@
#define ATTIMEOUT(sensor_id,timeout_ms) #define ATTIMEOUT(sensor_id,timeout_ms)
#define AUTOMATION(id,description) #define AUTOMATION(id,description)
#define AUTOSTART #define AUTOSTART
#define BLINK(vpin,onDuty,offDuty)
#define BROADCAST(msg) #define BROADCAST(msg)
#define CALL(route) #define CALL(route)
#define CLEAR_STASH(id) #define CLEAR_STASH(id)

View File

@ -183,12 +183,20 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
StringFormatter::send(stream, F("<* EXRAIL STATUS")); StringFormatter::send(stream, F("<* EXRAIL STATUS"));
RMFT2 * task=loopTask; RMFT2 * task=loopTask;
while(task) { 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"), StringFormatter::send(stream,F("\nID=%d,PC=%d,LOCO=%d%c,SPEED=%d%c"),
(int)(task->taskId),task->progCounter,task->loco, (int)(task->taskId),task->progCounter,task->loco,
task->invert?'I':' ', task->invert?'I':' ',
task->speedo, task->speedo,
task->forward?'F':'R' task->forward?'F':'R'
); );
}
task=task->next; task=task->next;
if (task==loopTask) break; if (task==loopTask) break;
} }

View File

@ -208,6 +208,8 @@ bool exrailHalSetup() {
#define PICKUP_STASH(id) | FEATURE_STASH #define PICKUP_STASH(id) | FEATURE_STASH
#undef STASH #undef STASH
#define STASH(id) | FEATURE_STASH #define STASH(id) | FEATURE_STASH
#undef BLINK
#define BLINK(vpin,onDuty,offDuty) | FEATURE_BLINK
const byte RMFT2::compileFeatures = 0 const byte RMFT2::compileFeatures = 0
#include "myAutomation.h" #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 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 AUTOMATION(id, description) OPCODE_AUTOMATION, V(id),
#define AUTOSTART OPCODE_AUTOSTART,0,0, #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 BROADCAST(msg) PRINT(msg)
#define CALL(route) OPCODE_CALL,V(route), #define CALL(route) OPCODE_CALL,V(route),
#define CLEAR_STASH(id) OPCODE_CLEAR_STASH,V(id), #define CLEAR_STASH(id) OPCODE_CLEAR_STASH,V(id),