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