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:
parent
c7ed47400d
commit
e69b777a2f
@ -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
|
||||||
|
|
||||||
|
65
EXRAIL2.cpp
65
EXRAIL2.cpp
@ -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;
|
||||||
|
15
EXRAIL2.h
15
EXRAIL2.h
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
Loading…
Reference in New Issue
Block a user