diff --git a/DCCACK.cpp b/DCCACK.cpp
index 1b339ee..8a074b4 100644
--- a/DCCACK.cpp
+++ b/DCCACK.cpp
@@ -351,7 +351,7 @@ void DCCACK::callback(int value) {
switch (callbackState) {
case AFTER_READ:
- if (ackManagerRejoin && autoPowerOff) {
+ if (ackManagerRejoin && !autoPowerOff) {
progDriver->setPower(POWERMODE::OFF);
callbackStart=millis();
callbackState=WAITING_30;
diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp
index 05e0eca..08aae01 100644
--- a/DCCEXParser.cpp
+++ b/DCCEXParser.cpp
@@ -25,6 +25,79 @@
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see .
*/
+
+/*
+List of single character OPCODEs in use for reference.
+
+When determining a new OPCODE for a new feature, refer to this list as the source of truth.
+
+Once a new OPCODE is decided upon, update this list.
+
+ Character, Usage
+ /, |EX-R| interactive commands
+ -, Remove from reminder table
+ =, |TM| configuration
+ !, Emergency stop
+ @, Reserved for future use - LCD messages to JMRI
+ #, Request number of supported cabs/locos; heartbeat
+ +, WiFi AT commands
+ ?, Reserved for future use
+ 0, Track power off
+ 1, Track power on
+ a, DCC accessory control
+ A,
+ b, Write CV bit on main
+ B, Write CV bit
+ c, Request current command
+ C,
+ d,
+ D, Diagnostic commands
+ e, Erase EEPROM
+ E, Store configuration in EEPROM
+ f, Loco decoder function control (deprecated)
+ F, Loco decoder function control
+ g,
+ G,
+ h,
+ H, Turnout state broadcast
+ i, Reserved for future use - Turntable object broadcast
+ I, Reserved for future use - Turntable object command and control
+ j, Throttle responses
+ J, Throttle queries
+ k, Reserved for future use - Potentially Railcom
+ K, Reserved for future use - Potentially Railcom
+ l, Loco speedbyte/function map broadcast
+ L,
+ m,
+ M, Write DCC packet
+ n,
+ N,
+ o,
+ O, Output broadcast
+ p, Broadcast power state
+ P, Write DCC packet
+ q, Sensor deactivated
+ Q, Sensor activated
+ r, Broadcast address read on programming track
+ R, Read CVs
+ s, Display status
+ S, Sensor configuration
+ t, Cab/loco update command
+ T, Turnout configuration/control
+ u, Reserved for user commands
+ U, Reserved for user commands
+ v,
+ V, Verify CVs
+ w, Write CV on main
+ W, Write CV
+ x,
+ X, Invalid command
+ y,
+ Y, Output broadcast
+ z,
+ Z, Output configuration/control
+*/
+
#include "StringFormatter.h"
#include "DCCEXParser.h"
#include "DCC.h"
@@ -383,12 +456,16 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
#ifndef DISABLE_PROG
case 'w': // WRITE CV on MAIN
- DCC::writeCVByteMain(p[0], p[1], p[2]);
- return;
+ if (params != 3)
+ break;
+ DCC::writeCVByteMain(p[0], p[1], p[2]);
+ return;
case 'b': // WRITE CV BIT ON MAIN
- DCC::writeCVBitMain(p[0], p[1], p[2], p[3]);
- return;
+ if (params != 4)
+ break;
+ DCC::writeCVBitMain(p[0], p[1], p[2], p[3]);
+ return;
#endif
case 'M': // WRITE TRANSPARENT DCC PACKET MAIN
@@ -411,14 +488,16 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
#ifndef DISABLE_PROG
case 'W': // WRITE CV ON PROG
- if (!stashCallback(stream, p, ringStream))
- break;
+ if (!stashCallback(stream, p, ringStream))
+ break;
if (params == 1) // Write new loco id (clearing consist and managing short/long)
DCC::setLocoId(p[0],callback_Wloco);
else if (params == 4) // WRITE CV ON PROG
DCC::writeCVByte(p[0], p[1], callback_W4);
- else // WRITE CV ON PROG
+ else if (params == 2) // WRITE CV ON PROG
DCC::writeCVByte(p[0], p[1], callback_W);
+ else
+ break;
return;
case 'V': // VERIFY CV ON PROG
@@ -438,9 +517,11 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
}
break;
- case 'B': // WRITE CV BIT ON PROG
+ case 'B': // WRITE CV BIT ON PROG or
+ if (params != 3 && params != 5)
+ break;
if (!stashCallback(stream, p, ringStream))
- break;
+ break;
DCC::writeCVBit(p[0], p[1], p[2], callback_B);
return;
@@ -570,12 +651,12 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
case ' ': // < >
StringFormatter::send(stream, F("\n"));
return;
-
+#ifndef DISABLE_DIAG
case 'D': // < >
if (parseD(stream, params, p))
return;
- return;
-
+ break;
+#endif
case '=': // <= Track manager control >
if (TrackManager::parseJ(stream, params, p))
return;
diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp
index c8ff8f3..fae6add 100644
--- a/EXRAIL2.cpp
+++ b/EXRAIL2.cpp
@@ -2,7 +2,7 @@
* © 2021 Neil McKechnie
* © 2021-2023 Harald Barth
* © 2020-2023 Chris Harlow
- * © 2022 Colin Murdoch
+ * © 2022-2023 Colin Murdoch
* All rights reserved.
*
* This file is part of CommandStation-EX
@@ -99,6 +99,7 @@ LookList * RMFT2::onClockLookup=NULL;
#ifndef IO_NO_HAL
LookList * RMFT2::onRotateLookup=NULL;
#endif
+LookList * RMFT2::onOverloadLookup=NULL;
#define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter)
#define SKIPOP progCounter+=3
@@ -183,6 +184,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
#ifndef IO_NO_HAL
onRotateLookup=LookListLoader(OPCODE_ONROTATE);
#endif
+ onOverloadLookup=LookListLoader(OPCODE_ONOVERLOAD);
// Second pass startup, define any turnouts or servos, set signals red
// add sequences onRoutines to the lookups
@@ -202,6 +204,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
case OPCODE_AT:
case OPCODE_ATTIMEOUT2:
case OPCODE_AFTER:
+ case OPCODE_AFTEROVERLOAD:
case OPCODE_IF:
case OPCODE_IFNOT: {
int16_t pin = (int16_t)operand;
@@ -737,7 +740,17 @@ void RMFT2::loop2() {
}
if (millis()-waitAfter < 500 ) return;
break;
-
+
+ case OPCODE_AFTEROVERLOAD: // waits for the power to be turned back on - either by power routine or button
+ if (!TrackManager::isPowerOn(operand)) {
+ // reset timer to half a second and keep waiting
+ waitAfter=millis();
+ delayMe(50);
+ return;
+ }
+ if (millis()-waitAfter < 500 ) return;
+ break;
+
case OPCODE_LATCH:
setFlag(operand,LATCH_FLAG);
break;
@@ -1060,6 +1073,7 @@ void RMFT2::loop2() {
case OPCODE_TTADDPOSITION: // Turntable position definition ignored at runtime
case OPCODE_ONROTATE:
#endif
+ case OPCODE_ONOVERLOAD:
break;
@@ -1221,6 +1235,16 @@ void RMFT2::clockEvent(int16_t clocktime, bool change) {
}
}
+void RMFT2::powerEvent(int16_t track, bool overload) {
+ // Hunt for an ONOVERLOAD for this item
+ if (Diag::CMD)
+ DIAG(F("Looking for Power event on track : %c"), track);
+ if (overload) {
+ handleEvent(F("POWER"),onOverloadLookup,track);
+ }
+}
+
+
void RMFT2::handleEvent(const FSH* reason,LookList* handlers, int16_t id) {
int pc= handlers->find(id);
if (pc<0) return;
diff --git a/EXRAIL2.h b/EXRAIL2.h
index 5247e3e..8af36d2 100644
--- a/EXRAIL2.h
+++ b/EXRAIL2.h
@@ -1,7 +1,7 @@
/*
* © 2021 Neil McKechnie
* © 2020-2022 Chris Harlow
- * © 2022 Colin Murdoch
+ * © 2022-2023 Colin Murdoch
* © 2023 Harald Barth
* All rights reserved.
*
@@ -36,7 +36,8 @@
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_AT,OPCODE_AFTER,
+ OPCODE_AFTEROVERLOAD,OPCODE_AUTOSTART,
OPCODE_ATGTE,OPCODE_ATLT,
OPCODE_ATTIMEOUT1,OPCODE_ATTIMEOUT2,
OPCODE_LATCH,OPCODE_UNLATCH,OPCODE_SET,OPCODE_RESET,
@@ -67,6 +68,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
OPCODE_TTADDPOSITION,OPCODE_DCCTURNTABLE,OPCODE_EXTTTURNTABLE,
OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_IFTTPOSITION,OPCODE_WAITFORTT,
#endif
+ OPCODE_ONOVERLOAD,
// OPcodes below this point are skip-nesting IF operations
// placed here so that they may be skipped as a group
@@ -136,6 +138,7 @@ class LookList {
static void changeEvent(int16_t id, bool change);
static void clockEvent(int16_t clocktime, bool change);
static void rotateEvent(int16_t id, bool change);
+ static void powerEvent(int16_t track, bool overload);
static const int16_t SERVO_SIGNAL_FLAG=0x4000;
static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000;
static const int16_t DCC_SIGNAL_FLAG=0x1000;
@@ -202,6 +205,7 @@ private:
#ifndef IO_NO_HAL
static LookList * onRotateLookup;
#endif
+ static LookList * onOverloadLookup;
// Local variables - exist for each instance/task
RMFT2 *next; // loop chain
diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h
index c8ff395..11c598b 100644
--- a/EXRAIL2MacroReset.h
+++ b/EXRAIL2MacroReset.h
@@ -1,6 +1,6 @@
/*
* © 2020-2022 Chris Harlow. All rights reserved.
- * © 2022 Colin Murdoch
+ * © 2022-2023 Colin Murdoch
* © 2023 Harald Barth
*
* This file is part of CommandStation-EX
@@ -27,6 +27,7 @@
#undef ACTIVATE
#undef ACTIVATEL
#undef AFTER
+#undef AFTEROVERLOAD
#undef ALIAS
#undef AMBER
#undef ANOUT
@@ -96,6 +97,7 @@
#undef ONTIME
#undef ONCLOCKTIME
#undef ONCLOCKMINS
+#undef ONOVERLOAD
#undef ONGREEN
#undef ONRED
#undef ONROTATE
@@ -162,6 +164,7 @@
#define ACTIVATE(addr,subaddr)
#define ACTIVATEL(addr)
#define AFTER(sensor_id)
+#define AFTEROVERLOAD(track_id)
#define ALIAS(name,value...)
#define AMBER(signal_id)
#define ANOUT(vpin,value,param1,param2)
@@ -228,6 +231,7 @@
#define ONTIME(value)
#define ONCLOCKTIME(hours,mins)
#define ONCLOCKMINS(mins)
+#define ONOVERLOAD(track_id)
#define ONDEACTIVATE(addr,subaddr)
#define ONDEACTIVATEL(linear)
#define ONCLOSE(turnout_id)
diff --git a/EXRAILMacros.h b/EXRAILMacros.h
index 39e5dda..0784242 100644
--- a/EXRAILMacros.h
+++ b/EXRAILMacros.h
@@ -1,7 +1,7 @@
/*
* © 2021 Neil McKechnie
* © 2020-2022 Chris Harlow
- * © 2022 Colin Murdoch
+ * © 2022-2023 Colin Murdoch
* © 2023 Harald Barth
* All rights reserved.
*
@@ -286,6 +286,7 @@ const HIGHFLASH 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 AFTEROVERLOAD(track_id) OPCODE_AFTEROVERLOAD,V(TRACK_NUMBER_##track_id),
#define ALIAS(name,value...)
#define AMBER(signal_id) OPCODE_AMBER,V(signal_id),
#define ANOUT(vpin,value,param1,param2) OPCODE_SERVO,V(vpin),OPCODE_PAD,V(value),OPCODE_PAD,V(param1),OPCODE_PAD,V(param2),
@@ -359,6 +360,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
#define ONTIME(value) OPCODE_ONTIME,V(value),
#define ONCLOCKTIME(hours,mins) OPCODE_ONTIME,V((STRIP_ZERO(hours)*60)+STRIP_ZERO(mins)),
#define ONCLOCKMINS(mins) ONCLOCKTIME(25,mins)
+#define ONOVERLOAD(track_id) OPCODE_ONOVERLOAD,V(TRACK_NUMBER_##track_id),
#define ONDEACTIVATE(addr,subaddr) OPCODE_ONDEACTIVATE,V(addr<<2|subaddr),
#define ONDEACTIVATEL(linear) OPCODE_ONDEACTIVATE,V(linear+3),
#define ONGREEN(signal_id) OPCODE_ONGREEN,V(signal_id),
diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h
index de4f953..98afa03 100644
--- a/GITHUB_SHA.h
+++ b/GITHUB_SHA.h
@@ -1 +1 @@
-#define GITHUB_SHA "devel-202308102205Z"
+#define GITHUB_SHA "devel-202308302157Z"
diff --git a/IO_PCA9555.h b/IO_PCA9555.h
index 137e287..e493165 100644
--- a/IO_PCA9555.h
+++ b/IO_PCA9555.h
@@ -30,8 +30,8 @@
class PCA9555 : public GPIOBase {
public:
- static void create(VPIN vpin, int nPins, uint8_t I2CAddress, int interruptPin=-1) {
- new PCA9555(vpin, min(nPins,16), I2CAddress, interruptPin);
+ static void create(VPIN vpin, uint8_t nPins, I2CAddress i2cAddress, int interruptPin=-1) {
+ if (checkNoOverlap(vpin, nPins, i2cAddress)) new PCA9555(vpin,nPins, i2cAddress, interruptPin);
}
// Constructor
diff --git a/MotorDriver.cpp b/MotorDriver.cpp
index d5dca13..4644ad5 100644
--- a/MotorDriver.cpp
+++ b/MotorDriver.cpp
@@ -4,6 +4,7 @@
* © 2021 Fred Decker
* © 2020-2023 Harald Barth
* © 2020-2021 Chris Harlow
+ * © 2023 Colin Murdoch
* All rights reserved.
*
* This file is part of CommandStation-EX
@@ -26,6 +27,7 @@
#include "DCCWaveform.h"
#include "DCCTimer.h"
#include "DIAG.h"
+#include "EXRAIL2.h"
unsigned long MotorDriver::globalOverloadStart = 0;
@@ -613,7 +615,9 @@ void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) {
// adjust next wait time
power_sample_overload_wait *= 2;
if (power_sample_overload_wait > POWER_SAMPLE_RETRY_MAX)
- power_sample_overload_wait = POWER_SAMPLE_RETRY_MAX;
+ power_sample_overload_wait = POWER_SAMPLE_RETRY_MAX;
+ DIAG(F("Calling EXRAIL"));
+ RMFT2::powerEvent(trackno, true); // Tell EXRAIL we have an overload
// power on test
DIAG(F("TRACK %c POWER RESTORE (after %4M)"), trackno + 'A', mslpc);
setPower(POWERMODE::ALERT);
diff --git a/TrackManager.cpp b/TrackManager.cpp
index 0f69235..bd7c623 100644
--- a/TrackManager.cpp
+++ b/TrackManager.cpp
@@ -1,6 +1,7 @@
/*
* © 2022 Chris Harlow
* © 2022 Harald Barth
+ * © 2023 Colin Murdoch
* All rights reserved.
*
* This file is part of DCC++EX
@@ -53,7 +54,7 @@ bool TrackManager::progTrackSyncMain=false;
bool TrackManager::progTrackBoosted=false;
int16_t TrackManager::joinRelay=UNUSED_PIN;
#ifdef ARDUINO_ARCH_ESP32
-byte TrackManager::tempProgTrack=MAX_TRACKS+1;
+byte TrackManager::tempProgTrack=MAX_TRACKS+1; // MAX_TRACKS+1 is the unused flag
#endif
#ifdef ANALOG_READ_INTERRUPT
@@ -505,7 +506,12 @@ void TrackManager::setJoin(bool joined) {
}
} else {
if (tempProgTrack != MAX_TRACKS+1) {
+ // as setTrackMode with TRACK_MODE_PROG defaults to
+ // power off, we will take the current power state
+ // of our track and then preserve that state.
+ POWERMODE tPTmode = track[tempProgTrack]->getPower(); //get current power status of this track
setTrackMode(tempProgTrack, TRACK_MODE_PROG);
+ track[tempProgTrack]->setPower(tPTmode); //set track status as it was before
tempProgTrack = MAX_TRACKS+1;
}
}
@@ -513,3 +519,10 @@ void TrackManager::setJoin(bool joined) {
progTrackSyncMain=joined;
if (joinRelay!=UNUSED_PIN) digitalWrite(joinRelay,joined?HIGH:LOW);
}
+
+bool TrackManager::isPowerOn(byte t) {
+ if (track[t]->getPower()!=POWERMODE::ON)
+ return false;
+ return true;
+ }
+
diff --git a/TrackManager.h b/TrackManager.h
index 965cfa3..60d5f24 100644
--- a/TrackManager.h
+++ b/TrackManager.h
@@ -1,6 +1,8 @@
/*
* © 2022 Chris Harlow
* © 2022 Harald Barth
+ * © 2023 Colin Murdoch
+ *
* All rights reserved.
*
* This file is part of CommandStation-EX
@@ -77,6 +79,7 @@ class TrackManager {
static void reportCurrent(Print* stream);
static void reportObsoleteCurrent(Print* stream);
static void streamTrackState(Print* stream, byte t);
+ static bool isPowerOn(byte t);
static int16_t joinRelay;
static bool progTrackSyncMain; // true when prog track is a siding switched to main
diff --git a/myHal.cpp_example.txt b/myHal.cpp_example.txt
index d93ea5c..5e9fec4 100644
--- a/myHal.cpp_example.txt
+++ b/myHal.cpp_example.txt
@@ -51,7 +51,7 @@ void halSetup() {
// Create a 20x4 LCD display device as display number 2
// (line 0 is written by EX-RAIL 'SCREEN(2, 0, "text")').
- // HALDisplay(2, 0x27, 20, 4);
+ // HALDisplay::create(2, 0x27, 20, 4);
//=======================================================================
diff --git a/version.h b/version.h
index 076daef..abcc577 100644
--- a/version.h
+++ b/version.h
@@ -3,8 +3,13 @@
#include "StringFormatter.h"
-#define VERSION "5.0.1"
-// 5.0.1 - Check bad AT firmware version
+#define VERSION "5.1.4"
+// 5.1.4 - Added ONOVERLOAD & AFTEROVERLOAD to EXRAIL
+// 5.1.3 - Make parser more fool proof
+// 5.1.2 - Bugfix: ESP32 30ms off time
+// 5.1.1 - Check bad AT firmware version
+// - Update IO_PCA9555.h reflecting IO_MCP23017.h changes to support PCA9548 mux
+// 5.0.1 - Bugfix: execute 30ms off time before rejoin
// 5.0.0 - Make 4.2.69 the 5.0.0 release
// 4.2.69 - Bugfix: Make work in DC mode
// 4.2.68 - Rename track mode OFF to NONE