1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-12-23 21:01:25 +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;
#endif
case '/': // implemented in EXRAIL parser
case 'L': // LCC interface implemented in EXRAIL parser
break; // Will <X> if not intercepted by EXRAIL

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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),