From 4300a3fdac613994c10538b25882b834e2802913 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sun, 6 Feb 2022 13:56:51 +0000 Subject: [PATCH 01/41] UNTESTED SERVO_SIGNAL SERVO_SIGNAL definition in EXRAIL SERVO_SIGNAL(vpin, redpos, amberpos, greenpos) use RED/AMBER/GREEN as for led signals. --- EXRAIL2.cpp | 46 +++++++++++++++++++++++++++++++++------------ EXRAIL2.h | 4 +++- EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 7 +++++-- version.h | 2 +- 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index af6f0a1..9316a78 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -913,23 +913,45 @@ void RMFT2::kill(const FSH * reason, int operand) { /* static */ void RMFT2::doSignal(VPIN id,bool red, bool amber, bool green) { //if (diag) DIAG(F(" dosignal %d"),id); - for (int sigpos=0;;sigpos+=3) { - VPIN redpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos); + for (int sigpos=0;;sigpos+=4) { //if (diag) DIAG(F("red=%d"),redpin); - if (redpin==0) { + VPIN sigid=GETFLASHW(RMFT2::SignalDefinitions+sigpos); + if (sigid==0) { // end of signal list DIAG(F("EXRAIL Signal %d not defined"), id); return; // signal not found } - if (redpin==id) { - VPIN amberpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+1); - VPIN greenpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+2); - //if (diag) DIAG(F("signal %d %d %d"),redpin,amberpin,greenpin); - // If amberpin is zero, synthesise amber from red+green - IODevice::write(redpin,red || (amber && (amberpin==0))); - if (amberpin) IODevice::write(amberpin,amber); - if (greenpin) IODevice::write(greenpin,green || (amber && (amberpin==0))); - return; + // sigid is the signal id used in RED/AMBER/GREEN macro + // for a LED signal it will be same as redpin + // but for a servo signal it will also have SERVER_SIGNAL_FLAG set. + + if ((sigid & ~SERVO_SIGNAL_FLAG)!= id) continue; // keep looking + + // Correct signal definition found, get the rag values + VPIN redpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+1); + VPIN amberpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+2); + VPIN greenpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+3); + //if (diag) DIAG(F("signal %d %d %d"),redpin,amberpin,greenpin); + + if (sigid & SERVO_SIGNAL_FLAG) { + // A servo signal, the pin numbers are actually servo positions + // Note, setting a signal to a zero position has no effect. + int16_t servopos= red? redpin: (green? greenpin : amberpin); + if (servopos!=0) IODevice::writeAnalogue(id,servopos,PCA9685::Bounce); + return; } + + // LED or similar 3 pin signal + // If amberpin is zero, synthesise amber from red+green + if (amber && (amberpin==0)) { + red=true; + green=true; + } + + // set the three pins + if (redpin) IODevice::write(redpin,red); + if (amberpin) IODevice::write(amberpin,amber); + if (greenpin) IODevice::write(greenpin,green); + return; } } diff --git a/EXRAIL2.h b/EXRAIL2.h index b44ada6..0004720 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -93,7 +93,9 @@ class LookList { static void emitTurnoutDescription(Print* stream,int16_t id); static const byte rosterNameCount; static void emitWithrottleRoster(Print * stream); - static const FSH * getRosterFunctions(int16_t cabid); + static const FSH * getRosterFunctions(int16_t cabid); + static const int16_t SERVO_SIGNAL_FLAG=0x4000; + private: static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); static bool parseSlash(Print * stream, byte & paramCount, int16_t p[]) ; diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 852eb70..0ddf9ba 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -97,6 +97,7 @@ #undef SERVO #undef SERVO2 #undef SERVO_TURNOUT +#undef SERVO_SIGNAL #undef SET #undef SETLOCO #undef SIGNAL @@ -185,6 +186,7 @@ #define SERIAL3(msg) #define SERVO(id,position,profile) #define SERVO2(id,position,duration) +#define SERVO_SIGNAL(vpin,redpos,amberpos,greenpos) #define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) #define SET(pin) #define SETLOCO(loco) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 7efb445..ddba9a7 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -146,10 +146,12 @@ const FSH * RMFT2::getRosterFunctions(int16_t cabid) { // Pass 8 Signal definitions #include "EXRAIL2MacroReset.h" #undef SIGNAL -#define SIGNAL(redpin,amberpin,greenpin) redpin,amberpin,greenpin, +#define SIGNAL(redpin,amberpin,greenpin) redpin,redpin,amberpin,greenpin, +#undef SERVO_SIGNAL +#define SERVO_SIGNAL(vpin,redval,amberval,greenval) vpin | RMFT2::SERVO_SIGNAL_FLAG,redval,amberval,greenval, const FLASH int16_t RMFT2::SignalDefinitions[] = { #include "myAutomation.h" - 0,0,0 }; + 0,0,0,0 }; // Last Pass : create main routes table // Only undef the macros, not dummy them. @@ -234,6 +236,7 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = { #define SERIAL3(msg) PRINT(msg) #define SERVO(id,position,profile) OPCODE_SERVO,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(PCA9685::profile),OPCODE_PAD,V(0), #define SERVO2(id,position,ms) OPCODE_SERVO,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(PCA9685::Instant),OPCODE_PAD,V(ms/100L), +#define SERVO_SIGNAL(vpin,redpos,amberpos,greenpos) #define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) OPCODE_SERVOTURNOUT,V(id),OPCODE_PAD,V(pin),OPCODE_PAD,V(activeAngle),OPCODE_PAD,V(inactiveAngle),OPCODE_PAD,V(PCA9685::ProfileType::profile), #define SET(pin) OPCODE_SET,V(pin), #define SETLOCO(loco) OPCODE_SETLOCO,V(loco), diff --git a/version.h b/version.h index c34d926..e387614 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,7 @@ #include "StringFormatter.h" -#define VERSION "3.2.0 rc13" +#define VERSION "4.0.1 ServoSignalTest" // 3.2.0 Major functional and non-functional changes. // New HAL added for I/O (digital and analogue inputs and outputs, servos etc). // Support for MCP23008, MCP23017 and PCF9584 I2C GPIO Extender modules. From 367e400d75f816b7cf52d83269f41e8bd9136140 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 18 Mar 2022 13:46:07 +0000 Subject: [PATCH 02/41] SIGNALH, ATGTE, ATLT UNTESTED --- EXRAIL2.cpp | 25 ++++++++++++++++++++----- EXRAIL2.h | 2 ++ EXRAIL2MacroReset.h | 6 ++++++ EXRAILMacros.h | 5 +++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 870db23..2bb75c5 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -594,6 +594,18 @@ void RMFT2::loop2() { delayMe(50); return; + case OPCODE_ATGTE: // wait for analog sensor>= value + timeoutFlag=false; + if (IODevice::readAnalogue(operand) >= (int)(GET_OPERAND(1))) break; + delayMe(50); + return; + + case OPCODE_ATLT: // wait for analog sensor < value + timeoutFlag=false; + if (IODevice::readAnalogue(operand) < (int)(GET_OPERAND(1))) break; + delayMe(50); + return; + case OPCODE_ATTIMEOUT1: // ATTIMEOUT(vpin,timeout) part 1 timeoutStart=millis(); timeoutFlag=false; @@ -928,9 +940,9 @@ void RMFT2::kill(const FSH * reason, int operand) { } // sigid is the signal id used in RED/AMBER/GREEN macro // for a LED signal it will be same as redpin - // but for a servo signal it will also have SERVER_SIGNAL_FLAG set. + // but for a servo signal it will also have SERVO_SIGNAL_FLAG set. - if ((sigid & ~SERVO_SIGNAL_FLAG)!= id) continue; // keep looking + if ((sigid & ~SERVO_SIGNAL_FLAG & ~ACTIVE_HIGH_SIGNAL_FLAG)!= id) continue; // keep looking // Correct signal definition found, get the rag values VPIN redpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+1); @@ -953,10 +965,13 @@ void RMFT2::kill(const FSH * reason, int operand) { green=true; } + // Manage invert (HIGH on) pins + bool aHigh=sigid & ACTIVE_HIGH_SIGNAL_FLAG; + // set the three pins - if (redpin) IODevice::write(redpin,red); - if (amberpin) IODevice::write(amberpin,amber); - if (greenpin) IODevice::write(greenpin,green); + if (redpin) IODevice::write(redpin,red^aHigh); + if (amberpin) IODevice::write(amberpin,amber^aHigh); + if (greenpin) IODevice::write(greenpin,green^aHigh); return; } } diff --git a/EXRAIL2.h b/EXRAIL2.h index d841323..fb81d44 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -33,6 +33,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_FWD,OPCODE_REV,OPCODE_SPEED,OPCODE_INVERT_DIRECTION, OPCODE_RESERVE,OPCODE_FREE, OPCODE_AT,OPCODE_AFTER,OPCODE_AUTOSTART, + OPCODE_ATGTE,OPCODE_ATLT, OPCODE_ATTIMEOUT1,OPCODE_ATTIMEOUT2,OPCODE_IFTIMEOUT, OPCODE_LATCH,OPCODE_UNLATCH,OPCODE_SET,OPCODE_RESET, OPCODE_IF,OPCODE_IFNOT,OPCODE_ENDIF,OPCODE_IFRANDOM,OPCODE_IFRESERVE, @@ -95,6 +96,7 @@ class LookList { static void emitWithrottleRoster(Print * stream); static const FSH * getRosterFunctions(int16_t cabid); static const int16_t SERVO_SIGNAL_FLAG=0x4000; + static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000; private: static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index aeae7e4..4456679 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -29,6 +29,8 @@ #undef ALIAS #undef AMBER #undef AT +#undef ATGTE +#undef ATLT #undef ATTIMEOUT #undef AUTOMATION #undef AUTOSTART @@ -103,6 +105,7 @@ #undef SET #undef SETLOCO #undef SIGNAL +#undef SIGNALH #undef SPEED #undef START #undef STOP @@ -121,6 +124,8 @@ #define ALIAS(name,value) #define AMBER(signal_id) #define AT(sensor_id) +#define ATGTE(sensor_id,value) +#define ATLT(sensor_id,value) #define ATTIMEOUT(sensor_id,timeout_ms) #define AUTOMATION(id, description) #define AUTOSTART @@ -195,6 +200,7 @@ #define SET(pin) #define SETLOCO(loco) #define SIGNAL(redpin,amberpin,greenpin) +#define SIGNALH(redpin,amberpin,greenpin) #define SPEED(speed) #define START(route) #define STOP diff --git a/EXRAILMacros.h b/EXRAILMacros.h index b4c6406..1f143f0 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -149,6 +149,8 @@ const FSH * RMFT2::getRosterFunctions(int16_t cabid) { #include "EXRAIL2MacroReset.h" #undef SIGNAL #define SIGNAL(redpin,amberpin,greenpin) redpin,redpin,amberpin,greenpin, +#undef SIGNALH +#define SIGNALH(redpin,amberpin,greenpin) redpin | RMFT2::ACTIVE_HIGH_SIGNAL_FLAG,redpin,amberpin,greenpin, #undef SERVO_SIGNAL #define SERVO_SIGNAL(vpin,redval,amberval,greenval) vpin | RMFT2::SERVO_SIGNAL_FLAG,redval,amberval,greenval, const FLASH int16_t RMFT2::SignalDefinitions[] = { @@ -171,6 +173,8 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = { #define ALIAS(name,value) #define AMBER(signal_id) OPCODE_AMBER,V(signal_id), #define AT(sensor_id) OPCODE_AT,V(sensor_id), +#define ATGTE(sensor_id,value) OPCODE_ATGTE,V(sensor_id),OPCODE_PAD,V(value), +#define ATLT(sensor_id,value) OPCODE_ATLT,V(sensor_id),OPCODE_PAD,V(value), #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, @@ -245,6 +249,7 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = { #define SET(pin) OPCODE_SET,V(pin), #define SETLOCO(loco) OPCODE_SETLOCO,V(loco), #define SIGNAL(redpin,amberpin,greenpin) +#define SIGNALH(redpin,amberpin,greenpin) #define SPEED(speed) OPCODE_SPEED,V(speed), #define START(route) OPCODE_START,V(route), #define STOP OPCODE_SPEED,V(0), From 269e1b36ea793344dca9793abebe480b44efbf06 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 21 Mar 2022 16:29:35 +0000 Subject: [PATCH 03/41] Automatic ALIAS(name) and _ in keywords --- DCCEXParser.cpp | 2 +- EXRAIL2MacroReset.h | 2 +- EXRAILMacros.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index f84c123..11a2796 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -145,7 +145,7 @@ int16_t DCCEXParser::splitValues(int16_t result[MAX_COMMAND_PARAMS], const byte runningValue = 16 * runningValue + (hot - 'A' + 10); break; } - if (hot >= 'A' && hot <= 'Z') + if (hot=='_' || (hot >= 'A' && hot <= 'Z')) { // Since JMRI got modified to send keywords in some rare cases, we need this // Super Kluge to turn keywords into a hash value that can be recognised later diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 4456679..7c6bcc9 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -121,7 +121,7 @@ #define ACTIVATE(addr,subaddr) #define ACTIVATEL(addr) #define AFTER(sensor_id) -#define ALIAS(name,value) +#define ALIAS(name,value...) #define AMBER(signal_id) #define AT(sensor_id) #define ATGTE(sensor_id,value) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 1f143f0..e3b0217 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -52,7 +52,7 @@ // Pass 1 Implements aliases #include "EXRAIL2MacroReset.h" #undef ALIAS -#define ALIAS(name,value) const int name=value; +#define ALIAS(name,value...) const int name= 1##value##0 ==10 ? -__COUNTER__ : value##0/10; #include "myAutomation.h" // Pass 2 convert descriptions to withrottle format emitter function @@ -170,7 +170,7 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = { #define ACTIVATE(addr,subaddr) OPCODE_DCCACTIVATE,V(addr<<3 | subaddr<<1 | 1), #define ACTIVATEL(addr) OPCODE_DCCACTIVATE,V((addr+3)<<1 | 1), #define AFTER(sensor_id) OPCODE_AT,V(sensor_id),OPCODE_AFTER,V(sensor_id), -#define ALIAS(name,value) +#define ALIAS(name,value...) #define AMBER(signal_id) OPCODE_AMBER,V(signal_id), #define AT(sensor_id) OPCODE_AT,V(sensor_id), #define ATGTE(sensor_id,value) OPCODE_ATGTE,V(sensor_id),OPCODE_PAD,V(value), From 349f5d5362c5cd0101508e761d236b8178500286 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 24 Mar 2022 10:40:49 +0000 Subject: [PATCH 04/41] EXRAIL FORGET current loco --- EXRAIL2.cpp | 7 +++++++ EXRAIL2.h | 2 +- EXRAIL2MacroReset.h | 4 +++- EXRAILMacros.h | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 2bb75c5..07adedb 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -570,6 +570,13 @@ void RMFT2::loop2() { driveLoco(operand); break; + case OPCODE_FORGET: + if (loco!=0) { + DCC::forgetLoco(loco); + loco=0; + } + break; + case OPCODE_INVERT_DIRECTION: invert= !invert; driveLoco(speedo); diff --git a/EXRAIL2.h b/EXRAIL2.h index fb81d44..dc33b76 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -44,7 +44,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_SERVO,OPCODE_SIGNAL,OPCODE_TURNOUT,OPCODE_WAITFOR, OPCODE_PAD,OPCODE_FOLLOW,OPCODE_CALL,OPCODE_RETURN, OPCODE_JOIN,OPCODE_UNJOIN,OPCODE_READ_LOCO1,OPCODE_READ_LOCO2,OPCODE_POM, - OPCODE_START,OPCODE_SETLOCO,OPCODE_SENDLOCO, + OPCODE_START,OPCODE_SETLOCO,OPCODE_SENDLOCO,OPCODE_FORGET, OPCODE_PAUSE, OPCODE_RESUME,OPCODE_POWEROFF,OPCODE_POWERON, OPCODE_ONCLOSE, OPCODE_ONTHROW, OPCODE_SERVOTURNOUT, OPCODE_PINTURNOUT, OPCODE_PRINT,OPCODE_DCCACTIVATE, diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 7c6bcc9..f87cbf2 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -54,6 +54,7 @@ #undef FOFF #undef FOLLOW #undef FON +#undef FORGET #undef FREE #undef FWD #undef GREEN @@ -148,7 +149,8 @@ #define FADE(pin,value,ms) #define FOFF(func) #define FOLLOW(route) -#define FON(func) +#define FON(func) +#define FORGET #define FREE(blockid) #define FWD(speed) #define GREEN(signal_id) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index e3b0217..1c608e1 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -198,6 +198,7 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = { #define FOFF(func) OPCODE_FOFF,V(func), #define FOLLOW(route) OPCODE_FOLLOW,V(route), #define FON(func) OPCODE_FON,V(func), +#define FORGET OPCODE_FORGET,0,0, #define FREE(blockid) OPCODE_FREE,V(blockid), #define FWD(speed) OPCODE_FWD,V(speed), #define GREEN(signal_id) OPCODE_GREEN,V(signal_id), From 0040f5caf6f2b123027ca2496f42b8b6fb24f8bb Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 24 Mar 2022 11:10:09 +0000 Subject: [PATCH 05/41] EXRAIL --- EXRAIL2.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 07adedb..c5f77c1 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -63,6 +63,7 @@ const int16_t HASH_KEYWORD_UNLATCH=1353; const int16_t HASH_KEYWORD_PAUSE=-4142; const int16_t HASH_KEYWORD_RESUME=27609; const int16_t HASH_KEYWORD_KILL=5218; +const int16_t HASH_KEYWORD_ALL=3457; const int16_t HASH_KEYWORD_ROUTES=-3702; // One instance of RMFT clas is used for each "thread" in the automation. @@ -362,8 +363,13 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { if (paramCount!=2 || p[1]<0 || p[1]>=MAX_FLAGS) return false; switch (p[0]) { - case HASH_KEYWORD_KILL: // Kill taskid + case HASH_KEYWORD_KILL: // Kill taskid|ALL { + if (p[1]==HASH_KEYWORD_ALL) { + while (loopTask) delete loopTask; + return true; + } + RMFT2 * task=loopTask; while(task) { if (task->taskId==p[1]) { From 92c2768c0b4903cbdd30d3be7fa1a3f1ef112463 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 24 Mar 2022 11:56:06 +0000 Subject: [PATCH 06/41] EXRAIL VIRTUAL_TURNOUT --- EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 3 +++ version.h | 3 +++ 3 files changed, 8 insertions(+) diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index f87cbf2..67939db 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -114,6 +114,7 @@ #undef TURNOUT #undef UNJOIN #undef UNLATCH +#undef VIRTUAL_TURNOUT #undef WAITFOR #undef XFOFF #undef XFON @@ -210,6 +211,7 @@ #define TURNOUT(id,addr,subaddr,description...) #define UNJOIN #define UNLATCH(sensor_id) +#define VIRTUAL_TURNOUT(id,description...) #define WAITFOR(pin) #define XFOFF(cab,func) #define XFON(cab,func) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 1c608e1..912c6c4 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -102,6 +102,8 @@ void RMFT2::printMessage(uint16_t id) { #define PIN_TURNOUT(id,pin,description...) case id: desc=F("" description); break; #undef SERVO_TURNOUT #define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) case id: desc=F("" description); break; +#undef VIRTUAL_TURNOUT +#define VIRTUAL_TURNOUT(id,description...) case id: desc=F("" description); break; void RMFT2::emitTurnoutDescription(Print* stream,int16_t turnoutid) { const FSH * desc=F(""); @@ -258,6 +260,7 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = { #define TURNOUT(id,addr,subaddr,description...) OPCODE_TURNOUT,V(id),OPCODE_PAD,V(addr),OPCODE_PAD,V(subaddr), #define UNJOIN OPCODE_UNJOIN,0,0, #define UNLATCH(sensor_id) OPCODE_UNLATCH,V(sensor_id), +#define VIRTUAL_TURNOUT(id,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(0), #define WAITFOR(pin) OPCODE_WAITFOR,V(pin), #define XFOFF(cab,func) OPCODE_XFOFF,V(cab),OPCODE_PAD,V(func), #define XFON(cab,func) OPCODE_XFON,V(cab),OPCODE_PAD,V(func), diff --git a/version.h b/version.h index cb47614..c8e0205 100644 --- a/version.h +++ b/version.h @@ -6,6 +6,9 @@ #define VERSION "4.0.2" // 4.0.2 EXRAIL additions: +// VIRTUAL_TURNOUT +// command to stop all tasks. +// FORGET forgets the current loco in DCC reminder tables. // Servo signals (SERVO_SIGNAL) // High-On signal pins (SIGNALH) // Wait for analog value (ATGTE, ATLT) From 2cd0c169cee0db769f8b15439fc8928ad3bfacc4 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 24 Mar 2022 13:56:01 +0000 Subject: [PATCH 07/41] Cleanup version.h --- version.h | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/version.h b/version.h index c8e0205..72e7d67 100644 --- a/version.h +++ b/version.h @@ -12,21 +12,22 @@ // Servo signals (SERVO_SIGNAL) // High-On signal pins (SIGNALH) // Wait for analog value (ATGTE, ATLT) -// 4.0.1 EXRAIL BROADCAST("msg") +// 4.0.1 Small EXRAIL updates +// EXRAIL BROADCAST("msg") // EXRAIL POWERON // 4.0.0 Major functional and non-functional changes. // Engine Driver "DriveAway" feature enhancement -//.......JMRI feature enhancement. Provides for multiple additional DCC++EX wifi -//.........connections as accessory controllers or CS for a programming track when -//.........motor shield is added +// JMRI feature enhancement. Provides for multiple additional DCC++EX wifi +// connections as accessory controllers or CS for a programming track when +// motor shield is added // New HAL added for I/O (digital and analogue inputs and outputs, servos etc). -// Support for MCP23008, MCP23017 and PCF9584 I2C GPIO Extender modules. -// Support for PCA9685 PWM (servo) control modules. -// Support for analogue inputs on Arduino pins and on ADS111x I2C modules. -// Support for MP3 sound playback via DFPlayer module. -// Support for HC-SR04 Ultrasonic range sensor module. -// Support for VL53L0X Laser range sensor module (Time-Of-Flight). -//.........Added diagnostic command to show configured devices +// Support for MCP23008, MCP23017 and PCF9584 I2C GPIO Extender modules. +// Support for PCA9685 PWM (servo) control modules. +// Support for analogue inputs on Arduino pins and on ADS111x I2C modules. +// Support for MP3 sound playback via DFPlayer module. +// Support for HC-SR04 Ultrasonic range sensor module. +// Support for VL53L0X Laser range sensor module (Time-Of-Flight). +// Added diagnostic command to show configured devices // Native non-blocking I2C drivers for AVR and Nano architectures (fallback // to blocking Wire library for other platforms). // EEPROM layout change - deletes EEPROM contents on first start following upgrade. From 6826e01bd31ed123c62fec46dbcabafd7a884d98 Mon Sep 17 00:00:00 2001 From: Kcsmith0708 Date: Thu, 24 Mar 2022 11:34:11 -0400 Subject: [PATCH 08/41] Update version.h (#223) Rewrite & Updated the 4.0.0 Section --- version.h | 51 ++++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/version.h b/version.h index 72e7d67..9abcc7b 100644 --- a/version.h +++ b/version.h @@ -17,34 +17,39 @@ // EXRAIL POWERON // 4.0.0 Major functional and non-functional changes. // Engine Driver "DriveAway" feature enhancement -// JMRI feature enhancement. Provides for multiple additional DCC++EX wifi -// connections as accessory controllers or CS for a programming track when -// motor shield is added -// New HAL added for I/O (digital and analogue inputs and outputs, servos etc). -// Support for MCP23008, MCP23017 and PCF9584 I2C GPIO Extender modules. -// Support for PCA9685 PWM (servo) control modules. -// Support for analogue inputs on Arduino pins and on ADS111x I2C modules. -// Support for MP3 sound playback via DFPlayer module. -// Support for HC-SR04 Ultrasonic range sensor module. -// Support for VL53L0X Laser range sensor module (Time-Of-Flight). -// Added diagnostic command to show configured devices -// Native non-blocking I2C drivers for AVR and Nano architectures (fallback -// to blocking Wire library for other platforms). -// EEPROM layout change - deletes EEPROM contents on first start following upgrade. -// New EX-RAIL automation capability. -// Turnout class revised to expand turnout capabilities, new commands added. -// Output class now allows ID > 255. -// Configuration options to globally flip polarity of DCC Accessory states when driven -// from command and command. -// Increased use of display for showing loco decoder programming information. +// 'Discovered Server' multicast Dynamic Network Server (mDNS) displays available WiFi connections to a DCC++EX Command Station +// New EX-RAIL "Extended Railroad Automation Instruction Language" automation capability. +// EX-Rail Function commands for creating Automation, Route & Sequence Scripts +// EX-RAIL “ROSTER” Engines Id & Function key layout on Engine Driver or WiThrottle +// EX-RAIL DCC++EX Commands to Control EX-RAIL via JMRI Send pane and IDE Serial monitors +// New JMRI feature enhancements; +// Reads DCC++EX EEPROM & automatically uploades any Signals, DCC Turnouts, Servo Turnouts, Vpin Turnouts , & Output pane +// Turnout class revised to expand turnout capabilities, new commands added. +// Provides for multiple additional DCC++EX WiFi connections as accessory controllers or CS for a programming track when Motor Shields are added +// Supports Multiple Command Station connections and individual tracking of Send DCC++ Command panes and DCC++ Traffic Monitor panes +// New HAL added for I/O (digital and analogue inputs and outputs, servos etc) +// Automatically detects & connects to supported devices included in your config.h file +// Support for MCP23008, MCP23017 and PCF9584 I2C GPIO Extender modules. +// Support for PCA9685 PWM (servo) control modules. +// Support for analogue inputs on Arduino pins and on ADS111x I2C modules. +// Support for MP3 sound playback via DFPlayer module. +// Support for HC-SR04 Ultrasonic range sensor module. +// Support for VL53L0X Laser range sensor module (Time-Of-Flight). +// Added diagnostic command to show configured devices +// New Processor Support added +// Compiles on Nano Every and Teensy +// Native non-blocking I2C drivers for AVR and Nano architectures (fallback to blocking Wire library for other platforms). // Can disable EEPROM code +// EEPROM layout change - deletes EEPROM contents on first start following upgrade. +// Output class now allows ID > 255. +// Configuration options to globally flip polarity of DCC Accessory states when driven from command and command. +// Increased use of display for showing loco decoder programming information. // Can define border between long and short addresses // Turnout and accessory states (thrown/closed = 0/1 or 1/0) can be set to match RCN-213 // Bugfix: one-off error in CIPSEND drop -// Compiles on Nano Every // Bugfix: disgnostic display of ack pulses >32kus -//.......Bugfix: Current read from wrong ADC during interrupt -// ... +// Bugfix: Current read from wrong ADC during interrupt +// 3.2.0 Development Release Includes all of 3.1.1 thru 3.1.7 enhancements // 3.1.7 Bugfix: Unknown locos should have speed forward // 3.1.6 Make output ID two bytes and guess format/size of registered outputs found in EEPROM // 3.1.5 Fix LCD corruption on power-up From 8085d03d65cf6e3aeb102c96fad1de09b2c259b3 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 31 Mar 2022 10:11:34 +0100 Subject: [PATCH 09/41] fix --- EXRAIL2.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index c5f77c1..669a632 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -357,27 +357,27 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { default: break; } - + + // check KILL ALL here, otherwise the next validation confuses ALL with a flag + if (p[0]==HASH_KEYWORD_KILL && p[1]==HASH_KEYWORD_ALL) { + while (loopTask) delete loopTask; // destructor changes loopTask + return true; + } + // all other / commands take 1 parameter 0 to MAX_FLAGS-1 - if (paramCount!=2 || p[1]<0 || p[1]>=MAX_FLAGS) return false; switch (p[0]) { case HASH_KEYWORD_KILL: // Kill taskid|ALL { - if (p[1]==HASH_KEYWORD_ALL) { - while (loopTask) delete loopTask; - return true; - } - RMFT2 * task=loopTask; while(task) { - if (task->taskId==p[1]) { - delete task; - return true; - } - task=task->next; - if (task==loopTask) break; + if (task->taskId==p[1]) { + delete task; + return true; + } + task=task->next; + if (task==loopTask) break; } } return false; From 566f8ada23430eb758f8a51bca955444effcb89e Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 31 Mar 2022 15:46:50 +0100 Subject: [PATCH 10/41] Incoming LCN turnout throw. --- LCN.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/LCN.cpp b/LCN.cpp index 16b3f3f..efb49ff 100644 --- a/LCN.cpp +++ b/LCN.cpp @@ -50,7 +50,11 @@ void LCN::loop() { if (Diag::LCN) DIAG(F("LCN IN %d%c"),id,(char)ch); if (!Turnout::exists(id)) LCNTurnout::create(id); Turnout::setClosedStateOnly(id,ch=='t'); - Turnout::turnoutlistHash++; // signals ED update of turnout data + id = 0; + } + else if (ch == 'y' || ch == 'Y') { // Turnout opcodes + if (Diag::LCN) DIAG(F("LCN IN %d%c"),id,(char)ch); + Turnout::setClosed(id,ch=='y'); id = 0; } else if (ch == 'S' || ch == 's') { From 731d838e8305c0a9c0924b1b5f631a1648b229e5 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 31 Mar 2022 21:52:43 +0100 Subject: [PATCH 11/41] KILLALL macro and DIAGNOSTIC messages when KILL command used. --- EXRAIL2.cpp | 10 +++++++--- EXRAIL2.h | 2 +- EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 1 + 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 669a632..3c7af23 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -360,7 +360,7 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { // check KILL ALL here, otherwise the next validation confuses ALL with a flag if (p[0]==HASH_KEYWORD_KILL && p[1]==HASH_KEYWORD_ALL) { - while (loopTask) delete loopTask; // destructor changes loopTask + while (loopTask) loopTask->kill(F("KILL ALL")); // destructor changes loopTask return true; } @@ -373,7 +373,7 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { RMFT2 * task=loopTask; while(task) { if (task->taskId==p[1]) { - delete task; + task->kill(F("KILL")); return true; } task=task->next; @@ -812,7 +812,11 @@ void RMFT2::loop2() { case OPCODE_ENDEXRAIL: kill(); return; - + + case OPCODE_KILLALL: + while(loopTask) loopTask->kill(F("KILLALL")); + return; + case OPCODE_JOIN: DCCWaveform::mainTrack.setPowerMode(POWERMODE::ON); DCCWaveform::progTrack.setPowerMode(POWERMODE::ON); diff --git a/EXRAIL2.h b/EXRAIL2.h index dc33b76..6cc0243 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -49,7 +49,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ONCLOSE, OPCODE_ONTHROW, OPCODE_SERVOTURNOUT, OPCODE_PINTURNOUT, OPCODE_PRINT,OPCODE_DCCACTIVATE, OPCODE_ONACTIVATE,OPCODE_ONDEACTIVATE,OPCODE_IFGTE,OPCODE_IFLT, - OPCODE_ROSTER, + OPCODE_ROSTER,OPCODE_KILLALL, OPCODE_ROUTE,OPCODE_AUTOMATION,OPCODE_SEQUENCE,OPCODE_ENDTASK,OPCODE_ENDEXRAIL }; diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 67939db..06318c7 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -69,6 +69,7 @@ #undef IFTIMEOUT #undef INVERT_DIRECTION #undef JOIN +#undef KILLALL #undef LATCH #undef LCD #undef LCN @@ -166,6 +167,7 @@ #define IFTIMEOUT #define INVERT_DIRECTION #define JOIN +#define KILLALL #define LATCH(sensor_id) #define LCD(row,msg) #define LCN(msg) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 912c6c4..07b75d2 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -215,6 +215,7 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = { #define IFTIMEOUT OPCODE_IFTIMEOUT,0,0, #define INVERT_DIRECTION OPCODE_INVERT_DIRECTION,0,0, #define JOIN OPCODE_JOIN,0,0, +#define KILLALL OPCODE_KILLALL,0,0, #define LATCH(sensor_id) OPCODE_LATCH,V(sensor_id), #define LCD(id,msg) PRINT(msg) #define LCN(msg) PRINT(msg) From 7c1c6dafa1351923f18593a3e79baa370de26823 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 31 Mar 2022 22:04:40 +0100 Subject: [PATCH 12/41] EXRAIL PARSE --- EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 3 +++ version.h | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 06318c7..e28452a 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -79,6 +79,7 @@ #undef ONDEACTIVATEL #undef ONCLOSE #undef ONTHROW +#undef PARSE #undef PAUSE #undef PIN_TURNOUT #undef PRINT @@ -180,6 +181,7 @@ #define PAUSE #define PIN_TURNOUT(id,pin,description...) #define PRINT(msg) +#define PARSE(msg) #define POM(cv,value) #define POWEROFF #define POWERON diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 07b75d2..e0b078c 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -71,6 +71,8 @@ void RMFT2::emitWithrottleDescriptions(Print * stream) { const int StringMacroTracker1=__COUNTER__; #undef BROADCAST #define BROADCAST(msg) case (__COUNTER__ - StringMacroTracker1) : CommandDistributor::broadcastText(F(msg));break; +#undef PARSE +#define PARSE(msg) case (__COUNTER__ - StringMacroTracker1) : DCCEXParser::parse(F(msg));break; #undef PRINT #define PRINT(msg) case (__COUNTER__ - StringMacroTracker1) : printMessage2(F(msg));break; #undef LCN @@ -231,6 +233,7 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = { #define POWEROFF OPCODE_POWEROFF,0,0, #define POWERON OPCODE_POWERON,0,0, #define PRINT(msg) OPCODE_PRINT,V(__COUNTER__ - StringMacroTracker2), +#define PARSE(msg) PRINT(msg) #define READ_LOCO OPCODE_READ_LOCO1,0,0,OPCODE_READ_LOCO2,0,0, #define RED(signal_id) OPCODE_RED,V(signal_id), #define RESERVE(blockid) OPCODE_RESERVE,V(blockid), diff --git a/version.h b/version.h index 9abcc7b..563b10d 100644 --- a/version.h +++ b/version.h @@ -6,8 +6,9 @@ #define VERSION "4.0.2" // 4.0.2 EXRAIL additions: +// PARSE <> commands // VIRTUAL_TURNOUT -// command to stop all tasks. +// and KILLALL command to stop all tasks. // FORGET forgets the current loco in DCC reminder tables. // Servo signals (SERVO_SIGNAL) // High-On signal pins (SIGNALH) From 4c8b7f851754c5a648fabe0bd1d58ff7a62aa286 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sun, 3 Apr 2022 11:19:04 +0100 Subject: [PATCH 13/41] Rebuild throttle info getters UNTESTED... create different methods to obtain throttle info without being withrottle specific. Also implements turnout description of "*" as hidden. --- EXRAIL2.cpp | 20 ++--------- EXRAIL2.h | 19 ++++++----- EXRAILMacros.h | 93 ++++++++++++++++++++++++++++++-------------------- WiThrottle.cpp | 42 +++++++++++++++++------ 4 files changed, 101 insertions(+), 73 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 3c7af23..1a4f536 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -40,7 +40,6 @@ T2. Extend to >64k */ - #include #include "EXRAIL2.h" #include "DCC.h" @@ -347,13 +346,6 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { } return true; - case HASH_KEYWORD_ROUTES: // JMRI withrottle support - if (paramCount>1) return false; - StringFormatter::send(stream,F("")); - return true; - default: break; } @@ -408,11 +400,7 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { // Automations are given a state to set the button to "handoff" which implies // handing over the loco to the automation. // Routes are given "Set" buttons and do not cause the loco to be handed over. -void RMFT2::emitWithrottleRouteList(Print* stream) { - StringFormatter::send(stream,F("PRT]\\[Routes}|{Route]\\[Set}|{2]\\[Handoff}|{4\nPRL")); - emitWithrottleDescriptions(stream); - StringFormatter::send(stream,F("\n")); -} + RMFT2::RMFT2(int progCtr) { @@ -1037,8 +1025,4 @@ void RMFT2::printMessage2(const FSH * msg) { DIAG(F("EXRAIL(%d) %S"),loco,msg); } -// This is called by emitRouteDescriptions to emit a withrottle description for a route or autoomation. -void RMFT2::emitRouteDescription(Print * stream, char type, int id, const FSH * description) { - StringFormatter::send(stream,F("]\\[%c%d}|{%S}|{%c"), - type,id,description, type=='R'?'2':'4'); -} + diff --git a/EXRAIL2.h b/EXRAIL2.h index 6cc0243..97e0d98 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -87,17 +87,21 @@ class LookList { RMFT2(int route, uint16_t cab); ~RMFT2(); static void readLocoCallback(int16_t cv); - static void emitWithrottleRouteList(Print* stream); static void createNewTask(int route, uint16_t cab); static void turnoutEvent(int16_t id, bool closed); static void activateEvent(int16_t addr, bool active); - static void emitTurnoutDescription(Print* stream,int16_t id); - static const byte rosterNameCount; - static void emitWithrottleRoster(Print * stream); - static const FSH * getRosterFunctions(int16_t cabid); static const int16_t SERVO_SIGNAL_FLAG=0x4000; static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000; - + + // Throttle Info Access functions built by exrail macros + static const byte rosterNameCount; + static const int16_t FLASH routeIdList[]; + static const int16_t FLASH rosterIdList[]; + static const FSH * getRouteDescription(int16_t id); + static const FSH * getTurnoutDescription(int16_t id); + static const FSH * getRosterName(int16_t id); + static const FSH * getRosterFunctions(int16_t id); + private: static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); static bool parseSlash(Print * stream, byte & paramCount, int16_t p[]) ; @@ -106,8 +110,6 @@ private: static bool getFlag(VPIN id,byte mask); static int16_t progtrackLocoId; static void doSignal(VPIN id,bool red, bool amber, bool green); - static void emitRouteDescription(Print * stream, char type, int id, const FSH * description); - static void emitWithrottleDescriptions(Print * stream); static RMFT2 * loopTask; static RMFT2 * pausingTask; @@ -132,6 +134,7 @@ private: static LookList * onActivateLookup; static LookList * onDeactivateLookup; + // Local variables - exist for each instance/task RMFT2 *next; // loop chain int progCounter; // Byte offset of next route opcode in ROUTES table diff --git a/EXRAILMacros.h b/EXRAILMacros.h index e0b078c..60fd557 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -49,24 +49,38 @@ // CAUTION: The macros below are multiple passed over myAutomation.h +#define O_DESC(id, desc) case id: return ("" desc)[0]?F("" desc):NULL; + // Pass 1 Implements aliases #include "EXRAIL2MacroReset.h" #undef ALIAS #define ALIAS(name,value...) const int name= 1##value##0 ==10 ? -__COUNTER__ : value##0/10; #include "myAutomation.h" -// Pass 2 convert descriptions to withrottle format emitter function +// Pass 2 create throttle route list #include "EXRAIL2MacroReset.h" #undef ROUTE -#define ROUTE(id, description) emitRouteDescription(stream,'R',id,F(description)); +#define ROUTE(id, description) id, #undef AUTOMATION -#define AUTOMATION(id, description) emitRouteDescription(stream,'A',id,F(description)); -void RMFT2::emitWithrottleDescriptions(Print * stream) { - (void)stream; +#define AUTOMATION(id, description) -id, +const int16_t FLASH RMFT2::routeIdList[]= { #include "myAutomation.h" + 0}; + +// Pass 3 Create route descriptions: +#undef ROUTE +#define ROUTE(id, description) case id: return F(description); +#undef AUTOMATION +#define AUTOMATION(id, description) case id: return F(description); +const FSH * RMFT2::getRouteDescription(int16_t id) { + switch(id) { + #include "myAutomation.h" + default: break; + } + return NULL; } -// Pass 3... Create Text sending functions +// Pass 4... Create Text sending functions #include "EXRAIL2MacroReset.h" const int StringMacroTracker1=__COUNTER__; #undef BROADCAST @@ -96,58 +110,63 @@ void RMFT2::printMessage(uint16_t id) { } -// Pass 4: Turnout descriptions (optional) +// Pass 5: Turnout descriptions (optional) #include "EXRAIL2MacroReset.h" #undef TURNOUT -#define TURNOUT(id,addr,subaddr,description...) case id: desc=F("" description); break; +#define TURNOUT(id,addr,subaddr,description...) O_DESC(id,description) #undef PIN_TURNOUT -#define PIN_TURNOUT(id,pin,description...) case id: desc=F("" description); break; +#define PIN_TURNOUT(id,pin,description...) O_DESC(id,description) #undef SERVO_TURNOUT -#define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) case id: desc=F("" description); break; +#define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) O_DESC(id,description) #undef VIRTUAL_TURNOUT -#define VIRTUAL_TURNOUT(id,description...) case id: desc=F("" description); break; +#define VIRTUAL_TURNOUT(id,description...) O_DESC(id,description) -void RMFT2::emitTurnoutDescription(Print* stream,int16_t turnoutid) { - const FSH * desc=F(""); +const FSH * RMFT2::getTurnoutDescription(int16_t turnoutid) { switch (turnoutid) { #include "myAutomation.h" - default: break; + default:break; } - if (GETFLASH(desc)=='\0') desc=F("%d"); - StringFormatter::send(stream,desc,turnoutid); + return NULL; } -// Pass 5: Roster names (count) +// Pass 6: Roster IDs (count) #include "EXRAIL2MacroReset.h" #undef ROSTER #define ROSTER(cabid,name,funcmap...) +1 - const byte RMFT2::rosterNameCount=0 - #include "myAutomation.h" - ; - -// Pass 6: Roster names emitter + #include "myAutomation.h" + ; + +// Pass 6: Roster IDs #include "EXRAIL2MacroReset.h" #undef ROSTER -#define ROSTER(cabid,name,funcmap...) StringFormatter::send(stream,(FSH *)format,F(name),cabid,cabid<128?'S':'L'); -void RMFT2::emitWithrottleRoster(Print * stream) { - static const char format[] FLASH ="]\\[%S}|{%d}|{%c"; - (void)format; - StringFormatter::send(stream,F("RL%d"), rosterNameCount); - #include "myAutomation.h" - stream->write('\n'); -} +#define ROSTER(cabid,name,funcmap...) cabid, +const int16_t FLASH RMFT2::rosterIdList[]={ + #include "myAutomation.h" + 0}; -// Pass 7: functions getter +// Pass 7: Roster names getter #include "EXRAIL2MacroReset.h" #undef ROSTER -#define ROSTER(cabid,name,funcmap...) case cabid: return F("" funcmap); -const FSH * RMFT2::getRosterFunctions(int16_t cabid) { - switch(cabid) { +#define ROSTER(cabid,name,funcmap...) case cabid: return F(name); +const FSH * RMFT2::getRosterName(int16_t id) { + switch(id) { #include "myAutomation.h" - default: return NULL; - } -} + default: return NULL; + } + return NULL; +} + +// Pass to get roster functions +#undef ROSTER +#define ROSTER(cabid,name,funcmap...) O_DESC(cabid,funcmap) +const FSH * RMFT2::getRosterFunctions(int16_t id) { + switch(id) { + #include "myAutomation.h" + default: break; + } + return NULL; +} // Pass 8 Signal definitions #include "EXRAIL2MacroReset.h" diff --git a/WiThrottle.cpp b/WiThrottle.cpp index d91745c..72ee75a 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -120,13 +120,15 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { StringFormatter::send(stream,F("PTL")); for(Turnout *tt=Turnout::first();tt!=NULL;tt=tt->next()){ int id=tt->getId(); - StringFormatter::send(stream,F("]\\[%d}|{"), id); -#ifdef EXRAIL_ACTIVE - RMFT2::emitTurnoutDescription(stream,id); -#else - StringFormatter::send(stream,F("%d"), id); -#endif - StringFormatter::send(stream,F("}|{%c"), Turnout::isClosed(id)?'2':'4'); + const FSH * tdesc=NULL; + #ifdef EXRAIL_ACTIVE + tdesc=RMFT2::getTurnoutDescription(id); + #endif + char tchar=Turnout::isClosed(id)?'2':'4'; + if (tdesc==NULL) // turnout with no description + StringFormatter::send(stream,F("]\\[%d}|{T%d}|{T%c"), id,id,tchar); + else if (GETFLASH(tdesc)!='*') // ignore hidden turnouts + StringFormatter::send(stream,F("]\\[%d}|{%S}|{%c"), id,tdesc,tchar); } StringFormatter::send(stream,F("\n")); turnoutListHash = Turnout::turnoutlistHash; // keep a copy of hash for later comparison @@ -136,7 +138,17 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { // Send EX-RAIL routes list if not already sent (but not at same time as turnouts above) exRailSent=true; #ifdef EXRAIL_ACTIVE - RMFT2::emitWithrottleRouteList(stream); + StringFormatter::send(stream,F("PRT]\\[Routes}|{Route]\\[Set}|{2]\\[Handoff}|{4\nPRL")); + for (int ix=0;;ix+=2) { + int16_t id=GETFLASHW(RMFT2::routeIdList+ix); + if (id==0) break; + bool isRoute=id<0; + if (isRoute) id=-id; + const FSH * desc=RMFT2::getRouteDescription(id); + StringFormatter::send(stream,F("]\\[%c%d}|{%S}|{%c"), + isRoute?'R':'A',id,desc, isRoute?'2':'4'); + } + StringFormatter::send(stream,F("\n")); #endif // allow heartbeat to slow down once all metadata sent StringFormatter::send(stream,F("*%d\n"),HEARTBEAT_SECONDS); @@ -205,9 +217,19 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { StringFormatter::send(stream,F("HtDCC-EX v%S, %S, %S, %S\n"), F(VERSION), F(ARDUINO_TYPE), DCC::getMotorShieldName(), F(GITHUB_SHA)); StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[THROW}|{2]\\[CLOSE}|{4\n")); StringFormatter::send(stream,F("PPA%x\n"),DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON); + + // Send the roster #ifdef EXRAIL_ACTIVE - RMFT2::emitWithrottleRoster(stream); -#endif + StringFormatter::send(stream,F("RL%d"), RMFT2::rosterNameCount); + for (int16_t r=0;rwrite('\n'); // end roster +#endif + + // set heartbeat to 1 second because we need to sync the metadata StringFormatter::send(stream,F("*1\n")); initSent = true; From 5846e0fe2384f159927fcfc691ddd3839188ca4a Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 8 Apr 2022 11:41:50 +0100 Subject: [PATCH 14/41] J command parsing JA JR JT commands parsed EXRAIL sets hidden turnout state HIDDEN description macro Turnouts hidden flag bit UNO seems OK, MEGA UNTESTED --- DCCEXParser.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++++-- DCCEXParser.h | 1 + EXRAIL2.cpp | 10 +++++--- EXRAIL2.h | 3 ++- EXRAILMacros.h | 4 +++ Turnouts.h | 12 ++++++--- 6 files changed, 86 insertions(+), 10 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 11a2796..bc2e759 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -37,6 +37,7 @@ #include "CommandDistributor.h" #include "EEStore.h" #include "DIAG.h" +#include "EXRAIL2.h" #include //////////////////////////////////////////////////////////////////////////////// @@ -74,8 +75,10 @@ const int16_t HASH_KEYWORD_SPEED28 = -17064; const int16_t HASH_KEYWORD_SPEED128 = 25816; const int16_t HASH_KEYWORD_SERVO=27709; const int16_t HASH_KEYWORD_VPIN=-415; -const int16_t HASH_KEYWORD_C=67; -const int16_t HASH_KEYWORD_T=84; +const int16_t HASH_KEYWORD_A='A'; +const int16_t HASH_KEYWORD_C='C'; +const int16_t HASH_KEYWORD_R='R'; +const int16_t HASH_KEYWORD_T='T'; const int16_t HASH_KEYWORD_LCN = 15137; const int16_t HASH_KEYWORD_HAL = 10853; const int16_t HASH_KEYWORD_SHOW = -21309; @@ -509,6 +512,57 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) } break; + case 'J' : // throttle info access + { + if ((params<1) | (params>2)) break; // + int16_t id=(params==2)?p[1]:0; + switch(p[0]) { + case HASH_KEYWORD_A: // returns automations/routes + StringFormatter::send(stream, F("\n")); + return; + case HASH_KEYWORD_R: // returns rosters + StringFormatter::send(stream, F("\n")); + return; + case HASH_KEYWORD_T: // returns turnout list + StringFormatter::send(stream, F(" + for ( Turnout * t=Turnout::first(); t; t=t->next()) { + if (t->isHidden()) continue; + StringFormatter::send(stream, F(" %c%d"), + t->isThrown()?'-':' ',t->getId()); + } + } + else { // + Turnout * t=Turnout::get(id); + if (t && !t->isHidden()) + StringFormatter::send(stream, F(" %d \"%S\""), + id, +#ifdef EXRAIL_ACTIVE + RMFT2::getTurnoutDescription(id) +#else + F("") +#endif + ); + } + StringFormatter::send(stream, F(">\n")); + return; + default: break; + } // switch(p[1]) + break; // case J + } + default: //anything else will diagnose and drop out to DIAG(F("Opcode=%c params=%d"), opcode, params); for (int i = 0; i < params; i++) @@ -521,6 +575,14 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) StringFormatter::send(stream, F("\n")); } +void DCCEXParser::sendFlashList(Print * stream,const int16_t flashList[]) { + for (int16_t i=0;;i+=2) { + int16_t value=GETFLASHW(flashList+i); + if (value==0) return; + StringFormatter::send(stream,F(" %d"),value); + } +} + bool DCCEXParser::parseZ(Print *stream, int16_t params, int16_t p[]) { diff --git a/DCCEXParser.h b/DCCEXParser.h index 5761eb0..227e3f0 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -70,6 +70,7 @@ struct DCCEXParser static FILTER_CALLBACK filterRMFTCallback; static AT_COMMAND_CALLBACK atCommandCallback; static void funcmap(int16_t cab, byte value, byte fstart, byte fstop); + static void sendFlashList(Print * stream,const int16_t flashList[]); }; diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 1a4f536..583bbd9 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -196,7 +196,7 @@ int16_t LookList::find(int16_t value) { VPIN id=operand; int addr=GET_OPERAND(1); byte subAddr=GET_OPERAND(2); - DCCTurnout::create(id,addr,subAddr); + setTurnoutHiddenState(DCCTurnout::create(id,addr,subAddr)); break; } @@ -206,14 +206,14 @@ int16_t LookList::find(int16_t value) { int activeAngle=GET_OPERAND(2); int inactiveAngle=GET_OPERAND(3); int profile=GET_OPERAND(4); - ServoTurnout::create(id,pin,activeAngle,inactiveAngle,profile); + setTurnoutHiddenState(ServoTurnout::create(id,pin,activeAngle,inactiveAngle,profile)); break; } case OPCODE_PINTURNOUT: { VPIN id=operand; VPIN pin=GET_OPERAND(1); - VpinTurnout::create(id,pin); + setTurnoutHiddenState(VpinTurnout::create(id,pin)); break; } @@ -257,6 +257,10 @@ int16_t LookList::find(int16_t value) { new RMFT2(0); // add the startup route } +void RMFT2::setTurnoutHiddenState(Turnout * t) { + t->setHidden(GETFLASH(getTurnoutDescription(t->getId()))==0x01); +} + // This filter intercepts <> commands to do the following: // - Implement RMFT specific commands/diagnostics // - Reject/modify JMRI commands that would interfere with RMFT processing diff --git a/EXRAIL2.h b/EXRAIL2.h index 97e0d98..2d3807b 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -22,6 +22,7 @@ #define EXRAIL2_H #include "FSH.h" #include "IODevice.h" +#include "Turnouts.h" // The following are the operation codes (or instructions) for a kind of virtual machine. // Each instruction is normally 3 bytes long with an operation code followed by a parameter. @@ -110,7 +111,7 @@ private: static bool getFlag(VPIN id,byte mask); static int16_t progtrackLocoId; static void doSignal(VPIN id,bool red, bool amber, bool green); - + static void setTurnoutHiddenState(Turnout * t); static RMFT2 * loopTask; static RMFT2 * pausingTask; void delayMe(long millisecs); diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 60fd557..07405fe 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -49,7 +49,11 @@ // CAUTION: The macros below are multiple passed over myAutomation.h + +// helper macro for turnout descriptions, creates NULL for missing description #define O_DESC(id, desc) case id: return ("" desc)[0]?F("" desc):NULL; +// helper macro for turnout description as HIDDEN +#define HIDDEN "\0x01" // Pass 1 Implements aliases #include "EXRAIL2MacroReset.h" diff --git a/Turnouts.h b/Turnouts.h index e394c85..3d1f0bc 100644 --- a/Turnouts.h +++ b/Turnouts.h @@ -3,7 +3,7 @@ * © 2021 M Steve Todd * © 2021 Fred Decker * © 2020-2021 Harald Barth - * © 2020-2021 Chris Harlow + * © 2020-2022 Chris Harlow * © 2013-2016 Gregg E. Berman * All rights reserved. * @@ -61,7 +61,8 @@ protected: struct { bool closed : 1; bool _rfu: 2; - uint8_t turnoutType : 5; + bool hidden: 1; + uint8_t turnoutType : 4; }; uint8_t flags; }; @@ -83,6 +84,7 @@ protected: _turnoutData.id = id; _turnoutData.turnoutType = turnoutType; _turnoutData.closed = closed; + _turnoutData.hidden=false; add(this); } @@ -104,11 +106,11 @@ protected: * Static functions */ - static Turnout *get(uint16_t id); static void add(Turnout *tt); public: + static Turnout *get(uint16_t id); /* * Static data */ @@ -120,6 +122,8 @@ public: */ inline bool isClosed() { return _turnoutData.closed; }; inline bool isThrown() { return !_turnoutData.closed; } + inline bool isHidden() { return _turnoutData.hidden; } + inline void setHidden(bool h) { _turnoutData.hidden=h; } inline bool isType(uint8_t type) { return _turnoutData.turnoutType == type; } inline uint16_t getId() { return _turnoutData.id; } inline Turnout *next() { return _nextTurnout; } @@ -169,7 +173,7 @@ public: #endif static void printAll(Print *stream) { for (Turnout *tt = _firstTurnout; tt != 0; tt = tt->_nextTurnout) - StringFormatter::send(stream, F("\n"),tt->getId(), tt->isThrown()); + if (!tt->isHidden()) StringFormatter::send(stream, F("\n"),tt->getId(), tt->isThrown()); } From bfb88bb30ae75ce9bad5af129745f654b5ed0142 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 8 Apr 2022 16:13:15 +0100 Subject: [PATCH 15/41] Assist notes draft & syntax tweaks --- DCCEXParser.cpp | 27 +++++++++++----- EXRAIL2.cpp | 15 ++++++++- EXRAIL2.h | 2 ++ EXRAILMacros.h | 9 ++++-- Release_Notes/ThrottleAssists.md | 55 ++++++++++++++++++++++++++++++++ WiThrottle.cpp | 17 +++++----- 6 files changed, 106 insertions(+), 19 deletions(-) create mode 100644 Release_Notes/ThrottleAssists.md diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index bc2e759..b0e9801 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -519,11 +519,23 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) switch(p[0]) { case HASH_KEYWORD_A: // returns automations/routes StringFormatter::send(stream, F(" #ifdef EXRAIL_ACTIVE - if (params==1) sendFlashList(stream,RMFT2::routeIdList); - else StringFormatter::send(stream,F(" %d \"%S\""), - id, RMFT2::getRouteDescription(id)); -#endif + sendFlashList(stream,RMFT2::automationIdList); + sendFlashList(stream,RMFT2::routeIdList); +#endif + } + else { // + StringFormatter::send(stream,F(" %d %c \"%S\""), + id, +#ifdef EXRAIL_ACTIVE + RMFT2::getRouteType(id), // A/R + RMFT2::getRouteDescription(id) +#else + 'X',F("") +#endif + ); + } StringFormatter::send(stream, F(">\n")); return; case HASH_KEYWORD_R: // returns rosters @@ -540,15 +552,14 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) if (params==1) { // for ( Turnout * t=Turnout::first(); t; t=t->next()) { if (t->isHidden()) continue; - StringFormatter::send(stream, F(" %c%d"), - t->isThrown()?'-':' ',t->getId()); + StringFormatter::send(stream, F(" %d"),t->getId()); } } else { // Turnout * t=Turnout::get(id); if (t && !t->isHidden()) - StringFormatter::send(stream, F(" %d \"%S\""), - id, + StringFormatter::send(stream, F(" %d %c \"%S\""), + id,t->isThrown()?'T':'C', #ifdef EXRAIL_ACTIVE RMFT2::getTurnoutDescription(id) #else diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 583bbd9..55be05a 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -260,7 +260,20 @@ int16_t LookList::find(int16_t value) { void RMFT2::setTurnoutHiddenState(Turnout * t) { t->setHidden(GETFLASH(getTurnoutDescription(t->getId()))==0x01); } - + +char RMFT2::getRouteType(int16_t id) { + for (int16_t i=0;;i+=2) { + int16_t rid= GETFLASHW(routeIdList+i); + if (rid==id) return 'R'; + if (rid==0) break; + } + for (int16_t i=0;;i+=2) { + int16_t rid= GETFLASHW(automationIdList+i); + if (rid==id) return 'A'; + if (rid==0) break; + } + return 'X'; +} // This filter intercepts <> commands to do the following: // - Implement RMFT specific commands/diagnostics // - Reject/modify JMRI commands that would interfere with RMFT processing diff --git a/EXRAIL2.h b/EXRAIL2.h index 2d3807b..616b05d 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -97,8 +97,10 @@ class LookList { // Throttle Info Access functions built by exrail macros static const byte rosterNameCount; static const int16_t FLASH routeIdList[]; + static const int16_t FLASH automationIdList[]; static const int16_t FLASH rosterIdList[]; static const FSH * getRouteDescription(int16_t id); + static char getRouteType(int16_t id); static const FSH * getTurnoutDescription(int16_t id); static const FSH * getRosterName(int16_t id); static const FSH * getRosterFunctions(int16_t id); diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 07405fe..c11bd35 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -65,11 +65,16 @@ #include "EXRAIL2MacroReset.h" #undef ROUTE #define ROUTE(id, description) id, -#undef AUTOMATION -#define AUTOMATION(id, description) -id, const int16_t FLASH RMFT2::routeIdList[]= { #include "myAutomation.h" 0}; +// Pass 2a create throttle automation list +#include "EXRAIL2MacroReset.h" +#undef AUTOMATION +#define AUTOMATION(id, description) id, +const int16_t FLASH RMFT2::automationIdList[]= { + #include "myAutomation.h" + 0}; // Pass 3 Create route descriptions: #undef ROUTE diff --git a/Release_Notes/ThrottleAssists.md b/Release_Notes/ThrottleAssists.md new file mode 100644 index 0000000..41d0250 --- /dev/null +++ b/Release_Notes/ThrottleAssists.md @@ -0,0 +1,55 @@ +Throttle Assist updates for versiuon 4.? + +Chris Harlow April 2022 + +There are a number of additional throttle information commands that have been implemented to assist throttle authors to obtain information from the Command Station in order to implement turnout, route/automation and roster features which are already found in the Withrottle implementations. +These commands are new and not overlapped with the existing commands which are probabaly due to be obsoleted as they are over complex and unfit for purpose. + +Turnouts: + +The conventional turnout definition commands and the `````` responses do not contain information about the turnout description which may have been provided in an EXRAIL script. A turnout description is much more user friendly than T123 and having a list helps the throttle UI build a suitable set of buttons. + +`````` command returns a list of turnout ids. The throttle should be uninterested in the turnout technology used but needs to know the ids it can throw/close and monitor the current state. +e.g. response `````` + +````` requests info on turnout 17. +e.g. response `````` or `````` +(T=thrown, C=closed) +or `````` indicating turnout description not given. +or `````` indicating turnout unknown. + +Note: It is still the throttles responsibility to monitor the status broadcasts. + (TBD I'm thinking that the existing broadcast is messy and needs cleaning up) + However, I'm not keen on dynamically created/deleted turnouts so I have no intention of providing a command that indicates the turnout list has been updated since the throttle started. + Also note that turnouts marked in EXRAIL with the HIDDEN keyword instead of a "description" will NOT show up in these commands. + + + Automations/Routes + + A throttle need to know which EXRAIL Automations and Routes it can show the user. + + `````` Returns a list of Automations/Routes + e.g. `````` + Indicates route/automation ids. + Information on each route needs to be obtained by + `````` + returns e.g. `````` for a route + or `````` for an automation. + + Whats the difference: + A Route is just a call to an EXRAIL ROUTE, traditionally to set some turnouts or signals but can be used to perform any kind of EXRAIL function... but its not expecting to know the loco. + Thus a route can be triggered by sending in for example ``````. + + An Automation is a handoff of the last accessed loco id to an EXRAIL AUTOMATION which would typically drive the loco away. + Thus an Automation expects a start command with a cab id + e.g. `````` + + + Roster Information: + The `````` command requests a list of cab ids from the roster. + e.g. responding `````` + or for none. + + Each Roster entry had a name and descripotion + + diff --git a/WiThrottle.cpp b/WiThrottle.cpp index 72ee75a..7459a9c 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -139,14 +139,15 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { exRailSent=true; #ifdef EXRAIL_ACTIVE StringFormatter::send(stream,F("PRT]\\[Routes}|{Route]\\[Set}|{2]\\[Handoff}|{4\nPRL")); - for (int ix=0;;ix+=2) { - int16_t id=GETFLASHW(RMFT2::routeIdList+ix); - if (id==0) break; - bool isRoute=id<0; - if (isRoute) id=-id; - const FSH * desc=RMFT2::getRouteDescription(id); - StringFormatter::send(stream,F("]\\[%c%d}|{%S}|{%c"), - isRoute?'R':'A',id,desc, isRoute?'2':'4'); + for (byte pass=0;pass<2;pass++) { + + for (int ix=0;;ix+=2) { + int16_t id=GETFLASHW((pass?RMFT2::routeIdList:RMFT2::automationIdList)+ix); + if (id==0) break; + const FSH * desc=RMFT2::getRouteDescription(id); + StringFormatter::send(stream,F("]\\[%c%d}|{%S}|{%c"), + pass?'A':'R',id,desc, pass?'4':'2'); + } } StringFormatter::send(stream,F("\n")); #endif From b41ca2f44a9074507331fdfaa4137f0184849907 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 12 Apr 2022 17:05:44 +0100 Subject: [PATCH 16/41] Throttle notes --- Release_Notes/ThrottleAssists.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Release_Notes/ThrottleAssists.md b/Release_Notes/ThrottleAssists.md index 41d0250..2d32979 100644 --- a/Release_Notes/ThrottleAssists.md +++ b/Release_Notes/ThrottleAssists.md @@ -35,6 +35,7 @@ Note: It is still the throttles responsibility to monitor the status broadcasts. `````` returns e.g. `````` for a route or `````` for an automation. + or `````` for id not found Whats the difference: A Route is just a call to an EXRAIL ROUTE, traditionally to set some turnouts or signals but can be used to perform any kind of EXRAIL function... but its not expecting to know the loco. @@ -50,6 +51,22 @@ Note: It is still the throttles responsibility to monitor the status broadcasts. e.g. responding `````` or for none. - Each Roster entry had a name and descripotion + Each Roster entry had a name and function map obtained by: + `````` reply like ``` + Refer to EXRAIL ROSTER command for function map format. + + + TODO: + `````` Requests a deliberate update on the cab speed/functions in the same format as the cab broadcast. + + COMMANDS TO AVOID + + `````` Use `````` + `````` + `````` other than `````` + `````` + `````` + + From e13175635c2443d9cdc0d31719cac7492a891a1e Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 12 Apr 2022 17:05:55 +0100 Subject: [PATCH 17/41] uno memory saver --- DCCEXParser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index b0e9801..4f866c6 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -503,6 +503,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) DCC::setFn(p[0], p[1], p[2] == 1); return; +#if WIFI_ON case '+': // Complex Wifi interface command (not usual parse) if (atCommandCallback && !ringStream) { DCCWaveform::mainTrack.setPowerMode(POWERMODE::OFF); @@ -511,6 +512,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) return; } break; +#endif case 'J' : // throttle info access { From 20b12bcb7c66c37b0ea6073ce3919324b535a9b7 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 12 Apr 2022 18:47:06 +0100 Subject: [PATCH 18/41] JA JR and --- DCC.cpp | 5 ++++- DCC.h | 5 ++--- DCCEXParser.cpp | 19 ++++++++++++++++--- EXRAIL2MacroReset.h | 4 ++-- EXRAILMacros.h | 10 +++++----- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 6674fb9..1c53456 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -647,7 +647,7 @@ byte DCC::cv2(int cv) { return lowByte(cv); } -int DCC::lookupSpeedTable(int locoId) { +int DCC::lookupSpeedTable(int locoId, bool autoCreate) { // determine speed reg for this loco int firstEmpty = MAX_LOCOS; int reg; @@ -655,6 +655,9 @@ int DCC::lookupSpeedTable(int locoId) { if (speedTable[reg].loco == locoId) break; if (speedTable[reg].loco == 0 && firstEmpty == MAX_LOCOS) firstEmpty = reg; } + + // return -1 if not found and not auto creating + if (reg== MAX_LOCOS && !autoCreate) return -1; if (reg == MAX_LOCOS) reg = firstEmpty; if (reg >= MAX_LOCOS) { DIAG(F("Too many locos")); diff --git a/DCC.h b/DCC.h index fbeb603..5d9064c 100644 --- a/DCC.h +++ b/DCC.h @@ -128,7 +128,6 @@ public: static void forgetLoco(int cab); // removes any speed reminders for this loco static void forgetAllLocos(); // removes all speed reminders static void displayCabList(Print *stream); - static FSH *getMotorShieldName(); static inline void setGlobalSpeedsteps(byte s) { globalSpeedsteps = s; @@ -148,7 +147,8 @@ public: unsigned long functions; }; static LOCO speedTable[MAX_LOCOS]; - + static int lookupSpeedTable(int locoId, bool autoCreate=true); + private: static byte joinRelay; static byte loopStatus; @@ -162,7 +162,6 @@ private: static byte cv1(byte opcode, int cv); static byte cv2(int cv); - static int lookupSpeedTable(int locoId); static void issueReminders(); static void callback(int value); diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 4f866c6..11a91d2 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -217,10 +217,23 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) return; // filterCallback asked us to ignore case 't': // THROTTLE { + if (params==1) { // display state + + int16_t slot=DCC::lookupSpeedTable(p[0],false); + if (slot>=0) { + DCC::LOCO * sp=&DCC::speedTable[slot]; + StringFormatter::send(stream,F("\n"), + sp->loco,slot,sp->speedCode,sp->functions); + } + else // send dummy state speed 0 fwd no functions. + StringFormatter::send(stream,F("\n"),p[0]); + return; + } + int16_t cab; int16_t tspeed; int16_t direction; - + if (params == 4) { // cab = p[1]; @@ -523,8 +536,8 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) StringFormatter::send(stream, F(" #ifdef EXRAIL_ACTIVE - sendFlashList(stream,RMFT2::automationIdList); sendFlashList(stream,RMFT2::routeIdList); + sendFlashList(stream,RMFT2::automationIdList); #endif } else { // @@ -589,7 +602,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) } void DCCEXParser::sendFlashList(Print * stream,const int16_t flashList[]) { - for (int16_t i=0;;i+=2) { + for (int16_t i=0;;i++) { int16_t value=GETFLASHW(flashList+i); if (value==0) return; StringFormatter::send(stream,F(" %d"),value); diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index e28452a..edbfd6d 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -131,7 +131,7 @@ #define ATGTE(sensor_id,value) #define ATLT(sensor_id,value) #define ATTIMEOUT(sensor_id,timeout_ms) -#define AUTOMATION(id, description) +#define AUTOMATION(id,description) #define AUTOSTART #define BROADCAST(msg) #define CALL(route) @@ -192,7 +192,7 @@ #define RESUME #define RETURN #define REV(speed) -#define ROUTE(id, description) +#define ROUTE(id,description) #define ROSTER(cab,name,funcmap...) #define SENDLOCO(cab,route) #define SEQUENCE(id) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index c11bd35..075a69c 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -86,7 +86,7 @@ const FSH * RMFT2::getRouteDescription(int16_t id) { #include "myAutomation.h" default: break; } - return NULL; + return F(""); } // Pass 4... Create Text sending functions @@ -161,20 +161,20 @@ const int16_t FLASH RMFT2::rosterIdList[]={ const FSH * RMFT2::getRosterName(int16_t id) { switch(id) { #include "myAutomation.h" - default: return NULL; + default: break; } - return NULL; + return F(""); } // Pass to get roster functions #undef ROSTER -#define ROSTER(cabid,name,funcmap...) O_DESC(cabid,funcmap) +#define ROSTER(cabid,name,funcmap...) case cabid: return F("" funcmap); const FSH * RMFT2::getRosterFunctions(int16_t id) { switch(id) { #include "myAutomation.h" default: break; } - return NULL; + return F(""); } // Pass 8 Signal definitions From 28a44060446a59907f9f30408358698dab754a70 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 12 Apr 2022 23:10:29 +0100 Subject: [PATCH 19/41] Subtle corrections --- DCCEXParser.cpp | 16 ++++++++-------- EXRAIL2.cpp | 4 ++-- EXRAILMacros.h | 2 +- Release_Notes/ThrottleAssists.md | 11 +++++++---- WiThrottle.cpp | 9 +++++---- 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 11a91d2..9d0cf5c 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -566,21 +566,21 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) StringFormatter::send(stream, F(" for ( Turnout * t=Turnout::first(); t; t=t->next()) { - if (t->isHidden()) continue; - StringFormatter::send(stream, F(" %d"),t->getId()); + if (t->isHidden()) continue; + StringFormatter::send(stream, F(" %d"),t->getId()); } } else { // Turnout * t=Turnout::get(id); - if (t && !t->isHidden()) - StringFormatter::send(stream, F(" %d %c \"%S\""), - id,t->isThrown()?'T':'C', + if (!t || t->isHidden()) StringFormatter::send(stream, F(" %d X"),id); + else StringFormatter::send(stream, F(" %d %c \"%S\""), + id,t->isThrown()?'T':'C', #ifdef EXRAIL_ACTIVE - RMFT2::getTurnoutDescription(id) + RMFT2::getTurnoutDescription(id) #else - F("") + F("") #endif - ); + ); } StringFormatter::send(stream, F(">\n")); return; diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 55be05a..dbbb038 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -262,12 +262,12 @@ void RMFT2::setTurnoutHiddenState(Turnout * t) { } char RMFT2::getRouteType(int16_t id) { - for (int16_t i=0;;i+=2) { + for (int16_t i=0;;i++) { int16_t rid= GETFLASHW(routeIdList+i); if (rid==id) return 'R'; if (rid==0) break; } - for (int16_t i=0;;i+=2) { + for (int16_t i=0;;i++) { int16_t rid= GETFLASHW(automationIdList+i); if (rid==id) return 'A'; if (rid==0) break; diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 075a69c..8cdbf41 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -53,7 +53,7 @@ // helper macro for turnout descriptions, creates NULL for missing description #define O_DESC(id, desc) case id: return ("" desc)[0]?F("" desc):NULL; // helper macro for turnout description as HIDDEN -#define HIDDEN "\0x01" +#define HIDDEN "\x01" // Pass 1 Implements aliases #include "EXRAIL2MacroReset.h" diff --git a/Release_Notes/ThrottleAssists.md b/Release_Notes/ThrottleAssists.md index 2d32979..9d979c9 100644 --- a/Release_Notes/ThrottleAssists.md +++ b/Release_Notes/ThrottleAssists.md @@ -16,7 +16,7 @@ e.g. response `````` e.g. response `````` or `````` (T=thrown, C=closed) or `````` indicating turnout description not given. -or `````` indicating turnout unknown. +or `````` indicating turnout unknown (or possibly hidden.) Note: It is still the throttles responsibility to monitor the status broadcasts. (TBD I'm thinking that the existing broadcast is messy and needs cleaning up) @@ -57,16 +57,19 @@ Note: It is still the throttles responsibility to monitor the status broadcasts. Refer to EXRAIL ROSTER command for function map format. - TODO: + Obtaining throttle status. `````` Requests a deliberate update on the cab speed/functions in the same format as the cab broadcast. + `````` + Note that a slot of -1 indicates that the cab is not in the reminders table and this comand will not reserve a slot until such time as the cab is throttled. + COMMANDS TO AVOID `````` Use `````` - `````` + `````` Just drop the slot number `````` other than `````` `````` `````` - + diff --git a/WiThrottle.cpp b/WiThrottle.cpp index 7459a9c..29a8c3a 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -119,6 +119,7 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { if (turnoutListHash != Turnout::turnoutlistHash) { StringFormatter::send(stream,F("PTL")); for(Turnout *tt=Turnout::first();tt!=NULL;tt=tt->next()){ + if (tt->isHidden()) continue; int id=tt->getId(); const FSH * tdesc=NULL; #ifdef EXRAIL_ACTIVE @@ -127,7 +128,7 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { char tchar=Turnout::isClosed(id)?'2':'4'; if (tdesc==NULL) // turnout with no description StringFormatter::send(stream,F("]\\[%d}|{T%d}|{T%c"), id,id,tchar); - else if (GETFLASH(tdesc)!='*') // ignore hidden turnouts + else StringFormatter::send(stream,F("]\\[%d}|{%S}|{%c"), id,tdesc,tchar); } StringFormatter::send(stream,F("\n")); @@ -140,9 +141,9 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { #ifdef EXRAIL_ACTIVE StringFormatter::send(stream,F("PRT]\\[Routes}|{Route]\\[Set}|{2]\\[Handoff}|{4\nPRL")); for (byte pass=0;pass<2;pass++) { - - for (int ix=0;;ix+=2) { - int16_t id=GETFLASHW((pass?RMFT2::routeIdList:RMFT2::automationIdList)+ix); + // first pass automations, second pass routes. + for (int ix=0;;ix++) { + int16_t id=GETFLASHW((pass?RMFT2::automationIdList:RMFT2::routeIdList)+ix); if (id==0) break; const FSH * desc=RMFT2::getRouteDescription(id); StringFormatter::send(stream,F("]\\[%c%d}|{%S}|{%c"), From 32fdb014efff990e01b82aef4da87bb803347e13 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 12 Apr 2022 23:32:23 +0100 Subject: [PATCH 20/41] Update version.h --- version.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 563b10d..bc37114 100644 --- a/version.h +++ b/version.h @@ -6,7 +6,10 @@ #define VERSION "4.0.2" // 4.0.2 EXRAIL additions: -// PARSE <> commands +// command to obtain current throttle settings +// JA, JR, JT commands to obtain route, roster and turnout descriptions +// HIDDEN turnouts +// PARSE <> commands in EXRAIL // VIRTUAL_TURNOUT // and KILLALL command to stop all tasks. // FORGET forgets the current loco in DCC reminder tables. From 766fdc43ace72907c9afd3c15255cb0569a58cfd Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Sat, 16 Apr 2022 23:35:58 +0100 Subject: [PATCH 21/41] I2C code corrections Corrections to I2C code: 1) I2CManager_Mega4809.h: Correct bitwise 'and' to logical 'and' - no impact. 2) I2CManager_Wire.h: Ensure that error codes from Wire subsystem are passed back to caller in queueRequest(). --- I2CManager_Mega4809.h | 2 +- I2CManager_Wire.h | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/I2CManager_Mega4809.h b/I2CManager_Mega4809.h index a8254a9..0b8e8ca 100644 --- a/I2CManager_Mega4809.h +++ b/I2CManager_Mega4809.h @@ -72,7 +72,7 @@ void I2CManagerClass::I2C_sendStart() { bytesToReceive = currentRequest->readLen; // If anything to send, initiate write. Otherwise initiate read. - if (operation == OPERATION_READ || ((operation == OPERATION_REQUEST) & !bytesToSend)) + if (operation == OPERATION_READ || ((operation == OPERATION_REQUEST) && !bytesToSend)) TWI0.MADDR = (currentRequest->i2cAddress << 1) | 1; else TWI0.MADDR = (currentRequest->i2cAddress << 1) | 0; diff --git a/I2CManager_Wire.h b/I2CManager_Wire.h index 26deb74..87152e7 100644 --- a/I2CManager_Wire.h +++ b/I2CManager_Wire.h @@ -94,22 +94,24 @@ uint8_t I2CManagerClass::read(uint8_t address, uint8_t readBuffer[], uint8_t rea /*************************************************************************** * Function to queue a request block and initiate operations. * - * For the Wire version, this executes synchronously, but the status is - * returned in the I2CRB as for the asynchronous version. + * For the Wire version, this executes synchronously. + * The read/write/write_P functions return I2C_STATUS_OK always, and the + * completion status of the operation is in the request block, as for + * the non-blocking version. ***************************************************************************/ void I2CManagerClass::queueRequest(I2CRB *req) { switch (req->operation) { case OPERATION_READ: - req->status = read(req->i2cAddress, req->readBuffer, req->readLen, NULL, 0, req); + read(req->i2cAddress, req->readBuffer, req->readLen, NULL, 0, req); break; case OPERATION_SEND: - req->status = write(req->i2cAddress, req->writeBuffer, req->writeLen, req); + write(req->i2cAddress, req->writeBuffer, req->writeLen, req); break; case OPERATION_SEND_P: - req->status = write_P(req->i2cAddress, req->writeBuffer, req->writeLen, req); + write_P(req->i2cAddress, req->writeBuffer, req->writeLen, req); break; case OPERATION_REQUEST: - req->status = read(req->i2cAddress, req->readBuffer, req->readLen, req->writeBuffer, req->writeLen, req); + read(req->i2cAddress, req->readBuffer, req->readLen, req->writeBuffer, req->writeLen, req); break; } } From 45f690eb4d86264bd49cbd56592e12412e68e698 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sun, 17 Apr 2022 09:58:32 +0100 Subject: [PATCH 22/41] RAG Ifs and cmds --- EXRAIL2.cpp | 157 +++++++++++++++++++++++++++++--------------- EXRAIL2.h | 16 +++-- EXRAIL2MacroReset.h | 6 ++ EXRAILMacros.h | 3 + 4 files changed, 123 insertions(+), 59 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index dbbb038..1114364 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -64,6 +64,9 @@ const int16_t HASH_KEYWORD_RESUME=27609; const int16_t HASH_KEYWORD_KILL=5218; const int16_t HASH_KEYWORD_ALL=3457; const int16_t HASH_KEYWORD_ROUTES=-3702; +const int16_t HASH_KEYWORD_RED=26099; +const int16_t HASH_KEYWORD_AMBER=18713; +const int16_t HASH_KEYWORD_GREEN=31493; // One instance of RMFT clas is used for each "thread" in the automation. // Each thread manages a loco on a journey through the layout, and/or may manage a scenery automation. @@ -406,6 +409,18 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { case HASH_KEYWORD_UNLATCH: setFlag(p[1], 0, LATCH_FLAG); return true; + + case HASH_KEYWORD_RED: + doSignal(p[1],SIGNAL_RED); + return true; + + case HASH_KEYWORD_AMBER: + doSignal(p[1],SIGNAL_AMBER); + return true; + + case HASH_KEYWORD_GREEN: + doSignal(p[1],SIGNAL_GREEN); + return true; default: return false; @@ -517,6 +532,9 @@ bool RMFT2::skipIfBlock() { case OPCODE_IFRESERVE: case OPCODE_IFTHROWN: case OPCODE_IFTIMEOUT: + case OPCODE_IFRED: + case OPCODE_IFAMBER: + case OPCODE_IFGREEN: nest++; break; case OPCODE_ENDIF: @@ -553,6 +571,10 @@ void RMFT2::loop2() { byte opcode = GET_OPCODE; int16_t operand = GET_OPERAND(0); + + // skipIf will get set to indicate a failing IF condition + bool skipIf=false; + // if (diag) DIAG(F("RMFT2 %d %d"),opcode,operand); // Attention: Returning from this switch leaves the program counter unchanged. // This is used for unfinished waits for timers or sensors. @@ -639,7 +661,7 @@ void RMFT2::loop2() { return; case OPCODE_IFTIMEOUT: // do next operand if timeout flag set - if (!timeoutFlag) if (!skipIfBlock()) return; + skipIf=!timeoutFlag; break; case OPCODE_AFTER: // waits for sensor to hit and then remain off for 0.5 seconds. (must come after an AT operation) @@ -691,40 +713,52 @@ void RMFT2::loop2() { break; case OPCODE_IF: // do next operand if sensor set - if (!readSensor(operand)) if (!skipIfBlock()) return; + skipIf=!readSensor(operand); break; case OPCODE_ELSE: // skip to matching ENDIF - if (!skipIfBlock()) return; + skipIf=true; break; case OPCODE_IFGTE: // do next operand if sensor>= value - if (IODevice::readAnalogue(operand)<(int)(GET_OPERAND(1))) if (!skipIfBlock()) return; + skipIf=IODevice::readAnalogue(operand)<(int)(GET_OPERAND(1)); break; case OPCODE_IFLT: // do next operand if sensor< value - if (IODevice::readAnalogue(operand)>=(int)(GET_OPERAND(1))) if (!skipIfBlock()) return; + skipIf=IODevice::readAnalogue(operand)>=(int)(GET_OPERAND(1)); break; case OPCODE_IFNOT: // do next operand if sensor not set - if (readSensor(operand)) if (!skipIfBlock()) return; + skipIf=readSensor(operand); break; case OPCODE_IFRANDOM: // do block on random percentage - if ((int16_t)random(100)>=operand) if (!skipIfBlock()) return; + skipIf=(int16_t)random(100)>=operand; break; case OPCODE_IFRESERVE: // do block if we successfully RERSERVE if (!getFlag(operand,SECTION_FLAG)) setFlag(operand,SECTION_FLAG); - else if (!skipIfBlock()) return; + else skipIf=true; + break; + + case OPCODE_IFRED: // do block if signal as expected + skipIf=!isSignal(operand,SIGNAL_RED); + break; + + case OPCODE_IFAMBER: // do block if signal as expected + skipIf=!isSignal(operand,SIGNAL_AMBER); + break; + + case OPCODE_IFGREEN: // do block if signal as expected + skipIf=!isSignal(operand,SIGNAL_GREEN); break; case OPCODE_IFTHROWN: - if (Turnout::isClosed(operand)) if (!skipIfBlock()) return; + skipIf=Turnout::isClosed(operand); break; case OPCODE_IFCLOSED: - if (!Turnout::isClosed(operand)) if (!skipIfBlock()) return; + skipIf=Turnout::isThrown(operand); break; case OPCODE_ENDIF: @@ -747,15 +781,15 @@ void RMFT2::loop2() { break; case OPCODE_RED: - doSignal(operand,true,false,false); + doSignal(operand,SIGNAL_RED); break; case OPCODE_AMBER: - doSignal(operand,false,true,false); + doSignal(operand,SIGNAL_AMBER); break; case OPCODE_GREEN: - doSignal(operand,false,false,true); + doSignal(operand,SIGNAL_GREEN); break; case OPCODE_FON: @@ -924,6 +958,8 @@ void RMFT2::loop2() { kill(F("INVOP"),operand); } // Falling out of the switch means move on to the next opcode + // but if we are skipping a false IF or else + if (skipIf) if (!skipIfBlock()) return; SKIPOP; } @@ -951,51 +987,64 @@ void RMFT2::kill(const FSH * reason, int operand) { delete this; } -/* static */ void RMFT2::doSignal(VPIN id,bool red, bool amber, bool green) { - //if (diag) DIAG(F(" dosignal %d"),id); +int16_t RMFT2::getSignalSlot(VPIN id) { for (int sigpos=0;;sigpos+=4) { - //if (diag) DIAG(F("red=%d"),redpin); - VPIN sigid=GETFLASHW(RMFT2::SignalDefinitions+sigpos); - if (sigid==0) { // end of signal list - DIAG(F("EXRAIL Signal %d not defined"), id); - return; // signal not found - } - // sigid is the signal id used in RED/AMBER/GREEN macro - // for a LED signal it will be same as redpin - // but for a servo signal it will also have SERVO_SIGNAL_FLAG set. + VPIN sigid=GETFLASHW(RMFT2::SignalDefinitions+sigpos); + if (sigid==0) { // end of signal list + DIAG(F("EXRAIL Signal %d not defined"), id); + return -1; + } + // sigid is the signal id used in RED/AMBER/GREEN macro + // for a LED signal it will be same as redpin + // but for a servo signal it will also have SERVO_SIGNAL_FLAG set. - if ((sigid & ~SERVO_SIGNAL_FLAG & ~ACTIVE_HIGH_SIGNAL_FLAG)!= id) continue; // keep looking + if ((sigid & ~SERVO_SIGNAL_FLAG & ~ACTIVE_HIGH_SIGNAL_FLAG)!= id) continue; // keep looking + return sigpos/4; // relative slot in signals table + } +} +/* static */ void RMFT2::doSignal(VPIN id,char rag) { + //if (diag) DIAG(F(" dosignal %d %x"),id,rag); + int16_t sigslot=getSignalSlot(id); + if (sigslot<0) return; + + // keep track of signal state + setFlag(sigslot,rag,SIGNAL_MASK); + + // Correct signal definition found, get the rag values + int16_t sigpos=sigslot*4; + VPIN sigid=GETFLASHW(RMFT2::SignalDefinitions+sigpos); + VPIN redpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+1); + VPIN amberpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+2); + VPIN greenpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+3); + //if (diag) DIAG(F("signal %d %d %d"),redpin,amberpin,greenpin); - // Correct signal definition found, get the rag values - VPIN redpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+1); - VPIN amberpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+2); - VPIN greenpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+3); - //if (diag) DIAG(F("signal %d %d %d"),redpin,amberpin,greenpin); - - if (sigid & SERVO_SIGNAL_FLAG) { - // A servo signal, the pin numbers are actually servo positions - // Note, setting a signal to a zero position has no effect. - int16_t servopos= red? redpin: (green? greenpin : amberpin); - if (servopos!=0) IODevice::writeAnalogue(id,servopos,PCA9685::Bounce); - return; - } - - // LED or similar 3 pin signal - // If amberpin is zero, synthesise amber from red+green - if (amber && (amberpin==0)) { - red=true; - green=true; - } - - // Manage invert (HIGH on) pins - bool aHigh=sigid & ACTIVE_HIGH_SIGNAL_FLAG; - - // set the three pins - if (redpin) IODevice::write(redpin,red^aHigh); - if (amberpin) IODevice::write(amberpin,amber^aHigh); - if (greenpin) IODevice::write(greenpin,green^aHigh); - return; + if (sigid & SERVO_SIGNAL_FLAG) { + // A servo signal, the pin numbers are actually servo positions + // Note, setting a signal to a zero position has no effect. + int16_t servopos= rag==SIGNAL_RED? redpin: (rag==SIGNAL_GREEN? greenpin : amberpin); + if (servopos!=0) IODevice::writeAnalogue(id,servopos,PCA9685::Bounce); + return; } + + // LED or similar 3 pin signal + // If amberpin is zero, synthesise amber from red+green + const byte SIMAMBER=0x00; + if (rag==SIGNAL_AMBER && (amberpin==0)) rag=SIMAMBER; // special case this func only + + // Manage invert (HIGH on) pins + bool aHigh=sigid & ACTIVE_HIGH_SIGNAL_FLAG; + + // set the three pins + if (redpin) IODevice::write(redpin,(rag==SIGNAL_RED || rag==SIMAMBER)^aHigh); + if (amberpin) IODevice::write(amberpin,(rag==SIGNAL_AMBER)^aHigh); + if (greenpin) IODevice::write(greenpin,(rag==SIGNAL_GREEN || rag==SIMAMBER)^aHigh); + return; +} + +/* static */ bool RMFT2::isSignal(VPIN id,char rag) { + int16_t sigslot=getSignalSlot(id); + if (sigslot<0) return false; + return (flags[sigslot] & SIGNAL_MASK) == rag; } void RMFT2::turnoutEvent(int16_t turnoutId, bool closed) { diff --git a/EXRAIL2.h b/EXRAIL2.h index 616b05d..e881e3b 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -51,6 +51,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_PRINT,OPCODE_DCCACTIVATE, OPCODE_ONACTIVATE,OPCODE_ONDEACTIVATE,OPCODE_IFGTE,OPCODE_IFLT, OPCODE_ROSTER,OPCODE_KILLALL, + OPCODE_IFRED,OPCODE_IFAMBER,OPCODE_IFGREEN, OPCODE_ROUTE,OPCODE_AUTOMATION,OPCODE_SEQUENCE,OPCODE_ENDTASK,OPCODE_ENDEXRAIL }; @@ -58,10 +59,13 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, // Flag bits for status of hardware and TPL static const byte SECTION_FLAG = 0x80; - static const byte LATCH_FLAG = 0x40; - static const byte TASK_FLAG = 0x20; - static const byte SPARE_FLAG = 0x10; - static const byte COUNTER_MASK= 0x0F; + static const byte LATCH_FLAG = 0x40; + static const byte TASK_FLAG = 0x20; + static const byte SPARE_FLAG = 0x10; + static const byte SIGNAL_MASK = 0x0C; + static const byte SIGNAL_RED = 0x08; + static const byte SIGNAL_AMBER = 0x0C; + static const byte SIGNAL_GREEN = 0x04; static const byte MAX_STACK_DEPTH=4; @@ -112,7 +116,9 @@ private: static void setFlag(VPIN id,byte onMask, byte OffMask=0); static bool getFlag(VPIN id,byte mask); static int16_t progtrackLocoId; - static void doSignal(VPIN id,bool red, bool amber, bool green); + static void doSignal(VPIN id,char rag); + static bool isSignal(VPIN id,char rag); + static int16_t getSignalSlot(VPIN id); static void setTurnoutHiddenState(Turnout * t); static RMFT2 * loopTask; static RMFT2 * pausingTask; diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index edbfd6d..dde5ea2 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -59,11 +59,14 @@ #undef FWD #undef GREEN #undef IF +#undef IFAMBER #undef IFCLOSED +#undef IFGREEN #undef IFGTE #undef IFLT #undef IFNOT #undef IFRANDOM +#undef IFRED #undef IFRESERVE #undef IFTHROWN #undef IFTIMEOUT @@ -158,11 +161,14 @@ #define FWD(speed) #define GREEN(signal_id) #define IF(sensor_id) +#define IFAMBER(signal_id) #define IFCLOSED(turnout_id) +#define IFGREEN(signal_id) #define IFGTE(sensor_id,value) #define IFLT(sensor_id,value) #define IFNOT(sensor_id) #define IFRANDOM(percent) +#define IFRED(signal_id) #define IFTHROWN(turnout_id) #define IFRESERVE(block) #define IFTIMEOUT diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 8cdbf41..eb56794 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -235,11 +235,14 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = { #define FWD(speed) OPCODE_FWD,V(speed), #define GREEN(signal_id) OPCODE_GREEN,V(signal_id), #define IF(sensor_id) OPCODE_IF,V(sensor_id), +#define IFAMBER(signal_id) OPCODE_IFAMBER,V(signal_id), #define IFCLOSED(turnout_id) OPCODE_IFCLOSED,V(turnout_id), +#define IFGREEN(signal_id) OPCODE_IFGREEN,V(signal_id), #define IFGTE(sensor_id,value) OPCODE_IFGTE,V(sensor_id),OPCODE_PAD,V(value), #define IFLT(sensor_id,value) OPCODE_IFLT,V(sensor_id),OPCODE_PAD,V(value), #define IFNOT(sensor_id) OPCODE_IFNOT,V(sensor_id), #define IFRANDOM(percent) OPCODE_IFRANDOM,V(percent), +#define IFRED(signal_id) OPCODE_IFRED,V(signal_id), #define IFRESERVE(block) OPCODE_IFRESERVE,V(block), #define IFTHROWN(turnout_id) OPCODE_IFTHROWN,V(turnout_id), #define IFTIMEOUT OPCODE_IFTIMEOUT,0,0, From 920fcbc0958c19b49bc667ce167caeefa29331cb Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sun, 17 Apr 2022 10:10:22 +0100 Subject: [PATCH 23/41] IF block perf/memory --- EXRAIL2.cpp | 42 ++++++++++++++++-------------------------- EXRAIL2.h | 20 +++++++++++++++----- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 1114364..492cd4e 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -519,31 +519,21 @@ bool RMFT2::skipIfBlock() { while (nest > 0) { SKIPOP; byte opcode = GET_OPCODE; - switch(opcode) { - case OPCODE_ENDEXRAIL: - kill(F("missing ENDIF"), nest); - return false; - case OPCODE_IF: - case OPCODE_IFCLOSED: - case OPCODE_IFGTE: - case OPCODE_IFLT: - case OPCODE_IFNOT: - case OPCODE_IFRANDOM: - case OPCODE_IFRESERVE: - case OPCODE_IFTHROWN: - case OPCODE_IFTIMEOUT: - case OPCODE_IFRED: - case OPCODE_IFAMBER: - case OPCODE_IFGREEN: - nest++; - break; - case OPCODE_ENDIF: - nest--; - break; - case OPCODE_ELSE: - // if nest==1 then this is the ELSE for the IF we are skipping - if (nest==1) nest=0; // cause loop exit and return after ELSE - break; + // all other IF type commands increase the nesting level + if (opcode>IF_TYPE_OPCODES) nest++; + else switch(opcode) { + case OPCODE_ENDEXRAIL: + kill(F("missing ENDIF"), nest); + return false; + + case OPCODE_ENDIF: + nest--; + break; + + case OPCODE_ELSE: + // if nest==1 then this is the ELSE for the IF we are skipping + if (nest==1) nest=0; // cause loop exit and return after ELSE + break; default: break; } @@ -574,7 +564,7 @@ void RMFT2::loop2() { // skipIf will get set to indicate a failing IF condition bool skipIf=false; - + // if (diag) DIAG(F("RMFT2 %d %d"),opcode,operand); // Attention: Returning from this switch leaves the program counter unchanged. // This is used for unfinished waits for timers or sensors. diff --git a/EXRAIL2.h b/EXRAIL2.h index e881e3b..b44ce6c 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -35,10 +35,9 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_RESERVE,OPCODE_FREE, OPCODE_AT,OPCODE_AFTER,OPCODE_AUTOSTART, OPCODE_ATGTE,OPCODE_ATLT, - OPCODE_ATTIMEOUT1,OPCODE_ATTIMEOUT2,OPCODE_IFTIMEOUT, + OPCODE_ATTIMEOUT1,OPCODE_ATTIMEOUT2, OPCODE_LATCH,OPCODE_UNLATCH,OPCODE_SET,OPCODE_RESET, - OPCODE_IF,OPCODE_IFNOT,OPCODE_ENDIF,OPCODE_IFRANDOM,OPCODE_IFRESERVE, - OPCODE_IFCLOSED, OPCODE_IFTHROWN,OPCODE_ELSE, + OPCODE_ENDIF,OPCODE_ELSE, OPCODE_DELAY,OPCODE_DELAYMINS,OPCODE_DELAYMS,OPCODE_RANDWAIT, OPCODE_FON,OPCODE_FOFF,OPCODE_XFON,OPCODE_XFOFF, OPCODE_RED,OPCODE_GREEN,OPCODE_AMBER,OPCODE_DRIVE, @@ -49,10 +48,21 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_PAUSE, OPCODE_RESUME,OPCODE_POWEROFF,OPCODE_POWERON, OPCODE_ONCLOSE, OPCODE_ONTHROW, OPCODE_SERVOTURNOUT, OPCODE_PINTURNOUT, OPCODE_PRINT,OPCODE_DCCACTIVATE, - OPCODE_ONACTIVATE,OPCODE_ONDEACTIVATE,OPCODE_IFGTE,OPCODE_IFLT, + OPCODE_ONACTIVATE,OPCODE_ONDEACTIVATE, OPCODE_ROSTER,OPCODE_KILLALL, + OPCODE_ROUTE,OPCODE_AUTOMATION,OPCODE_SEQUENCE, + OPCODE_ENDTASK,OPCODE_ENDEXRAIL, + + // OPcodes below this point are skip-nesting IF operations + // placed here so that they may be skipped as a group + // see skipIfBlock() + IF_TYPE_OPCODES, // do not move this... OPCODE_IFRED,OPCODE_IFAMBER,OPCODE_IFGREEN, - OPCODE_ROUTE,OPCODE_AUTOMATION,OPCODE_SEQUENCE,OPCODE_ENDTASK,OPCODE_ENDEXRAIL + OPCODE_IFGTE,OPCODE_IFLT, + OPCODE_IFTIMEOUT, + OPCODE_IF,OPCODE_IFNOT, + OPCODE_IFRANDOM,OPCODE_IFRESERVE, + OPCODE_IFCLOSED, OPCODE_IFTHROWN }; From 64cae263333121cf59b1f2faaa97148e0f3b9e88 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 18 Apr 2022 16:46:13 +0100 Subject: [PATCH 24/41] Allow negative route ids. --- WiThrottle.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WiThrottle.cpp b/WiThrottle.cpp index 29a8c3a..60bdf7d 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -255,11 +255,14 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { int WiThrottle::getInt(byte * cmd) { int i=0; + bool negate=cmd[0]=='-'; + if (negate) cmd++; while (cmd[0]>='0' && cmd[0]<='9') { i=i*10 + (cmd[0]-'0'); cmd++; } - return i; + if (negate) i=0-i; + return i ; } int WiThrottle::getLocoId(byte * cmd) { From 6515f1b51286a3a9e4acdb9ed94e4ef8ae06c49e Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 18 Apr 2022 16:47:07 +0100 Subject: [PATCH 25/41] correct GREEN keyword --- EXRAIL2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 492cd4e..6e7e005 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -66,7 +66,7 @@ const int16_t HASH_KEYWORD_ALL=3457; const int16_t HASH_KEYWORD_ROUTES=-3702; const int16_t HASH_KEYWORD_RED=26099; const int16_t HASH_KEYWORD_AMBER=18713; -const int16_t HASH_KEYWORD_GREEN=31493; +const int16_t HASH_KEYWORD_GREEN=-31493; // One instance of RMFT clas is used for each "thread" in the automation. // Each thread manages a loco on a journey through the layout, and/or may manage a scenery automation. From 14834d47a5b1006909b806f7936410609bd563eb Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 18 Apr 2022 16:50:05 +0100 Subject: [PATCH 26/41] Update version.h --- version.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/version.h b/version.h index bc37114..372fbd2 100644 --- a/version.h +++ b/version.h @@ -6,6 +6,9 @@ #define VERSION "4.0.2" // 4.0.2 EXRAIL additions: +// FIX negative route ids in WIthrottle problem. +// IFRED(signal_id), IFAMBER(signal_id), IFGREEN(signal_id) +// commands // command to obtain current throttle settings // JA, JR, JT commands to obtain route, roster and turnout descriptions // HIDDEN turnouts From 9cf70f5870576d5667e4d310a54975261e03398f Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 19 Apr 2022 09:35:03 +0100 Subject: [PATCH 27/41] myFilter auto detect --- DCCEXParser.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 9d0cf5c..6c7518e 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -94,7 +94,7 @@ Print *DCCEXParser::stashStream = NULL; RingStream *DCCEXParser::stashRingStream = NULL; byte DCCEXParser::stashTarget=0; -// This is a JMRI command parser, one instance per incoming stream +// This is a JMRI command parser. // It doesnt know how the string got here, nor how it gets back. // It knows nothing about hardware or tracks... it just parses strings and // calls the corresponding DCC api. @@ -165,9 +165,12 @@ int16_t DCCEXParser::splitValues(int16_t result[MAX_COMMAND_PARAMS], const byte return parameterCount; } -FILTER_CALLBACK DCCEXParser::filterCallback = 0; +extern __attribute__((weak)) FILTER_CALLBACK myFilter(); +FILTER_CALLBACK DCCEXParser::filterCallback = myFilter; FILTER_CALLBACK DCCEXParser::filterRMFTCallback = 0; AT_COMMAND_CALLBACK DCCEXParser::atCommandCallback = 0; + +// deprecated void DCCEXParser::setFilter(FILTER_CALLBACK filter) { filterCallback = filter; From ff4dd2f1cdbd853a464991e03c69b5d8d91d6c79 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 19 Apr 2022 11:34:57 +0100 Subject: [PATCH 28/41] Update version.h --- version.h | 1 + 1 file changed, 1 insertion(+) diff --git a/version.h b/version.h index 372fbd2..8ca3ea4 100644 --- a/version.h +++ b/version.h @@ -6,6 +6,7 @@ #define VERSION "4.0.2" // 4.0.2 EXRAIL additions: +// myFilter automatic detection (no need to call setFilter) // FIX negative route ids in WIthrottle problem. // IFRED(signal_id), IFAMBER(signal_id), IFGREEN(signal_id) // commands From 17eb7c560ec89c3c8dd9edc3bd233e1009bd7a1d Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 20 Apr 2022 09:10:27 +0100 Subject: [PATCH 29/41] fix weak ref to myFilter --- DCCEXParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 6c7518e..0d8b537 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -165,7 +165,7 @@ int16_t DCCEXParser::splitValues(int16_t result[MAX_COMMAND_PARAMS], const byte return parameterCount; } -extern __attribute__((weak)) FILTER_CALLBACK myFilter(); +extern __attribute__((weak)) void myFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); FILTER_CALLBACK DCCEXParser::filterCallback = myFilter; FILTER_CALLBACK DCCEXParser::filterRMFTCallback = 0; AT_COMMAND_CALLBACK DCCEXParser::atCommandCallback = 0; From 431208d191e15d7cc6e2b6ce8c937cd07fa1017c Mon Sep 17 00:00:00 2001 From: Ash-4 <81280775+Ash-4@users.noreply.github.com> Date: Wed, 27 Apr 2022 10:32:21 -0500 Subject: [PATCH 30/41] ACK defaults now 50-2000-20000 --- DCCWaveform.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DCCWaveform.h b/DCCWaveform.h index 14f2f11..3838b8e 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -157,7 +157,7 @@ class DCCWaveform { volatile bool ackPending; volatile bool ackDetected; int ackThreshold; - int ackLimitmA = 60; + int ackLimitmA = 50; int ackMaxCurrent; unsigned long ackCheckStart; // millis unsigned int ackCheckDuration; // millis @@ -165,8 +165,8 @@ class DCCWaveform { unsigned int ackPulseDuration; // micros unsigned long ackPulseStart; // micros - unsigned int minAckPulseDuration = 4000; // micros - unsigned int maxAckPulseDuration = 8500; // micros + unsigned int minAckPulseDuration = 2000; // micros + unsigned int maxAckPulseDuration = 20000; // micros volatile static uint8_t numAckGaps; volatile static uint8_t numAckSamples; From ad975927888cc12ce3181453c6ffb257bc254d16 Mon Sep 17 00:00:00 2001 From: Ash-4 <81280775+Ash-4@users.noreply.github.com> Date: Wed, 27 Apr 2022 10:38:00 -0500 Subject: [PATCH 31/41] Update version.h --- version.h | 1 + 1 file changed, 1 insertion(+) diff --git a/version.h b/version.h index 8ca3ea4..352ba8e 100644 --- a/version.h +++ b/version.h @@ -6,6 +6,7 @@ #define VERSION "4.0.2" // 4.0.2 EXRAIL additions: +// ACK defaults set to 50mA LIMIT, 2000uS MIN, 20000uS MAX // myFilter automatic detection (no need to call setFilter) // FIX negative route ids in WIthrottle problem. // IFRED(signal_id), IFAMBER(signal_id), IFGREEN(signal_id) From afd94f0645eb4dc71175ef6360a9d5fb5cc5fce7 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 29 Apr 2022 11:56:17 +0100 Subject: [PATCH 32/41] Improved SIGNALs startup and diagnostics --- EXRAIL2.cpp | 17 +++++++---------- IO_PCA9685.cpp | 6 +++--- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 6e7e005..599411e 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -169,14 +169,10 @@ int16_t LookList::find(int16_t value) { // Second pass startup, define any turnouts or servos, set signals red // add sequences onRoutines to the lookups - for (int sigpos=0;;sigpos+=3) { - VPIN redpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos); - if (redpin==0) break; // end of signal list - VPIN amberpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+1); - VPIN greenpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+2); - IODevice::write(redpin,true); - if (amberpin) IODevice::write(amberpin,false); - IODevice::write(greenpin,false); + for (int sigpos=0;;sigpos+=4) { + VPIN sigid=GETFLASHW(RMFT2::SignalDefinitions+sigpos); + if (sigid==0) break; // end of signal list + doSignal(sigid & (~ SERVO_SIGNAL_FLAG) & (~ACTIVE_HIGH_SIGNAL_FLAG), SIGNAL_RED); } for (progCounter=0;; SKIPOP){ @@ -993,7 +989,7 @@ int16_t RMFT2::getSignalSlot(VPIN id) { } } /* static */ void RMFT2::doSignal(VPIN id,char rag) { - //if (diag) DIAG(F(" dosignal %d %x"),id,rag); + if (diag) DIAG(F(" doSignal %d %x"),id,rag); int16_t sigslot=getSignalSlot(id); if (sigslot<0) return; @@ -1006,12 +1002,13 @@ int16_t RMFT2::getSignalSlot(VPIN id) { VPIN redpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+1); VPIN amberpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+2); VPIN greenpin=GETFLASHW(RMFT2::SignalDefinitions+sigpos+3); - //if (diag) DIAG(F("signal %d %d %d"),redpin,amberpin,greenpin); + if (diag) DIAG(F("signal %d %d %d %d"),sigid,redpin,amberpin,greenpin); if (sigid & SERVO_SIGNAL_FLAG) { // A servo signal, the pin numbers are actually servo positions // Note, setting a signal to a zero position has no effect. int16_t servopos= rag==SIGNAL_RED? redpin: (rag==SIGNAL_GREEN? greenpin : amberpin); + if (diag) DIAG(F("sigA %d %d"),id,servopos); if (servopos!=0) IODevice::writeAnalogue(id,servopos,PCA9685::Bounce); return; } diff --git a/IO_PCA9685.cpp b/IO_PCA9685.cpp index 009ff63..8270509 100644 --- a/IO_PCA9685.cpp +++ b/IO_PCA9685.cpp @@ -139,11 +139,11 @@ void PCA9685::_write(VPIN vpin, int value) { // 4 (Bounce) Servo 'bounces' at extremes. // void PCA9685::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { - if (_deviceState == DEVSTATE_FAILED) return; #ifdef DIAG_IO - DIAG(F("PCA9685 WriteAnalogue Vpin:%d Value:%d Profile:%d Duration:%d"), - vpin, value, profile, duration); + DIAG(F("PCA9685 WriteAnalogue Vpin:%d Value:%d Profile:%d Duration:%d %S"), + vpin, value, profile, duration, _deviceState == DEVSTATE_FAILED?F("DEVSTATE_FAILED"):F("")); #endif + if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; if (value > 4095) value = 4095; else if (value < 0) value = 0; From c58a126dfc4e7193baa40a42c51f81a140a5393b Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 29 Apr 2022 17:08:42 +0100 Subject: [PATCH 33/41] Update IO_PCA9685.cpp --- IO_PCA9685.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/IO_PCA9685.cpp b/IO_PCA9685.cpp index 8270509..23b09df 100644 --- a/IO_PCA9685.cpp +++ b/IO_PCA9685.cpp @@ -114,7 +114,6 @@ void PCA9685::_begin() { // Device-specific write function, invoked from IODevice::write(). // For this function, the configured profile is used. void PCA9685::_write(VPIN vpin, int value) { - if (_deviceState == DEVSTATE_FAILED) return; #ifdef DIAG_IO DIAG(F("PCA9685 Write Vpin:%d Value:%d"), vpin, value); #endif @@ -125,7 +124,10 @@ void PCA9685::_write(VPIN vpin, int value) { if (s != NULL) { // Use configured parameters _writeAnalogue(vpin, value ? s->activePosition : s->inactivePosition, s->profile, s->duration); - } // else { /* ignorethe request */ } + } else { + /* simulate digital pin on PWM */ + _writeAnalogue(vpin, value ? 4095 : 0, Instant, 0); + } } // Device-specific writeAnalogue function, invoked from IODevice::writeAnalogue(). From e7fb3648b03263d044ea87cd954edc5d6cf478cf Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 29 Apr 2022 19:33:44 +0100 Subject: [PATCH 34/41] Allow turnout id 0 --- EXRAIL2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 599411e..60c66c6 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -450,7 +450,7 @@ RMFT2::RMFT2(int progCtr) { invert=false; timeoutFlag=false; stackDepth=0; - onTurnoutId=0; // Not handling an ONTHROW/ONCLOSE + onTurnoutId=-1; // Not handling an ONTHROW/ONCLOSE // chain into ring of RMFTs if (loopTask==NULL) { From f7d64d5449e5d9abd3d5b4810a145bf3455ad415 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 29 Apr 2022 19:34:08 +0100 Subject: [PATCH 35/41] Position servo pin used as GPIO --- IO_PCA9685.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IO_PCA9685.cpp b/IO_PCA9685.cpp index 23b09df..036f634 100644 --- a/IO_PCA9685.cpp +++ b/IO_PCA9685.cpp @@ -155,7 +155,7 @@ void PCA9685::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t dur // Servo pin not configured, so configure now using defaults s = _servoData[pin] = (struct ServoData *) calloc(sizeof(struct ServoData), 1); if (s == NULL) return; // Check for memory allocation failure - s->activePosition = 0; + s->activePosition = 4095; s->inactivePosition = 0; s->currentPosition = value; s->profile = Instant; // Use instant profile (but not this time) From 85c437b10807d2489872d3bfb8a2880635ae14dd Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 29 Apr 2022 23:06:44 +0100 Subject: [PATCH 36/41] NoPowerOff LEDS --- IO_PCA9685.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IO_PCA9685.cpp b/IO_PCA9685.cpp index 036f634..55065b7 100644 --- a/IO_PCA9685.cpp +++ b/IO_PCA9685.cpp @@ -126,7 +126,7 @@ void PCA9685::_write(VPIN vpin, int value) { _writeAnalogue(vpin, value ? s->activePosition : s->inactivePosition, s->profile, s->duration); } else { /* simulate digital pin on PWM */ - _writeAnalogue(vpin, value ? 4095 : 0, Instant, 0); + _writeAnalogue(vpin, value ? 4095 : 0, Instant | NoPowerOff, 0); } } @@ -158,7 +158,7 @@ void PCA9685::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t dur s->activePosition = 4095; s->inactivePosition = 0; s->currentPosition = value; - s->profile = Instant; // Use instant profile (but not this time) + s->profile = Instant | NoPowerOff; // Use instant profile (but not this time) } // Animated profile. Initiate the appropriate action. From 0dc91451d92e41a1b0d43a282a729aee1b56988c Mon Sep 17 00:00:00 2001 From: Ash-4 <81280775+Ash-4@users.noreply.github.com> Date: Fri, 29 Apr 2022 23:14:27 -0500 Subject: [PATCH 37/41] CALLBACK parameter optional for Write --- DCCEXParser.h | 1 + 1 file changed, 1 insertion(+) diff --git a/DCCEXParser.h b/DCCEXParser.h index 227e3f0..d9fe0e1 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -60,6 +60,7 @@ struct DCCEXParser static int16_t stashP[MAX_COMMAND_PARAMS]; static bool stashCallback(Print * stream, int16_t p[MAX_COMMAND_PARAMS], RingStream * ringStream); static void callback_W(int16_t result); + static void callback_W4(int16_t result); static void callback_B(int16_t result); static void callback_R(int16_t result); static void callback_Rloco(int16_t result); From b2df10a99a506487002c155e8eed1d710c1bed4a Mon Sep 17 00:00:00 2001 From: Ash-4 <81280775+Ash-4@users.noreply.github.com> Date: Fri, 29 Apr 2022 23:23:15 -0500 Subject: [PATCH 38/41] WRITE CV ON PROG Callback parameters are now optional on PROG --- DCCEXParser.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 0d8b537..185c87d 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -352,7 +352,9 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) break; if (params == 1) // Write new loco id (clearing consist and managing short/long) DCC::setLocoId(p[0],callback_Wloco); - else // WRITE CV ON PROG + else if (params == 4) // WRITE CV ON PROG + DCC::writeCVByte(p[0], p[1], callback_W4); + else // WRITE CV ON PROG DCC::writeCVByte(p[0], p[1], callback_W); return; @@ -947,7 +949,14 @@ void DCCEXParser::commitAsyncReplyStream() { void DCCEXParser::callback_W(int16_t result) { StringFormatter::send(getAsyncReplyStream(), - F("\n"), stashP[2], stashP[3], stashP[0], result == 1 ? stashP[1] : -1); + F("\n"), stashP[0], result == 1 ? stashP[1] : -1); + commitAsyncReplyStream(); +} + +void DCCEXParser::callback_W4(int16_t result) +{ + StringFormatter::send(getAsyncReplyStream(), + F("\n"), stashP[2], stashP[3], stashP[0], result == 1 ? stashP[1] : -1); commitAsyncReplyStream(); } From 7b40bd3290a0da11cc1362f419099db944557fc4 Mon Sep 17 00:00:00 2001 From: Ash-4 <81280775+Ash-4@users.noreply.github.com> Date: Mon, 2 May 2022 18:58:03 -0500 Subject: [PATCH 39/41] Updated CV read command Equivalent to uses the verify callback. --- DCCEXParser.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 185c87d..2dfac84 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -382,6 +382,13 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) return; case 'R': // READ CV ON PROG + if (params == 1) + { // -- uses verify callback + if (!stashCallback(stream, p, ringStream)) + break; + DCC::verifyCVByte(p[0], p[1], callback_Vbyte); + return; + } if (params == 3) { // if (!stashCallback(stream, p, ringStream)) From a614a616fafd5410177f3595e61871b66b498318 Mon Sep 17 00:00:00 2001 From: Ash-4 <81280775+Ash-4@users.noreply.github.com> Date: Wed, 4 May 2022 13:44:12 -0500 Subject: [PATCH 40/41] struct TurnoutData to enable EEPROM from v 4.0 --- Turnouts.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Turnouts.h b/Turnouts.h index 3d1f0bc..709ab6c 100644 --- a/Turnouts.h +++ b/Turnouts.h @@ -61,8 +61,8 @@ protected: struct { bool closed : 1; bool _rfu: 2; - bool hidden: 1; uint8_t turnoutType : 4; + bool hidden: 1; }; uint8_t flags; }; From 2d3794724654056403e98e5edc6427ef233f5b9e Mon Sep 17 00:00:00 2001 From: Ash-4 <81280775+Ash-4@users.noreply.github.com> Date: Wed, 4 May 2022 14:33:08 -0500 Subject: [PATCH 41/41] Update version.h --- version.h | 1 - 1 file changed, 1 deletion(-) diff --git a/version.h b/version.h index 9d5fedd..bea474f 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,6 @@ #include "StringFormatter.h" - #define VERSION "4.1.0" // 4.0.2 EXRAIL additions: // ACK defaults set to 50mA LIMIT, 2000uS MIN, 20000uS MAX