From 1101cfd6377702fe4e3f03fd361d63cf48ec9bef Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 24 Feb 2024 17:24:55 +0100 Subject: [PATCH 01/89] surpress warnings --- IO_EXTurntable.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index aeb935b..0e134d4 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -83,6 +83,7 @@ void EXTurntable::_loop(unsigned long currentMicros) { // Read returns status as obtained in our loop. // Return false if our status value is invalid. int EXTurntable::_read(VPIN vpin) { + (void)vpin; // surpress warning if (_deviceState == DEVSTATE_FAILED) return 0; if (_stepperStatus > 1) { return false; @@ -127,6 +128,8 @@ void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_ vpin, value, activity, duration); DIAG(F("I2CManager write I2C Address:%d stepsMSB:%d stepsLSB:%d activity:%d"), _I2CAddress.toString(), stepsMSB, stepsLSB, activity); +#else + (void)duration; #endif if (activity < 4) _stepperStatus = 1; // Tell the device driver Turntable-EX is busy _previousStatus = _stepperStatus; From b29a01f436daab54978cd00b0d9af1830672def1 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 24 Feb 2024 20:56:06 +0100 Subject: [PATCH 02/89] ESP32: Use the BOOSTER_INPUT define --- TrackManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index a77108b..5aba978 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -219,7 +219,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr if (mode & TRACK_MODE_BOOST) { //DIAG(F("Track=%c mode boost pin %d"),trackToSet+'A', p.pin); pinMode(BOOSTER_INPUT, INPUT); - gpio_matrix_in(26, SIG_IN_FUNC228_IDX, false); //pads 224 to 228 available as loopback + gpio_matrix_in(BOOSTER_INPUT, SIG_IN_FUNC228_IDX, false); //pads 224 to 228 available as loopback gpio_matrix_out(p.pin, SIG_IN_FUNC228_IDX, false, false); if (p.invpin != UNUSED_PIN) { gpio_matrix_out(p.invpin, SIG_IN_FUNC228_IDX, true /*inverted*/, false); From 274affce45d1c0d5e5527fdd524f607ffa058af8 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 24 Feb 2024 20:57:38 +0100 Subject: [PATCH 03/89] version 5.2.37 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 9c706cf..699ead5 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202402201404Z" +#define GITHUB_SHA "devel-202402241957Z" diff --git a/version.h b/version.h index dcc7c6e..ee8c827 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.36" +#define VERSION "5.2.37" +// 5.2.37 - Bugfix ESP32: Use BOOSTER_INPUT define // 5.2.36 - Variable frequency for DC mode // 5.2.35 - Bugfix: Make DCC Extended Accessories follow RCN-213 // 5.2.34 - Command fopr DCC Extended Accessories From 7503421eb6bea5f7c3758cb0604c54829df826c3 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 26 Feb 2024 09:11:21 +0100 Subject: [PATCH 04/89] Compile time optimization for booster mode --- MotorDriver.h | 12 +++++++++--- TrackManager.cpp | 9 +++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/MotorDriver.h b/MotorDriver.h index b678a84..4491164 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -34,9 +34,15 @@ template inline T operator| (T a, T b) { return (T)((int)a | (int)b); } template inline T operator& (T a, T b) { return (T)((int)a & (int)b); } template inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); } enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4, - TRACK_MODE_DC = 8, TRACK_MODE_EXT = 16, TRACK_MODE_BOOST = 32, - TRACK_MODE_ALL = 62, // only to operate all tracks - TRACK_MODE_INV = 64, TRACK_MODE_DCX = 72 /*DC + INV*/, TRACK_MODE_AUTOINV = 128}; + TRACK_MODE_DC = 8, TRACK_MODE_EXT = 16, +#ifdef ARDUINO_ARCH_ESP32 + TRACK_MODE_BOOST = 32, +#else + TRACK_MODE_BOOST = 0, +#endif + TRACK_MODE_ALL = TRACK_MODE_MAIN|TRACK_MODE_PROG|TRACK_MODE_DC|TRACK_MODE_EXT|TRACK_MODE_BOOST, + TRACK_MODE_INV = 64, + TRACK_MODE_DCX = TRACK_MODE_DC|TRACK_MODE_INV, TRACK_MODE_AUTOINV = 128}; #define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH #define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW diff --git a/TrackManager.cpp b/TrackManager.cpp index 5aba978..e357e7f 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -362,7 +362,8 @@ bool TrackManager::parseEqualSign(Print *stream, int16_t params, int16_t p[]) if (params==2 && p[1]=="EXT"_hk) // <= id EXT> return setTrackMode(p[0],TRACK_MODE_EXT); #ifdef BOOSTER_INPUT - if (params==2 && p[1]=="BOOST"_hk) // <= id BOOST> + if (TRACK_MODE_BOOST != 0 && // compile time optimization + params==2 && p[1]=="BOOST"_hk) // <= id BOOST> return setTrackMode(p[0],TRACK_MODE_BOOST); #endif if (params==2 && p[1]=="AUTO"_hk) // <= id AUTO> @@ -401,11 +402,11 @@ const FSH* TrackManager::getModeName(TRACK_MODE tm) { modename=F("EXT"); else if(tm & TRACK_MODE_BOOST) { if(tm & TRACK_MODE_AUTOINV) - modename=F("B A"); + modename=F("BOOST A"); else if (tm & TRACK_MODE_INV) - modename=F("B I"); + modename=F("BOOST I"); else - modename=F("B"); + modename=F("BOOST"); } else if (tm & TRACK_MODE_DC) { if (tm & TRACK_MODE_INV) From 3d6f41398d5efac7fcd8e35a70510657649f4770 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 4 Mar 2024 15:07:03 +0100 Subject: [PATCH 05/89] compile time check WIFI_PASSWORD length for reasonable value --- CommandStation-EX.ino | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index 2cd9d33..3a0e5ca 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -65,6 +65,9 @@ #ifdef EXRAIL_WARNING #warning You have myAutomation.h but your hardware has not enough memory to do that, so EX-RAIL DISABLED #endif +// compile time check, passwords 1 to 7 chars do not work, so do not try to compile with them at all +// remember trailing '\0', sizeof("") == 1. +#define PASSWDCHECK(S) static_assert(sizeof(S) == 1 || sizeof(S) > 8, "Password shorter than 8 chars") void setup() { @@ -102,10 +105,12 @@ void setup() // Start Ethernet if it exists #ifndef ARDUINO_ARCH_ESP32 #if WIFI_ON + PASSWDCHECK(WIFI_PASSWORD); // compile time check WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP); #endif // WIFI_ON #else // ESP32 needs wifi on always + PASSWDCHECK(WIFI_PASSWORD); // compile time check WifiESP::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP); #endif // ARDUINO_ARCH_ESP32 From b75266689928b1040a06c90240a34970379301c9 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 4 Mar 2024 15:20:48 +0100 Subject: [PATCH 06/89] remove warning --- EXRAILMacros.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 825ce02..876bc23 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -350,6 +350,8 @@ const FSH * RMFT2::getTurntableDescription(int16_t turntableId) { #define TT_ADDPOSITION(turntable_id,position,value,home,description...) T_DESC(turntable_id,position,description) const FSH * RMFT2::getTurntablePositionDescription(int16_t turntableId, uint8_t positionId) { + (void)turntableId; + (void)positionId; #include "myAutomation.h" return NULL; } From 4b04a80e6fa5b4d2505f31eb53c284944d3cfbd6 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 5 Mar 2024 19:59:29 +0000 Subject: [PATCH 07/89] Remove unnecessary warning --- EXRAILMacros.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 876bc23..ca12e23 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -95,14 +95,14 @@ constexpr int16_t stuffSize=sizeof(compileTimeSequenceList)/sizeof(int16_t) - 1; // Compile time function to check for sequence nos. -constexpr bool hasseq(const int16_t value, const uint16_t pos=0 ) { +constexpr bool hasseq(const int16_t value, const int16_t pos=0 ) { return pos>=stuffSize? false : compileTimeSequenceList[pos]==value || hasseq(value,pos+1); } // Compile time function to check for duplicate sequence nos. -constexpr bool hasdup(const int16_t value, const uint16_t pos ) { +constexpr bool hasdup(const int16_t value, const int16_t pos ) { return pos>=stuffSize? false : compileTimeSequenceList[pos]==value || hasseq(value,pos+1) From be218d3032967a64326a1daddf954cf0baa8b35c Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 8 Mar 2024 20:33:11 +0000 Subject: [PATCH 08/89] EXRAIL MESSAGE() --- CommandDistributor.cpp | 5 +++++ CommandDistributor.h | 1 + EXRAIL2.cpp | 4 ++++ EXRAIL2.h | 2 +- EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 4 ++++ version.h | 4 +++- 7 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index f838fd2..67bcc54 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -312,6 +312,11 @@ void CommandDistributor::broadcastRaw(clientType type, char * msg) { broadcastReply(type, F("%s"),msg); } +void CommandDistributor::broadcastMessage(char * message) { + broadcastReply(COMMAND_TYPE, F("\n"),message); + broadcastReply(WITHROTTLE_TYPE, F("Hm%s\n"),message); +} + void CommandDistributor::broadcastTrackState(const FSH* format, byte trackLetter, const FSH *modename, int16_t dcAddr) { broadcastReply(COMMAND_TYPE, format, trackLetter, modename, dcAddr); } diff --git a/CommandDistributor.h b/CommandDistributor.h index e4dff5d..f68a5e2 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -60,6 +60,7 @@ public : static void forget(byte clientId); static void broadcastRouteState(uint16_t routeId,byte state); static void broadcastRouteCaption(uint16_t routeId,const FSH * caption); + static void broadcastMessage(char * message); // Handling code for virtual LCD receiver. static Print * getVirtualLCDSerial(byte screen, byte row); diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 9f1075c..c9c6716 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1334,6 +1334,7 @@ void RMFT2::thrungeString(uint32_t strfar, thrunger mode, byte id) { break; case thrunge_parse: case thrunge_broadcast: + case thrunge_message: case thrunge_lcd: default: // thrunge_lcd+1, ... if (!buffer) buffer=new StringBuffer(); @@ -1371,6 +1372,9 @@ void RMFT2::thrungeString(uint32_t strfar, thrunger mode, byte id) { case thrunge_withrottle: CommandDistributor::broadcastRaw(CommandDistributor::WITHROTTLE_TYPE,buffer->getString()); break; + case thrunge_message: + CommandDistributor::broadcastMessage(buffer->getString()); + break; case thrunge_lcd: LCD(id,F("%s"),buffer->getString()); break; diff --git a/EXRAIL2.h b/EXRAIL2.h index b4a06da..f4cf320 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -94,7 +94,7 @@ enum thrunger: byte { thrunge_serial,thrunge_parse, thrunge_serial1, thrunge_serial2, thrunge_serial3, thrunge_serial4, thrunge_serial5, thrunge_serial6, - thrunge_lcn, + thrunge_lcn,thrunge_message, thrunge_lcd, // Must be last!! }; diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 2428a09..e94c657 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -97,6 +97,7 @@ #undef LCCX #undef LCN #undef MOVETT +#undef MESSAGE #undef ONACTIVATE #undef ONACTIVATEL #undef ONAMBER @@ -253,6 +254,7 @@ #define LCD(row,msg) #define SCREEN(display,row,msg) #define LCN(msg) +#define MESSAGE(msg) #define MOVETT(id,steps,activity) #define ONACTIVATE(addr,subaddr) #define ONACTIVATEL(linear) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index ca12e23..5588811 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -253,6 +253,9 @@ const int StringMacroTracker1=__COUNTER__; #define PRINT(msg) THRUNGE(msg,thrunge_print) #undef LCN #define LCN(msg) THRUNGE(msg,thrunge_lcn) +#undef MESSAGE +#define MESSAGE(msg) THRUNGE(msg,thrunge_message) + #undef ROUTE_CAPTION #define ROUTE_CAPTION(id,caption) \ case (__COUNTER__ - StringMacroTracker1) : {\ @@ -516,6 +519,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define SCREEN(display,id,msg) PRINT(msg) #define STEALTH(code...) PRINT(dummy) #define LCN(msg) PRINT(msg) +#define MESSAGE(msg) PRINT(msg) #define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0), #define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr), #define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3), diff --git a/version.h b/version.h index ee8c827..97b0a70 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.37" +#define VERSION "5.2.38" +// 5.2.38 - Exrail MESSAGE("text") to send a user message to all +// connected throttles (uses and withrottle Hmtext. // 5.2.37 - Bugfix ESP32: Use BOOSTER_INPUT define // 5.2.36 - Variable frequency for DC mode // 5.2.35 - Bugfix: Make DCC Extended Accessories follow RCN-213 From 9aac34b403b07f02ffdcad5747aa151f21bf7a86 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 11 Mar 2024 12:26:28 +0000 Subject: [PATCH 09/89] comments --- DCCEXParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 2eeedc9..6f4bf52 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -68,7 +68,7 @@ Once a new OPCODE is decided upon, update this list. K, Reserved for future use - Potentially Railcom l, Loco speedbyte/function map broadcast L, Reserved for LCC interface (implemented in EXRAIL) - m, + m, message to throttles broadcast M, Write DCC packet n, N, From d753eb43e3d53a0c9d17ba98742b49cb27f3485a Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 11 Mar 2024 14:52:55 +0100 Subject: [PATCH 10/89] Functions for DC frequency: Use func up to F31 --- CommandDistributor.cpp | 4 +--- DCC.cpp | 2 +- DCCEXParser.cpp | 25 +++++++++++-------------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 67bcc54..e7087ad 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -209,9 +209,7 @@ int16_t CommandDistributor::retClockTime() { void CommandDistributor::broadcastLoco(byte slot) { DCC::LOCO * sp=&DCC::speedTable[slot]; - uint32_t func = sp->functions; - func = func & 0x1fffffff; // mask out bits 0-28 - broadcastReply(COMMAND_TYPE, F("\n"), sp->loco,slot,sp->speedCode,func); + broadcastReply(COMMAND_TYPE, F("\n"), sp->loco,slot,sp->speedCode,sp->functions); #ifdef SABERTOOTH if (Serial2 && sp->loco == SABERTOOTH) { static uint8_t rampingmode = 0; diff --git a/DCC.cpp b/DCC.cpp index 5ab7eff..36d91fb 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -242,7 +242,7 @@ void DCC::changeFn( int cab, int16_t functionNumber) { // Report function state (used from withrottle protocol) // returns 0 false, 1 true or -1 for do not know int8_t DCC::getFn( int cab, int16_t functionNumber) { - if (cab<=0 || functionNumber>28) + if (cab<=0 || functionNumber>31) return -1; // unknown int reg = lookupSpeedTable(cab); if (reg<0) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 6f4bf52..fa4c9f8 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -283,25 +283,22 @@ void DCCEXParser::parseOne(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==1) { // display state + int16_t slot=DCC::lookupSpeedTable(p[0],false); + if (slot>=0) + CommandDistributor::broadcastLoco(slot); + else // send dummy state speed 0 fwd no functions. + StringFormatter::send(stream,F("\n"),p[0]); + return; + } + if (params == 4) { // + // ignore register p[0] cab = p[1]; tspeed = p[2]; direction = p[3]; From 8eec85edcf742b42bdd53517baa9b271463151ca Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 11 Mar 2024 14:54:18 +0100 Subject: [PATCH 11/89] version 5.2.39 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 699ead5..29d9a87 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202402241957Z" +#define GITHUB_SHA "devel-202403111353Z" diff --git a/version.h b/version.h index 97b0a70..23e7e02 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.38" +#define VERSION "5.2.39" +// 5.2.39 - Functions for DC frequency: Use func up to F31 // 5.2.38 - Exrail MESSAGE("text") to send a user message to all // connected throttles (uses and withrottle Hmtext. // 5.2.37 - Bugfix ESP32: Use BOOSTER_INPUT define From e3081a7e56a58d2af0858a8ba80ba6f3e66dede2 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 12 Mar 2024 11:45:28 +0100 Subject: [PATCH 12/89] Functions for DC frequency: Use func up to F31 part 2 --- DCC.cpp | 7 ++++--- WiThrottle.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 36d91fb..a8bc953 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -219,8 +219,9 @@ bool DCC::setFn( int cab, int16_t functionNumber, bool on) { } else { speedTable[reg].functions &= ~funcmask; } - if (speedTable[reg].functions != previous && functionNumber <= 28) { - updateGroupflags(speedTable[reg].groupFlags, functionNumber); + if (speedTable[reg].functions != previous) { + if (functionNumber <= 28) + updateGroupflags(speedTable[reg].groupFlags, functionNumber); CommandDistributor::broadcastLoco(reg); } return true; @@ -235,8 +236,8 @@ void DCC::changeFn( int cab, int16_t functionNumber) { speedTable[reg].functions ^= funcmask; if (functionNumber <= 28) { updateGroupflags(speedTable[reg].groupFlags, functionNumber); - CommandDistributor::broadcastLoco(reg); } + CommandDistributor::broadcastLoco(reg); } // Report function state (used from withrottle protocol) diff --git a/WiThrottle.cpp b/WiThrottle.cpp index 3e712b3..f3d9253 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -571,7 +571,7 @@ void WiThrottle::sendRoutes(Print* stream) { void WiThrottle::sendFunctions(Print* stream, byte loco) { int16_t locoid=myLocos[loco].cab; - int fkeys=29; + int fkeys=32; // upper limit (send functions 0 to 31) myLocos[loco].functionToggles=1<<2; // F2 (HORN) is a non-toggle #ifdef EXRAIL_ACTIVE From 59d855549e087cababbb3cab368208fd6bdfb629 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 12 Mar 2024 11:47:25 +0100 Subject: [PATCH 13/89] version tag --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 29d9a87..c5e1882 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202403111353Z" +#define GITHUB_SHA "devel-202403121045Z" From 3cda869c6e15de7c00e7817172f248e2e2be01ea Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 18 Mar 2024 21:17:51 +0100 Subject: [PATCH 14/89] Allow no shield --- MotorDrivers.h | 6 +++++- TrackManager.cpp | 10 +++++++--- TrackManager.h | 6 +++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/MotorDrivers.h b/MotorDrivers.h index 907c11b..9e5f85b 100644 --- a/MotorDrivers.h +++ b/MotorDrivers.h @@ -1,7 +1,7 @@ /* * © 2022-2023 Paul M. Antoine * © 2021 Fred Decker - * © 2020-2023 Harald Barth + * © 2020-2024 Harald Barth * (c) 2020 Chris Harlow. All rights reserved. * (c) 2021 Fred Decker. All rights reserved. * (c) 2020 Harald Barth. All rights reserved. @@ -57,6 +57,10 @@ // of the brake pin on the motor bridge is inverted // (HIGH == release brake) +// You can have a CS wihout any possibility to do any track signal. +// That's strange but possible. +#define NO_SHIELD F("No shield at all") + // Arduino STANDARD Motor Shield, used on different architectures: #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_STM32) diff --git a/TrackManager.cpp b/TrackManager.cpp index e357e7f..338b11c 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -1,6 +1,6 @@ /* * © 2022 Chris Harlow - * © 2022,2023 Harald Barth + * © 2022-2024 Harald Barth * © 2023 Colin Murdoch * All rights reserved. * @@ -41,7 +41,7 @@ MotorDriver * TrackManager::track[MAX_TRACKS]; int16_t TrackManager::trackDCAddr[MAX_TRACKS]; -byte TrackManager::lastTrack=0; +int8_t TrackManager::lastTrack=-1; bool TrackManager::progTrackSyncMain=false; bool TrackManager::progTrackBoosted=false; int16_t TrackManager::joinRelay=UNUSED_PIN; @@ -498,7 +498,11 @@ void TrackManager::setTrackPower(TRACK_MODE trackmodeToMatch, POWERMODE powermod // Set track power for this track, inependent of mode void TrackManager::setTrackPower(POWERMODE powermode, byte t) { - MotorDriver *driver=track[t]; + MotorDriver *driver=track[t]; + if (driver == NULL) { // track is not defined at all + DIAG(F("Error: Track %c does not exist"), t+'A'); + return; + } TRACK_MODE trackmode = driver->getMode(); POWERMODE oldpower = driver->getPower(); if (trackmode & TRACK_MODE_NONE) { diff --git a/TrackManager.h b/TrackManager.h index c1f314a..7dce0ee 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -1,6 +1,6 @@ /* * © 2022 Chris Harlow - * © 2022 Harald Barth + * © 2022-2024 Harald Barth * © 2023 Colin Murdoch * * All rights reserved. @@ -46,7 +46,7 @@ const byte TRACK_POWER_1=1, TRACK_POWER_ON=1; class TrackManager { public: static void Setup(const FSH * shieldName, - MotorDriver * track0, + MotorDriver * track0=NULL, MotorDriver * track1=NULL, MotorDriver * track2=NULL, MotorDriver * track3=NULL, @@ -108,7 +108,7 @@ class TrackManager { private: static void addTrack(byte t, MotorDriver* driver); - static byte lastTrack; + static int8_t lastTrack; static byte nextCycleTrack; static void applyDCSpeed(byte t); From 0587e6fc097a708d76ce002d804df9c5d3865ff0 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 18 Mar 2024 21:18:57 +0100 Subject: [PATCH 15/89] version 5.2.40 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index c5e1882..e5f3571 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202403121045Z" +#define GITHUB_SHA "devel-202403182018Z" diff --git a/version.h b/version.h index 23e7e02..27a10f0 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.39" +#define VERSION "5.2.40" +// 5.2.40 - Allow no shield // 5.2.39 - Functions for DC frequency: Use func up to F31 // 5.2.38 - Exrail MESSAGE("text") to send a user message to all // connected throttles (uses and withrottle Hmtext. From 87073b0d36ad6c2b39d3c9cc45b7692f5d45fbe1 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 23 Mar 2024 13:31:34 +1000 Subject: [PATCH 16/89] Rotary Encoder address 0x67 --- IO_RotaryEncoder.h | 6 +++--- myHal.cpp_example.txt | 9 +++++---- version.h | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/IO_RotaryEncoder.h b/IO_RotaryEncoder.h index 9d40b34..2e6cfe7 100644 --- a/IO_RotaryEncoder.h +++ b/IO_RotaryEncoder.h @@ -42,9 +42,9 @@ * Defining in myAutomation.h requires the device driver to be included in addition to the HAL() statement. Examples: * * #include "IO_RotaryEncoder.h" -* HAL(RotaryEncoder, 700, 1, 0x70) // Define single Vpin, no feedback or position sent to rotary encoder software -* HAL(RotaryEncoder, 700, 2, 0x70) // Define two Vpins, feedback only sent to rotary encoder software -* HAL(RotaryEncoder, 700, 3, 0x70) // Define three Vpins, can send feedback and position update to rotary encoder software +* HAL(RotaryEncoder, 700, 1, 0x67) // Define single Vpin, no feedback or position sent to rotary encoder software +* HAL(RotaryEncoder, 700, 2, 0x67) // Define two Vpins, feedback only sent to rotary encoder software +* HAL(RotaryEncoder, 700, 3, 0x67) // Define three Vpins, can send feedback and position update to rotary encoder software * * Refer to the documentation for further information including the valid activities and examples. */ diff --git a/myHal.cpp_example.txt b/myHal.cpp_example.txt index 9073430..f715c63 100644 --- a/myHal.cpp_example.txt +++ b/myHal.cpp_example.txt @@ -311,12 +311,13 @@ void halSetup() { //======================================================================= // The parameters are: // firstVpin = First available Vpin to allocate - // numPins= Number of Vpins to allocate, can be either 1 or 2 - // i2cAddress = Available I2C address (default 0x70) + // numPins= Number of Vpins to allocate, can be either 1 to 3 + // i2cAddress = Available I2C address (default 0x67) //RotaryEncoder::create(firstVpin, numPins, i2cAddress); - //RotaryEncoder::create(700, 1, 0x70); - //RotaryEncoder::create(701, 2, 0x71); + //RotaryEncoder::create(700, 1, 0x67); + //RotaryEncoder::create(700, 2, 0x67); + //RotaryEncoder::create(700, 3, 0x67); //======================================================================= // The following directive defines an EX-FastClock instance. diff --git a/version.h b/version.h index 27a10f0..f292b43 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.40" +#define VERSION "5.2.41" +// 5.2.41 - Update rotary encoder default address to 0x67 // 5.2.40 - Allow no shield // 5.2.39 - Functions for DC frequency: Use func up to F31 // 5.2.38 - Exrail MESSAGE("text") to send a user message to all From c8f18e4d67e4edd87037718b4c255bd555466015 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 2 Apr 2024 00:02:09 +0200 Subject: [PATCH 17/89] ESP32 Bugfix: Uninitialized stack variable. Will bite you with infinite loop if no tracks are defined --- DCCWaveform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 2d50929..3d77e9e 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -294,7 +294,7 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea // The resets will be zero not only now but as well repeats packets into the future clearResets(repeats+1); { - int ret; + int ret = 0; do { if(isMainTrack) { if (rmtMainChannel != NULL) From 02bf50b909c03acf22a3396ca72ee14c98ee134d Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 2 Apr 2024 00:05:30 +0200 Subject: [PATCH 18/89] version --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index e5f3571..5d6ee02 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202403182018Z" +#define GITHUB_SHA "devel-202404012205Z" diff --git a/version.h b/version.h index f292b43..1f11713 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.41" +#define VERSION "5.2.42" +// 5.2.42 - ESP32 Bugfix: Uninitialized stack variable // 5.2.41 - Update rotary encoder default address to 0x67 // 5.2.40 - Allow no shield // 5.2.39 - Functions for DC frequency: Use func up to F31 From fdc956576b3478291fe33ad0d072af77f3de5865 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 5 Apr 2024 01:02:49 +0200 Subject: [PATCH 19/89] ESP32 rewrite PWM LEDC to use pin mux --- DCCTimer.h | 4 +++- DCCTimerESP.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++---- MotorDriver.cpp | 6 +++--- MotorDriver.h | 3 ++- TrackManager.cpp | 23 +++++++++++++++++++-- 5 files changed, 79 insertions(+), 11 deletions(-) diff --git a/DCCTimer.h b/DCCTimer.h index 5cf1026..984d6eb 100644 --- a/DCCTimer.h +++ b/DCCTimer.h @@ -66,7 +66,9 @@ class DCCTimer { static void ackRailcomTimer(); static void DCCEXanalogWriteFrequency(uint8_t pin, uint32_t frequency); static void DCCEXanalogWrite(uint8_t pin, int value); - + static void DCCEXledcDetachPin(uint8_t pin); + static void DCCEXanalogCopyChannel(uint8_t frompin, uint8_t topin); + static void DCCEXInrushControlOn(uint8_t pin); // Update low ram level. Allow for extra bytes to be specified // by estimation or inspection, that may be used by other // called subroutines. Must be called with interrupts disabled. diff --git a/DCCTimerESP.cpp b/DCCTimerESP.cpp index ae81c74..3e8d49e 100644 --- a/DCCTimerESP.cpp +++ b/DCCTimerESP.cpp @@ -78,6 +78,7 @@ int DCCTimer::freeMemory() { //////////////////////////////////////////////////////////////////////// #ifdef ARDUINO_ARCH_ESP32 +#include "DIAG.h" #include #include #include @@ -154,8 +155,10 @@ void DCCTimer::reset() { void DCCTimer::DCCEXanalogWriteFrequency(uint8_t pin, uint32_t f) { if (f >= 16) DCCTimer::DCCEXanalogWriteFrequencyInternal(pin, f); - else if (f == 7) +/* + else if (f == 7) // not used on ESP32 DCCTimer::DCCEXanalogWriteFrequencyInternal(pin, 62500); +*/ else if (f >= 4) DCCTimer::DCCEXanalogWriteFrequencyInternal(pin, 32000); else if (f >= 3) @@ -188,22 +191,65 @@ void DCCTimer::DCCEXanalogWriteFrequencyInternal(uint8_t pin, uint32_t frequency } } +void DCCTimer::DCCEXledcDetachPin(uint8_t pin) { + DIAG(F("Clear pin %d channel"), pin); + pin_to_channel[pin] = 0; + pinMatrixOutDetach(pin, false, false); +} + + +void DCCTimer::DCCEXanalogCopyChannel(uint8_t frompin, uint8_t topin) { + DIAG(F("Pin %d copied to %d channel %d"), frompin, topin, pin_to_channel[frompin]); + pin_to_channel[topin] = pin_to_channel[frompin]; + ledcAttachPin(topin, pin_to_channel[topin]); +} void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value) { + // This allocates channels 15, 13, 11, .... + // so each channel gets its own timer. if (pin < SOC_GPIO_PIN_COUNT) { if (pin_to_channel[pin] == 0) { + int search_channel; + int n; if (!cnt_channel) { log_e("No more PWM channels available! All %u already used", LEDC_CHANNELS); return; } - pin_to_channel[pin] = --cnt_channel; - ledcSetup(cnt_channel, 1000, 8); - ledcAttachPin(pin, cnt_channel); + // search for free channels top down + for (search_channel=LEDC_CHANNELS-1; search_channel >=cnt_channel; search_channel -= 2) { + bool chanused = false; + for (n=0; n < SOC_GPIO_PIN_COUNT; n++) { + if (pin_to_channel[n] == search_channel) { // current search_channel used + chanused = true; + break; + } + } + if (chanused) + continue; + if (n == SOC_GPIO_PIN_COUNT) // current search_channel unused + break; + } + if (search_channel >= cnt_channel) { + pin_to_channel[pin] = search_channel; + DIAG(F("Pin %d assigned to search channel %d"), pin, search_channel); + } else { + pin_to_channel[pin] = --cnt_channel; // This sets 15, 13, ... + DIAG(F("Pin %d assigned to new channel %d"), pin, cnt_channel); + --cnt_channel; // Now we are at 14, 12, ... + } + ledcSetup(pin_to_channel[pin], 1000, 8); + ledcAttachPin(pin, pin_to_channel[pin]); } else { + //DIAG(F("Pin %d assigned to old channel %d"), pin, pin_to_channel[pin]); ledcAttachPin(pin, pin_to_channel[pin]); } ledcWrite(pin_to_channel[pin], value); } } +void DCCTimer::DCCEXInrushControlOn(uint8_t pin) { + ledcSetup(0, 62500, 8); + ledcAttachPin(pin, 0); + ledcWrite(0, 207); +} int ADCee::init(uint8_t pin) { pinMode(pin, ANALOG); diff --git a/MotorDriver.cpp b/MotorDriver.cpp index c233c22..afd8e6e 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -411,10 +411,10 @@ void MotorDriver::throttleInrush(bool on) { duty = 255-duty; #if defined(ARDUINO_ARCH_ESP32) if(on) { - DCCTimer::DCCEXanalogWrite(brakePin,duty); - DCCTimer::DCCEXanalogWriteFrequency(brakePin, 7); // 7 means max + DCCTimer::DCCEXInrushControlOn(brakePin); } else { - ledcDetachPin(brakePin); + ledcDetachPin(brakePin); // not DCCTimer::DCCEXledcDetachPin() as we have not + // registered the pin in the pin to channel array } #elif defined(ARDUINO_ARCH_STM32) if(on) { diff --git a/MotorDriver.h b/MotorDriver.h index 4491164..945e4ee 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -193,13 +193,14 @@ class MotorDriver { } }; inline pinpair getSignalPin() { return pinpair(signalPin,signalPin2); }; + inline byte getBrakePin() { return brakePin; }; void setDCSignal(byte speedByte, uint8_t frequency=0); void throttleInrush(bool on); inline void detachDCSignal() { #if defined(__arm__) pinMode(brakePin, OUTPUT); #elif defined(ARDUINO_ARCH_ESP32) - ledcDetachPin(brakePin); + DCCTimer::DCCEXledcDetachPin(brakePin); #else setDCSignal(128); #endif diff --git a/TrackManager.cpp b/TrackManager.cpp index 338b11c..21fe44a 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -252,13 +252,32 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr track[trackToSet]->makeProgTrack(false); // only the prog track knows it's type } track[trackToSet]->setMode(mode); - trackDCAddr[trackToSet]=dcAddr; // When a track is switched, we must clear any side effects of its previous // state, otherwise trains run away or just dont move. // This can be done BEFORE the PWM-Timer evaluation (methinks) - if (!(mode & TRACK_MODE_DC)) { + if (mode & TRACK_MODE_DC) { + if (trackDCAddr[trackToSet] != dcAddr) { + // if we change dcAddr, detach first old signal + track[trackToSet]->detachDCSignal(); +#ifdef ARDUINO_ARCH_ESP32 + int trackfound = -1; + FOR_EACH_TRACK(t) { + if ((track[t]->getMode() & TRACK_MODE_DC) && trackDCAddr[t] == dcAddr) { + trackfound = t; + break; + } + } + if (trackfound > -1) { + DCCTimer::DCCEXanalogCopyChannel(track[trackfound]->getBrakePin(), + track[trackToSet]->getBrakePin()); + } +#endif + } + // set future DC Addr; + trackDCAddr[trackToSet]=dcAddr; + } else { // DCC tracks need to have set the PWM to zero or they will not work. track[trackToSet]->detachDCSignal(); track[trackToSet]->setBrake(false); From 6d7d2325da786a53db11612508e31a7e4f0e9f33 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 5 Apr 2024 01:10:10 +0200 Subject: [PATCH 20/89] ESP32 rewrite PWM LEDC inrush duty fix --- DCCTimer.h | 2 +- DCCTimerESP.cpp | 4 ++-- MotorDriver.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DCCTimer.h b/DCCTimer.h index 984d6eb..4ce8590 100644 --- a/DCCTimer.h +++ b/DCCTimer.h @@ -68,7 +68,7 @@ class DCCTimer { static void DCCEXanalogWrite(uint8_t pin, int value); static void DCCEXledcDetachPin(uint8_t pin); static void DCCEXanalogCopyChannel(uint8_t frompin, uint8_t topin); - static void DCCEXInrushControlOn(uint8_t pin); + static void DCCEXInrushControlOn(uint8_t pin, int duty); // Update low ram level. Allow for extra bytes to be specified // by estimation or inspection, that may be used by other // called subroutines. Must be called with interrupts disabled. diff --git a/DCCTimerESP.cpp b/DCCTimerESP.cpp index 3e8d49e..b651a49 100644 --- a/DCCTimerESP.cpp +++ b/DCCTimerESP.cpp @@ -245,10 +245,10 @@ void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value) { ledcWrite(pin_to_channel[pin], value); } } -void DCCTimer::DCCEXInrushControlOn(uint8_t pin) { +void DCCTimer::DCCEXInrushControlOn(uint8_t pin, int duty) { ledcSetup(0, 62500, 8); ledcAttachPin(pin, 0); - ledcWrite(0, 207); + ledcWrite(0, duty); } int ADCee::init(uint8_t pin) { diff --git a/MotorDriver.cpp b/MotorDriver.cpp index afd8e6e..235f557 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -411,7 +411,7 @@ void MotorDriver::throttleInrush(bool on) { duty = 255-duty; #if defined(ARDUINO_ARCH_ESP32) if(on) { - DCCTimer::DCCEXInrushControlOn(brakePin); + DCCTimer::DCCEXInrushControlOn(brakePin, duty); } else { ledcDetachPin(brakePin); // not DCCTimer::DCCEXledcDetachPin() as we have not // registered the pin in the pin to channel array From 84b90ae75775f8ea22cc523bdbc663f1d508071e Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 5 Apr 2024 01:11:12 +0200 Subject: [PATCH 21/89] Booster mode inrush throttle, too --- MotorDriver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 235f557..47c359e 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -404,7 +404,7 @@ void MotorDriver::setDCSignal(byte speedcode, uint8_t frequency /*default =0*/) void MotorDriver::throttleInrush(bool on) { if (brakePin == UNUSED_PIN) return; - if ( !(trackMode & (TRACK_MODE_MAIN | TRACK_MODE_PROG | TRACK_MODE_EXT))) + if ( !(trackMode & (TRACK_MODE_MAIN | TRACK_MODE_PROG | TRACK_MODE_EXT | TRACK_MODE_BOOST))) return; byte duty = on ? 207 : 0; // duty of 81% at 62500Hz this gives pauses of 3usec if (invertBrake) From cff407593713f09fdc6d701c3aba742dba49f786 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 5 Apr 2024 01:12:08 +0200 Subject: [PATCH 22/89] version 5.2.43 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 5d6ee02..821e344 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202404012205Z" +#define GITHUB_SHA "devel-202404042311Z" diff --git a/version.h b/version.h index 1f11713..58d0cb6 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.42" +#define VERSION "5.2.43" +// 5.2.43 - ESP32 rewrite PWM LEDC to use pin mux // 5.2.42 - ESP32 Bugfix: Uninitialized stack variable // 5.2.41 - Update rotary encoder default address to 0x67 // 5.2.40 - Allow no shield From dc5f5e05b9530a8b950c6382042fd19c2dbea76c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 5 Apr 2024 14:05:12 +0200 Subject: [PATCH 23/89] ESP32 fix PWM LEDC inverted pin mode --- DCCTimer.h | 8 ++++--- DCCTimerESP.cpp | 58 +++++++++++++++++++++++++++++++++++++++-------- DCCTimerSTM32.cpp | 4 +++- MotorDriver.cpp | 14 ++++++------ MotorDriver.h | 2 +- TrackManager.cpp | 10 +++++--- 6 files changed, 71 insertions(+), 25 deletions(-) diff --git a/DCCTimer.h b/DCCTimer.h index 4ce8590..44c85f2 100644 --- a/DCCTimer.h +++ b/DCCTimer.h @@ -65,10 +65,12 @@ class DCCTimer { static void startRailcomTimer(byte brakePin); static void ackRailcomTimer(); static void DCCEXanalogWriteFrequency(uint8_t pin, uint32_t frequency); - static void DCCEXanalogWrite(uint8_t pin, int value); + static void DCCEXanalogWrite(uint8_t pin, int value, bool invert); static void DCCEXledcDetachPin(uint8_t pin); - static void DCCEXanalogCopyChannel(uint8_t frompin, uint8_t topin); - static void DCCEXInrushControlOn(uint8_t pin, int duty); + static void DCCEXanalogCopyChannel(int8_t frompin, int8_t topin); + static void DCCEXInrushControlOn(uint8_t pin, int duty, bool invert); + static void DCCEXledcAttachPin(uint8_t pin, int8_t channel, bool inverted); + // Update low ram level. Allow for extra bytes to be specified // by estimation or inspection, that may be used by other // called subroutines. Must be called with interrupts disabled. diff --git a/DCCTimerESP.cpp b/DCCTimerESP.cpp index b651a49..93711aa 100644 --- a/DCCTimerESP.cpp +++ b/DCCTimerESP.cpp @@ -197,13 +197,48 @@ void DCCTimer::DCCEXledcDetachPin(uint8_t pin) { pinMatrixOutDetach(pin, false, false); } +static byte LEDCToMux[] = { + LEDC_HS_SIG_OUT0_IDX, + LEDC_HS_SIG_OUT1_IDX, + LEDC_HS_SIG_OUT2_IDX, + LEDC_HS_SIG_OUT3_IDX, + LEDC_HS_SIG_OUT4_IDX, + LEDC_HS_SIG_OUT5_IDX, + LEDC_HS_SIG_OUT6_IDX, + LEDC_HS_SIG_OUT7_IDX, + LEDC_LS_SIG_OUT0_IDX, + LEDC_LS_SIG_OUT1_IDX, + LEDC_LS_SIG_OUT2_IDX, + LEDC_LS_SIG_OUT3_IDX, + LEDC_LS_SIG_OUT4_IDX, + LEDC_LS_SIG_OUT5_IDX, + LEDC_LS_SIG_OUT6_IDX, + LEDC_LS_SIG_OUT7_IDX, +}; -void DCCTimer::DCCEXanalogCopyChannel(uint8_t frompin, uint8_t topin) { - DIAG(F("Pin %d copied to %d channel %d"), frompin, topin, pin_to_channel[frompin]); - pin_to_channel[topin] = pin_to_channel[frompin]; - ledcAttachPin(topin, pin_to_channel[topin]); +void DCCTimer::DCCEXledcAttachPin(uint8_t pin, int8_t channel, bool inverted) { + DIAG(F("Attaching pin %d to channel %d %c"), pin, channel, inverted ? 'I' : ' '); + ledcAttachPin(pin, channel); + if (inverted) // we attach again but with inversion + gpio_matrix_out(pin, LEDCToMux[channel], inverted, 0); } -void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value) { + +void DCCTimer::DCCEXanalogCopyChannel(int8_t frompin, int8_t topin) { + // arguments are signed depending on inversion of pins + DIAG(F("Pin %d copied to %d"), frompin, topin); + bool inverted = false; + if (frompin<0) + frompin = -frompin; + if (topin<0) { + inverted = true; + topin = -topin; + } + int channel = pin_to_channel[frompin]; // after abs(frompin) + pin_to_channel[topin] = channel; + DCCTimer::DCCEXledcAttachPin(topin, channel, inverted); +} + +void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value, bool invert) { // This allocates channels 15, 13, 11, .... // so each channel gets its own timer. if (pin < SOC_GPIO_PIN_COUNT) { @@ -237,17 +272,20 @@ void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value) { --cnt_channel; // Now we are at 14, 12, ... } ledcSetup(pin_to_channel[pin], 1000, 8); - ledcAttachPin(pin, pin_to_channel[pin]); + DCCEXledcAttachPin(pin, pin_to_channel[pin], invert); } else { - //DIAG(F("Pin %d assigned to old channel %d"), pin, pin_to_channel[pin]); - ledcAttachPin(pin, pin_to_channel[pin]); + // This else is only here so we can enable diag + // Pin should be already attached to channel + // DIAG(F("Pin %d assigned to old channel %d"), pin, pin_to_channel[pin]); } ledcWrite(pin_to_channel[pin], value); } } -void DCCTimer::DCCEXInrushControlOn(uint8_t pin, int duty) { + +void DCCTimer::DCCEXInrushControlOn(uint8_t pin, int duty, bool inverted) { + // this uses hardcoded channel 0 ledcSetup(0, 62500, 8); - ledcAttachPin(pin, 0); + DCCEXledcAttachPin(pin, 0, inverted); ledcWrite(0, duty); } diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index 43c8ece..0c1d5d6 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -333,7 +333,9 @@ void DCCTimer::DCCEXanalogWriteFrequencyInternal(uint8_t pin, uint32_t frequency return; } -void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value) { +void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value, bool invert) { + if (invert) + value = 255-value; // Calculate percentage duty cycle from value given uint32_t duty_cycle = (value * 100 / 256) + 1; if (pin_timer[pin] != NULL) { diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 47c359e..1ab52d8 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -336,8 +336,6 @@ void MotorDriver::setDCSignal(byte speedcode, uint8_t frequency /*default =0*/) if (tSpeed <= 1) brake = 255; else if (tSpeed >= 127) brake = 0; else brake = 2 * (128-tSpeed); - if (invertBrake) - brake=255-brake; { // new block because of variable f #if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_STM32) @@ -351,10 +349,10 @@ void MotorDriver::setDCSignal(byte speedcode, uint8_t frequency /*default =0*/) #endif //DIAG(F("Brake pin %d freqency %d"), brakePin, f); DCCTimer::DCCEXanalogWriteFrequency(brakePin, f); // set DC PWM frequency - DCCTimer::DCCEXanalogWrite(brakePin,brake); + DCCTimer::DCCEXanalogWrite(brakePin, brake, invertBrake); #else // all AVR here DCCTimer::DCCEXanalogWriteFrequency(brakePin, frequency); // frequency steps - analogWrite(brakePin,brake); + analogWrite(brakePin, invertBrake ? 255-brake : brake); #endif } @@ -407,16 +405,16 @@ void MotorDriver::throttleInrush(bool on) { if ( !(trackMode & (TRACK_MODE_MAIN | TRACK_MODE_PROG | TRACK_MODE_EXT | TRACK_MODE_BOOST))) return; byte duty = on ? 207 : 0; // duty of 81% at 62500Hz this gives pauses of 3usec - if (invertBrake) - duty = 255-duty; #if defined(ARDUINO_ARCH_ESP32) if(on) { - DCCTimer::DCCEXInrushControlOn(brakePin, duty); + DCCTimer::DCCEXInrushControlOn(brakePin, duty, invertBrake); } else { ledcDetachPin(brakePin); // not DCCTimer::DCCEXledcDetachPin() as we have not // registered the pin in the pin to channel array } #elif defined(ARDUINO_ARCH_STM32) + if (invertBrake) + duty = 255-duty; if(on) { DCCTimer::DCCEXanalogWriteFrequency(brakePin, 7); // 7 means max DCCTimer::DCCEXanalogWrite(brakePin,duty); @@ -424,6 +422,8 @@ void MotorDriver::throttleInrush(bool on) { pinMode(brakePin, OUTPUT); } #else // all AVR here + if (invertBrake) + duty = 255-duty; if(on){ DCCTimer::DCCEXanalogWriteFrequency(brakePin, 7); // 7 means max } diff --git a/MotorDriver.h b/MotorDriver.h index 945e4ee..a6ed1f6 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -193,7 +193,7 @@ class MotorDriver { } }; inline pinpair getSignalPin() { return pinpair(signalPin,signalPin2); }; - inline byte getBrakePin() { return brakePin; }; + inline int8_t getBrakePinSigned() { return invertBrake ? -brakePin : brakePin; }; void setDCSignal(byte speedByte, uint8_t frequency=0); void throttleInrush(bool on); inline void detachDCSignal() { diff --git a/TrackManager.cpp b/TrackManager.cpp index 21fe44a..06b6a18 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -264,14 +264,18 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr #ifdef ARDUINO_ARCH_ESP32 int trackfound = -1; FOR_EACH_TRACK(t) { - if ((track[t]->getMode() & TRACK_MODE_DC) && trackDCAddr[t] == dcAddr) { + //DIAG(F("Checking track %c mode %x dcAddr %d"), 'A'+t, track[t]->getMode(), trackDCAddr[t]); + if (t != trackToSet // not our track + && (track[t]->getMode() & TRACK_MODE_DC) // right mode + && trackDCAddr[t] == dcAddr) { // right addr + //DIAG(F("Found track %c"), 'A'+t); trackfound = t; break; } } if (trackfound > -1) { - DCCTimer::DCCEXanalogCopyChannel(track[trackfound]->getBrakePin(), - track[trackToSet]->getBrakePin()); + DCCTimer::DCCEXanalogCopyChannel(track[trackfound]->getBrakePinSigned(), + track[trackToSet]->getBrakePinSigned()); } #endif } From d367f5dc8174230057e880f93016581e4ecdef7a Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 5 Apr 2024 14:06:36 +0200 Subject: [PATCH 24/89] version 5.2.44 --- GITHUB_SHA.h | 2 +- version.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 821e344..0bcb861 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202404042311Z" +#define GITHUB_SHA "devel-202404051206Z" diff --git a/version.h b/version.h index 58d0cb6..6f25b47 100644 --- a/version.h +++ b/version.h @@ -3,8 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.43" -// 5.2.43 - ESP32 rewrite PWM LEDC to use pin mux +#define VERSION "5.2.44" +// 5.2.44 - ESP32 fix PWM LEDC inverted pin mode +// ESP32 rewrite PWM LEDC to use pin mux // 5.2.42 - ESP32 Bugfix: Uninitialized stack variable // 5.2.41 - Update rotary encoder default address to 0x67 // 5.2.40 - Allow no shield From 7b77d4ce1e9d679cf703da37ac91221a9f57e54b Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 5 Apr 2024 14:08:39 +0200 Subject: [PATCH 25/89] STM32 fix inverted pin mode --- MotorDriver.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 1ab52d8..8662ca1 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -413,11 +413,9 @@ void MotorDriver::throttleInrush(bool on) { // registered the pin in the pin to channel array } #elif defined(ARDUINO_ARCH_STM32) - if (invertBrake) - duty = 255-duty; if(on) { DCCTimer::DCCEXanalogWriteFrequency(brakePin, 7); // 7 means max - DCCTimer::DCCEXanalogWrite(brakePin,duty); + DCCTimer::DCCEXanalogWrite(brakePin,duty,invertBrake); } else { pinMode(brakePin, OUTPUT); } From f581d56bdce137c532607ee5d2368a24de88b4b9 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 5 Apr 2024 20:30:26 +0200 Subject: [PATCH 26/89] ESP32 set frequency after DC speed --- MotorDriver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 8662ca1..66d1b71 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -347,9 +347,9 @@ void MotorDriver::setDCSignal(byte speedcode, uint8_t frequency /*default =0*/) } } #endif - //DIAG(F("Brake pin %d freqency %d"), brakePin, f); - DCCTimer::DCCEXanalogWriteFrequency(brakePin, f); // set DC PWM frequency + //DIAG(F("Brake pin %d value %d freqency %d"), brakePin, brake, f); DCCTimer::DCCEXanalogWrite(brakePin, brake, invertBrake); + DCCTimer::DCCEXanalogWriteFrequency(brakePin, f); // set DC PWM frequency #else // all AVR here DCCTimer::DCCEXanalogWriteFrequency(brakePin, frequency); // frequency steps analogWrite(brakePin, invertBrake ? 255-brake : brake); From e4a3aa9f1e42afc248e75c88aba428d2c599c065 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 5 Apr 2024 20:31:05 +0200 Subject: [PATCH 27/89] tag --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 0bcb861..5588243 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202404051206Z" +#define GITHUB_SHA "devel-202404051830Z" From 1a307eea3debf251107a78dd5ec7e0847fe850fb Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 6 Apr 2024 13:19:56 +0100 Subject: [PATCH 28/89] Extended consist and --- DCC.cpp | 43 ++++++++++++++++++++++++++++++++++++++++--- DCCACK.cpp | 17 +++++++++++++++++ DCCACK.h | 2 ++ 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index a8bc953..f2ab8c3 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -325,8 +325,8 @@ preamble -0- 1 0 A7 A6 A5 A4 A3 A2 -0- 0 ^A10 ^A9 ^A8 0 A1 A0 1 -0- .... Thus in byte packet form the format is 10AAAAAA, 0AAA0AA1, 000XXXXX -Die Adresse für den ersten erweiterten Zubehördecoder ist wie bei den einfachen -Zubehördecodern die Adresse 4 = 1000-0001 0111-0001 . Diese Adresse wird in +Die Adresse f�r den ersten erweiterten Zubeh�rdecoder ist wie bei den einfachen +Zubeh�rdecodern die Adresse 4 = 1000-0001 0111-0001 . Diese Adresse wird in Anwenderdialogen als Adresse 1 dargestellt. This means that the first address shown to the user as "1" is mapped @@ -500,6 +500,36 @@ const ackOp FLASH READ_CV_PROG[] = { const ackOp FLASH LOCO_ID_PROG[] = { BASELINE, + // first check cv20 for extended addressing + SETCV, (ackOp)20, // CV 19 is extended + SETBYTE, (ackOp)0, + VB, WACK, ITSKIP, // skip past extended section if cv20 is zero + // read cv20 and 19 and merge + STARTMERGE, // Setup to read cv 20 + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + VB, WACK, NAKSKIP, // bad read of cv20, assume its 0 + STASHLOCOID, // keep cv 20 until we have cv19 as well. + SETCV, (ackOp)19, + STARTMERGE, // Setup to read cv 19 + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + VB, WACK, NAKFAIL, // cant recover if cv 19 unreadable + COMBINE1920, // Combile byte with stash and callback +// end of advanced 20,19 check + SKIPTARGET, SETCV, (ackOp)19, // CV 19 is consist setting SETBYTE, (ackOp)0, VB, WACK, ITSKIP, // ignore consist if cv19 is zero (no consist) @@ -566,6 +596,10 @@ const ackOp FLASH LOCO_ID_PROG[] = { const ackOp FLASH SHORT_LOCO_ID_PROG[] = { BASELINE, + // Clear consist CV 19,20 + SETCV,(ackOp)20, + SETBYTE, (ackOp)0, + WB,WACK, // ignore dedcoder without cv20 support SETCV,(ackOp)19, SETBYTE, (ackOp)0, WB,WACK, // ignore dedcoder without cv19 support @@ -583,7 +617,10 @@ const ackOp FLASH SHORT_LOCO_ID_PROG[] = { const ackOp FLASH LONG_LOCO_ID_PROG[] = { BASELINE, - // Clear consist CV 19 + // Clear consist CV 19,20 + SETCV,(ackOp)20, + SETBYTE, (ackOp)0, + WB,WACK, // ignore dedcoder without cv20 support SETCV,(ackOp)19, SETBYTE, (ackOp)0, WB,WACK, // ignore decoder without cv19 support diff --git a/DCCACK.cpp b/DCCACK.cpp index 8a074b4..517d513 100644 --- a/DCCACK.cpp +++ b/DCCACK.cpp @@ -314,6 +314,14 @@ void DCCACK::loop() { callback( LONG_ADDR_MARKER | ( ackManagerByte + ((ackManagerStash - 192) << 8))); return; + case COMBINE1920: + // ackManagerStash is cv20, ackManagerByte is CV 19 + // This will not be called if cv20==0 + ackManagerByte &= 0x7F; // ignore direction marker + ackManagerByte %=100; // take last 2 decimal digits + callback( ackManagerStash*100+ackManagerByte); + return; + case ITSKIP: if (!ackReceived) break; // SKIP opcodes until SKIPTARGET found @@ -322,6 +330,15 @@ void DCCACK::loop() { opcode=GETFLASH(ackManagerProg); } break; + + case NAKSKIP: + if (ackReceived) break; + // SKIP opcodes until SKIPTARGET found + while (opcode!=SKIPTARGET) { + ackManagerProg++; + opcode=GETFLASH(ackManagerProg); + } + break; case SKIPTARGET: break; default: diff --git a/DCCACK.h b/DCCACK.h index 7d39319..fa03387 100644 --- a/DCCACK.h +++ b/DCCACK.h @@ -56,6 +56,8 @@ enum ackOp : byte STASHLOCOID, // keeps current byte value for later COMBINELOCOID, // combines current value with stashed value and returns it ITSKIP, // skip to SKIPTARGET if ack true + NAKSKIP, // skip to SKIPTARGET if ack false + COMBINE1920, // combine cvs 19 and 20 and callback SKIPTARGET = 0xFF // jump to target }; From 38a9585a412a9ae1b492a031b2d785e3eab4e66f Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 6 Apr 2024 19:46:23 +0200 Subject: [PATCH 29/89] ESP32 Trackmanager reset cab number to 0 when track is not DC --- TrackManager.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index 06b6a18..d66b999 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -38,8 +38,8 @@ if (track[t]->getMode()==findmode) \ track[t]->function; -MotorDriver * TrackManager::track[MAX_TRACKS]; -int16_t TrackManager::trackDCAddr[MAX_TRACKS]; +MotorDriver * TrackManager::track[MAX_TRACKS] = { NULL }; +int16_t TrackManager::trackDCAddr[MAX_TRACKS] = { 0 }; int8_t TrackManager::lastTrack=-1; bool TrackManager::progTrackSyncMain=false; @@ -251,7 +251,6 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr } else { track[trackToSet]->makeProgTrack(false); // only the prog track knows it's type } - track[trackToSet]->setMode(mode); // When a track is switched, we must clear any side effects of its previous // state, otherwise trains run away or just dont move. @@ -259,8 +258,13 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr // This can be done BEFORE the PWM-Timer evaluation (methinks) if (mode & TRACK_MODE_DC) { if (trackDCAddr[trackToSet] != dcAddr) { - // if we change dcAddr, detach first old signal - track[trackToSet]->detachDCSignal(); + // new or changed DC Addr, run the new setup + if (trackDCAddr[trackToSet] != 0) { + // if we change dcAddr and not only + // change from another mode, + // first detach old DC signal + track[trackToSet]->detachDCSignal(); + } #ifdef ARDUINO_ARCH_ESP32 int trackfound = -1; FOR_EACH_TRACK(t) { @@ -285,7 +289,9 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr // DCC tracks need to have set the PWM to zero or they will not work. track[trackToSet]->detachDCSignal(); track[trackToSet]->setBrake(false); + trackDCAddr[trackToSet]=0; // clear that an addr is set for DC as this is not a DC track } + track[trackToSet]->setMode(mode); // BOOST: // Leave it as is From 6b713bf57c95565879fb5cbe2552ffa71d9b8d00 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 6 Apr 2024 19:48:02 +0200 Subject: [PATCH 30/89] version 5.2.45 --- GITHUB_SHA.h | 2 +- version.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 5588243..a9ab348 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202404051830Z" +#define GITHUB_SHA "devel-202404061747Z" diff --git a/version.h b/version.h index 6f25b47..2af0a55 100644 --- a/version.h +++ b/version.h @@ -3,8 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.44" -// 5.2.44 - ESP32 fix PWM LEDC inverted pin mode +#define VERSION "5.2.45" +// 5.2.45 - ESP32 Trackmanager reset cab number to 0 when track is not DC +// ESP32 fix PWM LEDC inverted pin mode // ESP32 rewrite PWM LEDC to use pin mux // 5.2.42 - ESP32 Bugfix: Uninitialized stack variable // 5.2.41 - Update rotary encoder default address to 0x67 From f41f61dd5fc1adb8e0445f8fbfb4c3f8011d089a Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 6 Apr 2024 23:41:25 +0100 Subject: [PATCH 31/89] 10239) { //0x27FF according to standard + callback(-1); + return; + } + byte cv20; + byte cv19; + + if (id<=HIGHEST_SHORT_ADDR) { + cv19=id; + cv20=0; + } + else { + cv20=id/100; + cv19=id%100; + } + if (reverse) cv19|=0x80; + DCCACK::Setup((cv20<<8)|cv19, CONSIST_ID_PROG, callback); +} + void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco setThrottle2(cab,1); // ESTOP this loco if still on track int reg=lookupSpeedTable(cab, false); diff --git a/DCC.h b/DCC.h index 4503227..4bc222c 100644 --- a/DCC.h +++ b/DCC.h @@ -85,7 +85,7 @@ public: static void getLocoId(ACK_CALLBACK callback); static void setLocoId(int id,ACK_CALLBACK callback); - + static void setConsistId(int id,bool reverse,ACK_CALLBACK callback); // Enhanced API functions static void forgetLoco(int cab); // removes any speed reminders for this loco static void forgetAllLocos(); // removes all speed reminders diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index fa4c9f8..6e41473 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -458,6 +458,9 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) DCC::setLocoId(p[0],callback_Wloco); else if (params == 4) // WRITE CV ON PROG DCC::writeCVByte(p[0], p[1], callback_W4); + else if ((params==2 | params==3 ) && p[0]=="CONSIST"_hk ) { + DCC::setConsistId(p[1],p[2]=="REVERSE"_hk,callback_Wconsist); + } else if (params == 2) // WRITE CV ON PROG DCC::writeCVByte(p[0], p[1], callback_W); else @@ -1347,3 +1350,11 @@ void DCCEXParser::callback_Wloco(int16_t result) StringFormatter::send(getAsyncReplyStream(), F("\n"), result); commitAsyncReplyStream(); } + +void DCCEXParser::callback_Wconsist(int16_t result) +{ + if (result==1) result=stashP[1]; // pick up original requested id from command + StringFormatter::send(getAsyncReplyStream(), F("\n"), + result, stashP[2]=="REVERSE"_hk ? F(" REVERSE") : F("")); + commitAsyncReplyStream(); +} diff --git a/DCCEXParser.h b/DCCEXParser.h index 3c3382c..d3b7851 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -71,6 +71,7 @@ struct DCCEXParser static void callback_R(int16_t result); static void callback_Rloco(int16_t result); static void callback_Wloco(int16_t result); + static void callback_Wconsist(int16_t result); static void callback_Vbit(int16_t result); static void callback_Vbyte(int16_t result); static FILTER_CALLBACK filterCallback; From 182479c07b4d0bc00ff4bbe2a1077284e3f32216 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 6 Apr 2024 23:49:26 +0100 Subject: [PATCH 32/89] Consist version. --- DCC.cpp | 2 +- DCCEXParser.cpp | 2 +- version.h | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 2ed75f1..0aa623f 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -703,7 +703,7 @@ void DCC::setLocoId(int id,ACK_CALLBACK callback) { } void DCC::setConsistId(int id,bool reverse,ACK_CALLBACK callback) { - if (id<1 || id>10239) { //0x27FF according to standard + if (id<0 || id>10239) { //0x27FF according to standard callback(-1); return; } diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 6e41473..a8180ec 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -458,7 +458,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) DCC::setLocoId(p[0],callback_Wloco); else if (params == 4) // WRITE CV ON PROG DCC::writeCVByte(p[0], p[1], callback_W4); - else if ((params==2 | params==3 ) && p[0]=="CONSIST"_hk ) { + else if ((params==2 || params==3 ) && p[0]=="CONSIST"_hk ) { DCC::setConsistId(p[1],p[2]=="REVERSE"_hk,callback_Wconsist); } else if (params == 2) // WRITE CV ON PROG diff --git a/version.h b/version.h index 2af0a55..a772201 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.45" +#define VERSION "5.2.46" +// 5.2.46 - Support for extended consist CV20 in and +// - New cmd to handle long/short consist ids // 5.2.45 - ESP32 Trackmanager reset cab number to 0 when track is not DC // ESP32 fix PWM LEDC inverted pin mode // ESP32 rewrite PWM LEDC to use pin mux From 263c3d01e3fc841a0623a27c98d3b8615159c04d Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 7 Apr 2024 09:26:32 +0200 Subject: [PATCH 33/89] DISABLE_DIAG by default for Uno and Nano --- config.example.h | 13 +++++++++++++ defines.h | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/config.example.h b/config.example.h index a2e08b2..3fc86c3 100644 --- a/config.example.h +++ b/config.example.h @@ -211,6 +211,19 @@ The configuration file for DCC-EX Command Station // #define DISABLE_VDPY // #define ENABLE_VDPY +///////////////////////////////////////////////////////////////////////////////////// +// DISABLE / ENABLE DIAG +// +// To diagose different errors, you can turn on differnet messages. This costs +// program memory which we do not have enough on the Uno and Nano, so it is +// by default DISABLED on those. If you think you can fit it (for example +// having disabled some of the features above) you can enable it with +// ENABLE_DIAG. You can even disable it on all other CPUs with +// DISABLE_DIAG +// +// #define DISABLE_DIAG +// #define ENABLE_DIAG + ///////////////////////////////////////////////////////////////////////////////////// // REDEFINE WHERE SHORT/LONG ADDR break is. According to NMRA the last short address // is 127 and the first long address is 128. There are manufacturers which have diff --git a/defines.h b/defines.h index 14dd1c5..2c3ee55 100644 --- a/defines.h +++ b/defines.h @@ -220,9 +220,15 @@ // #if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO) #define IO_NO_HAL // HAL too big whatever you disable otherwise + #ifndef ENABLE_VDPY #define DISABLE_VDPY #endif + +#ifndef ENABLE_DIAG +#define DISABLE_DIAG +#endif + #endif #if __has_include ( "myAutomation.h") From 5ea6feb11aac6854a903d7fa8cd0ecc3e1ebb330 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 9 Apr 2024 20:45:28 +0100 Subject: [PATCH 34/89] Squashed commit of the following: commit 8987d622e60fb27174203ce47b49462a01ecb61c Author: Asbelos Date: Tue Apr 9 20:44:47 2024 +0100 doc note commit 8f0a5c1ec0fde18dbb262311a1ef5ef79d571807 Author: Asbelos Date: Thu Apr 4 09:45:58 2024 +0100 Exrail notes commit 94083b9ab8a2322c39b7087c522730569194b732 Merge: 72ef199 02bf50b Author: Asbelos Date: Thu Apr 4 09:08:26 2024 +0100 Merge branch 'devel' into devel_chris commit 72ef199315d1c7717331864abb11730890fd3162 Author: Asbelos Date: Thu Apr 4 09:06:50 2024 +0100 TOGGLE_TURNOUT commit e69b777a2f62104dd8b74ba688b950fa92919d54 Author: Asbelos Date: Wed Apr 3 15:17:40 2024 +0100 BLINK command commit c7ed47400d5d89c0b8425ec12b1828e710fb23ec Author: Asbelos Date: Tue Apr 2 10:12:45 2024 +0100 FTOGGLE,XFTOGGLE commit 7a93cf7be856afd30f6976a483b1db4bfc4073a1 Author: Asbelos Date: Fri Mar 29 13:21:35 2024 +0000 EXRAIL STEALTH_GLOBAL --- DCCEXParser.cpp | 1 + EXRAIL2.cpp | 79 ++++++++++++++++++++++--- EXRAIL2.h | 18 +++++- EXRAIL2MacroReset.h | 11 ++++ EXRAIL2Parser.cpp | 21 +++++-- EXRAILMacros.h | 13 ++++ Release_Notes/EXRAIL additions.md | 38 ++++++++++++ Release_Notes/Exrail mods.txt | 98 +++++++++++++++++++++++++++++++ 8 files changed, 262 insertions(+), 17 deletions(-) create mode 100644 Release_Notes/EXRAIL additions.md create mode 100644 Release_Notes/Exrail mods.txt diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index a8180ec..b9ac3a9 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -800,6 +800,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) break; #endif + case '/': // implemented in EXRAIL parser case 'L': // LCC interface implemented in EXRAIL parser break; // Will if not intercepted by EXRAIL diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index c9c6716..ef4387c 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -373,7 +373,7 @@ RMFT2::RMFT2(int progCtr) { speedo=0; forward=true; invert=false; - timeoutFlag=false; + blinkState=not_blink_task; stackDepth=0; onEventStartPosition=-1; // Not handling an ONxxx @@ -491,6 +491,23 @@ void RMFT2::loop() { void RMFT2::loop2() { if (delayTime!=0 && millis()-delayStart < delayTime) return; + // special stand alone blink task + if (compileFeatures & FEATURE_BLINK) { + if (blinkState==blink_low) { + IODevice::write(blinkPin,HIGH); + blinkState=blink_high; + delayMe(getOperand(1)); + return; + } + if (blinkState==blink_high) { + IODevice::write(blinkPin,LOW); + blinkState=blink_low; + delayMe(getOperand(2)); + return; + } + } + + // Normal progstep following tasks continue here. byte opcode = GET_OPCODE; int16_t operand = getOperand(0); @@ -511,6 +528,10 @@ void RMFT2::loop2() { Turnout::setClosed(operand, true); break; + case OPCODE_TOGGLE_TURNOUT: + Turnout::setClosed(operand, Turnout::isThrown(operand)); + break; + #ifndef IO_NO_HAL case OPCODE_ROTATE: uint8_t activity; @@ -560,39 +581,39 @@ void RMFT2::loop2() { break; case OPCODE_AT: - timeoutFlag=false; + blinkState=not_blink_task; if (readSensor(operand)) break; delayMe(50); return; case OPCODE_ATGTE: // wait for analog sensor>= value - timeoutFlag=false; + blinkState=not_blink_task; if (IODevice::readAnalogue(operand) >= (int)(getOperand(1))) break; delayMe(50); return; case OPCODE_ATLT: // wait for analog sensor < value - timeoutFlag=false; + blinkState=not_blink_task; if (IODevice::readAnalogue(operand) < (int)(getOperand(1))) break; delayMe(50); return; case OPCODE_ATTIMEOUT1: // ATTIMEOUT(vpin,timeout) part 1 timeoutStart=millis(); - timeoutFlag=false; + blinkState=not_blink_task; break; case OPCODE_ATTIMEOUT2: if (readSensor(operand)) break; // success without timeout if (millis()-timeoutStart > 100*getOperand(1)) { - timeoutFlag=true; + blinkState=at_timeout; break; // and drop through } delayMe(50); return; case OPCODE_IFTIMEOUT: // do next operand if timeout flag set - skipIf=!timeoutFlag; + skipIf=blinkState!=at_timeout; break; case OPCODE_AFTER: // waits for sensor to hit and then remain off for 0.5 seconds. (must come after an AT operation) @@ -624,13 +645,25 @@ void RMFT2::loop2() { break; case OPCODE_SET: + killBlinkOnVpin(operand); IODevice::write(operand,true); break; case OPCODE_RESET: + killBlinkOnVpin(operand); IODevice::write(operand,false); break; - + + case OPCODE_BLINK: + // Start a new task to blink this vpin + killBlinkOnVpin(operand); + { + auto newtask=new RMFT2(progCounter); + newtask->blinkPin=operand; + newtask->blinkState=blink_low; // will go high on first call + } + break; + case OPCODE_PAUSE: DCC::setThrottle(0,1,true); // pause all locos on the track pausingTask=this; @@ -815,6 +848,10 @@ void RMFT2::loop2() { case OPCODE_FOFF: if (loco) DCC::setFn(loco,operand,false); break; + + case OPCODE_FTOGGLE: + if (loco) DCC::changeFn(loco,operand); + break; case OPCODE_DRIVE: { @@ -830,6 +867,10 @@ void RMFT2::loop2() { case OPCODE_XFOFF: DCC::setFn(operand,getOperand(1),false); break; + + case OPCODE_XFTOGGLE: + DCC::changeFn(operand,getOperand(1)); + break; case OPCODE_DCCACTIVATE: { // operand is address<<3 | subaddr<<1 | active @@ -1167,16 +1208,19 @@ int16_t RMFT2::getSignalSlot(int16_t id) { if (redpin) { bool redval=(rag==SIGNAL_RED || rag==SIMAMBER); if (!aHigh) redval=!redval; + killBlinkOnVpin(redpin); IODevice::write(redpin,redval); } if (amberpin) { bool amberval=(rag==SIGNAL_AMBER); if (!aHigh) amberval=!amberval; + killBlinkOnVpin(amberpin); IODevice::write(amberpin,amberval); } if (greenpin) { bool greenval=(rag==SIGNAL_GREEN || rag==SIMAMBER); if (!aHigh) greenval=!greenval; + killBlinkOnVpin(greenpin); IODevice::write(greenpin,greenval); } } @@ -1264,6 +1308,25 @@ void RMFT2::powerEvent(int16_t track, bool overload) { } } +// This function is used when setting pins so that a SET or RESET +// will cause any blink task on that pin to terminate. +// It will be compiled out of existence if no BLINK feature is used. +void RMFT2::killBlinkOnVpin(VPIN pin) { + if (!(compileFeatures & FEATURE_BLINK)) return; + + RMFT2 * task=loopTask; + while(task) { + if ( + (task->blinkState==blink_high || task->blinkState==blink_low) + && task->blinkPin==pin) { + task->kill(); + return; + } + task=task->next; + if (task==loopTask) return; + } +} + void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc) { // Check we dont already have a task running this handler RMFT2 * task=loopTask; diff --git a/EXRAIL2.h b/EXRAIL2.h index f4cf320..7075f26 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -33,7 +33,7 @@ // or more OPCODE_PAD instructions with the subsequent parameters. This wastes a byte but makes // searching easier as a parameter can never be confused with an opcode. // -enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, +enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,OPCODE_TOGGLE_TURNOUT, OPCODE_FWD,OPCODE_REV,OPCODE_SPEED,OPCODE_INVERT_DIRECTION, OPCODE_RESERVE,OPCODE_FREE, OPCODE_AT,OPCODE_AFTER, @@ -41,9 +41,11 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ATGTE,OPCODE_ATLT, OPCODE_ATTIMEOUT1,OPCODE_ATTIMEOUT2, OPCODE_LATCH,OPCODE_UNLATCH,OPCODE_SET,OPCODE_RESET, + OPCODE_BLINK, OPCODE_ENDIF,OPCODE_ELSE, OPCODE_DELAY,OPCODE_DELAYMINS,OPCODE_DELAYMS,OPCODE_RANDWAIT, OPCODE_FON,OPCODE_FOFF,OPCODE_XFON,OPCODE_XFOFF, + OPCODE_FTOGGLE,OPCODE_XFTOGGLE, OPCODE_RED,OPCODE_GREEN,OPCODE_AMBER,OPCODE_DRIVE, OPCODE_SERVO,OPCODE_SIGNAL,OPCODE_TURNOUT,OPCODE_WAITFOR, OPCODE_PAD,OPCODE_FOLLOW,OPCODE_CALL,OPCODE_RETURN, @@ -98,12 +100,21 @@ enum thrunger: byte { thrunge_lcd, // Must be last!! }; + +enum BlinkState: byte { + not_blink_task, + blink_low, // blink task running with pin LOW + blink_high, // blink task running with pin high + at_timeout // ATTIMEOUT timed out flag + }; + // Flag bits for compile time features. static const byte FEATURE_SIGNAL= 0x80; static const byte FEATURE_LCC = 0x40; static const byte FEATURE_ROSTER= 0x20; static const byte FEATURE_ROUTESTATE= 0x10; static const byte FEATURE_STASH = 0x08; + static const byte FEATURE_BLINK = 0x04; // Flag bits for status of hardware and TPL @@ -192,6 +203,7 @@ private: static LookList* LookListLoader(OPCODE op1, OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL); static uint16_t getOperand(int progCounter,byte n); + static void killBlinkOnVpin(VPIN pin); static RMFT2 * loopTask; static RMFT2 * pausingTask; void delayMe(long millisecs); @@ -244,10 +256,10 @@ private: union { unsigned long waitAfter; // Used by OPCODE_AFTER unsigned long timeoutStart; // Used by OPCODE_ATTIMEOUT + VPIN blinkPin; // Used by blink tasks }; - bool timeoutFlag; byte taskId; - + BlinkState blinkState; // includes AT_TIMEOUT flag. uint16_t loco; bool forward; bool invert; diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index e94c657..ce242ea 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -38,6 +38,7 @@ #undef ATTIMEOUT #undef AUTOMATION #undef AUTOSTART +#undef BLINK #undef BROADCAST #undef CALL #undef CLEAR_STASH @@ -66,6 +67,7 @@ #undef FOLLOW #undef FON #undef FORGET +#undef FTOGGLE #undef FREE #undef FWD #undef GREEN @@ -164,8 +166,10 @@ #undef START #undef STASH #undef STEALTH +#undef STEALTH_GLOBAL #undef STOP #undef THROW +#undef TOGGLE_TURNOUT #undef TT_ADDPOSITION #undef TURNOUT #undef TURNOUTL @@ -180,6 +184,7 @@ #undef WITHROTTLE #undef XFOFF #undef XFON +#undef XFTOGGLE #ifndef RMFT2_UNDEF_ONLY #define ACTIVATE(addr,subaddr) @@ -196,6 +201,7 @@ #define ATTIMEOUT(sensor_id,timeout_ms) #define AUTOMATION(id,description) #define AUTOSTART +#define BLINK(vpin,onDuty,offDuty) #define BROADCAST(msg) #define CALL(route) #define CLEAR_STASH(id) @@ -225,6 +231,7 @@ #define FON(func) #define FORGET #define FREE(blockid) +#define FTOGGLE(func) #define FWD(speed) #define GREEN(signal_id) #define HAL(haltype,params...) @@ -322,8 +329,10 @@ #define START(route) #define STASH(id) #define STEALTH(code...) +#define STEALTH_GLOBAL(code...) #define STOP #define THROW(id) +#define TOGGLE_TURNOUT(id) #define TT_ADDPOSITION(turntable_id,position,value,angle,description...) #define TURNOUT(id,addr,subaddr,description...) #define TURNOUTL(id,addr,description...) @@ -338,4 +347,6 @@ #define WITHROTTLE(msg) #define XFOFF(cab,func) #define XFON(cab,func) +#define XFTOGGLE(cab,func) + #endif diff --git a/EXRAIL2Parser.cpp b/EXRAIL2Parser.cpp index 7969750..95375bb 100644 --- a/EXRAIL2Parser.cpp +++ b/EXRAIL2Parser.cpp @@ -36,7 +36,7 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]) { (void)stream; // avoid compiler warning if we don't access this parameter - bool reject=false; + switch(opcode) { case 'D': @@ -47,8 +47,7 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16 break; case '/': // New EXRAIL command - reject=!parseSlash(stream,paramCount,p); - opcode=0; + if (parseSlash(stream,paramCount,p)) opcode=0; break; case 'A': // @@ -106,9 +105,11 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16 } if (paramCount==1) { // LCC event arrived from adapter int16_t eventid=p[0]; - reject=eventid<0 || eventid>=countLCCLookup; - if (!reject) startNonRecursiveTask(F("LCC"),eventid,onLCCLookup[eventid]); - opcode=0; + bool reject = eventid<0 || eventid>=countLCCLookup; + if (!reject) { + startNonRecursiveTask(F("LCC"),eventid,onLCCLookup[eventid]); + opcode=0; + } } break; @@ -182,12 +183,20 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { StringFormatter::send(stream, F("<* EXRAIL STATUS")); RMFT2 * task=loopTask; while(task) { + if ((compileFeatures & FEATURE_BLINK) + && (task->blinkState==blink_high || task->blinkState==blink_low)) { + StringFormatter::send(stream,F("\nID=%d,PC=%d,BLINK=%d"), + (int)(task->taskId),task->progCounter,task->blinkPin + ); + } + else { StringFormatter::send(stream,F("\nID=%d,PC=%d,LOCO=%d%c,SPEED=%d%c"), (int)(task->taskId),task->progCounter,task->loco, task->invert?'I':' ', task->speedo, task->forward?'F':'R' ); + } task=task->next; if (task==loopTask) break; } diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 5588811..7db52dc 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -145,6 +145,12 @@ static_assert(!hasdup(compileTimeSequenceList[0],1),"Duplicate SEQUENCE/ROUTE/AU #include "myAutomation.h" +// Pass 1g Implants STEALTH_GLOBAL in correct place +#include "EXRAIL2MacroReset.h" +#undef STEALTH_GLOBAL +#define STEALTH_GLOBAL(code...) code +#include "myAutomation.h" + // Pass 1h Implements HAL macro by creating exrailHalSetup function // Also allows creating EXTurntable object #include "EXRAIL2MacroReset.h" @@ -202,6 +208,8 @@ bool exrailHalSetup() { #define PICKUP_STASH(id) | FEATURE_STASH #undef STASH #define STASH(id) | FEATURE_STASH +#undef BLINK +#define BLINK(vpin,onDuty,offDuty) | FEATURE_BLINK const byte RMFT2::compileFeatures = 0 #include "myAutomation.h" @@ -451,6 +459,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define ATTIMEOUT(sensor_id,timeout) OPCODE_ATTIMEOUT1,0,0,OPCODE_ATTIMEOUT2,V(sensor_id),OPCODE_PAD,V(timeout/100L), #define AUTOMATION(id, description) OPCODE_AUTOMATION, V(id), #define AUTOSTART OPCODE_AUTOSTART,0,0, +#define BLINK(vpin,onDuty,offDuty) OPCODE_BLINK,V(vpin),OPCODE_PAD,V(onDuty),OPCODE_PAD,V(offDuty), #define BROADCAST(msg) PRINT(msg) #define CALL(route) OPCODE_CALL,V(route), #define CLEAR_STASH(id) OPCODE_CLEAR_STASH,V(id), @@ -484,6 +493,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define FON(func) OPCODE_FON,V(func), #define FORGET OPCODE_FORGET,0,0, #define FREE(blockid) OPCODE_FREE,V(blockid), +#define FTOGGLE(func) OPCODE_FTOGGLE,V(func), #define FWD(speed) OPCODE_FWD,V(speed), #define GREEN(signal_id) OPCODE_GREEN,V(signal_id), #define HAL(haltype,params...) @@ -518,6 +528,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define LCD(id,msg) PRINT(msg) #define SCREEN(display,id,msg) PRINT(msg) #define STEALTH(code...) PRINT(dummy) +#define STEALTH_GLOBAL(code...) #define LCN(msg) PRINT(msg) #define MESSAGE(msg) PRINT(msg) #define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0), @@ -595,6 +606,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define STASH(id) OPCODE_STASH,V(id), #define STOP OPCODE_SPEED,V(0), #define THROW(id) OPCODE_THROW,V(id), +#define TOGGLE_TURNOUT(id) OPCODE_TOGGLE_TURNOUT,V(id), #ifndef IO_NO_HAL #define TT_ADDPOSITION(id,position,value,angle,description...) OPCODE_TTADDPOSITION,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(value),OPCODE_PAD,V(angle), #endif @@ -611,6 +623,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #endif #define XFOFF(cab,func) OPCODE_XFOFF,V(cab),OPCODE_PAD,V(func), #define XFON(cab,func) OPCODE_XFON,V(cab),OPCODE_PAD,V(func), +#define XFTOGGLE(cab,func) OPCODE_XFTOGGLE,V(cab),OPCODE_PAD,V(func), // Build RouteCode const int StringMacroTracker2=__COUNTER__; diff --git a/Release_Notes/EXRAIL additions.md b/Release_Notes/EXRAIL additions.md new file mode 100644 index 0000000..690ff0a --- /dev/null +++ b/Release_Notes/EXRAIL additions.md @@ -0,0 +1,38 @@ + +BLINK(vpin, onMs,offMs) + +which will start a vpin blinking until such time as it is SET, RESET or set by a signal operation such as RED, AMBER, GREEN. + +BLINK returns immediately, the blinking is autonomous. + +This means a signal that always blinks amber could be done like this: +``` +SIGNAL(30,31,32) +ONAMBER(30) BLINK(31,500,500) DONE +``` +The RED or GREEN calls will turn off the amber blink automatically. + +Alternatively a signal that has normal AMBER and flashing AMBER could be like this: + +#define FLASHAMBER(signal) \ + AMBER(signal) \ + BLINK(signal+1,500,500) + + (Caution: this issumes that the amber pin is redpin+1) + + == + + FTOGGLE(function) + Toggles the current loco function (see FON and FOFF) + + XFTOGGLE(loco,function) + Toggles the function on given loco. (See XFON, XFOFF) + + TOGGLE_TURNOUT(id) + Toggles the turnout (see CLOSE, THROW) + + STEALTH_GLOBAL(code) + ADVANCED C++ users only. + Inserts code such as static variables and functions that + may be utilised by multiple STEALTH operations. + \ No newline at end of file diff --git a/Release_Notes/Exrail mods.txt b/Release_Notes/Exrail mods.txt new file mode 100644 index 0000000..68f57b7 --- /dev/null +++ b/Release_Notes/Exrail mods.txt @@ -0,0 +1,98 @@ + +BLINK(vpin, onMs,offMs) + +which will start a vpin blinking until such time as it is SET, RESET or set by a signal operation such as RED, AMBER, GREEN. + +BLINK returns immediately, the blinking is autonomous. + +This means a signal that always blinks amber could be done like this: + +SIGNAL(30,31,32) +ONAMBER(30) BLINK(31,500,500) DONE + +The RED or GREEN calls will turn off the amber blink automatically. + +Alternatively a signal that has normal AMBER and flashing AMBER could be like this: + +#define FLASHAMBER(signal) \ + AMBER(signal) \ + BLINK(signal+1,500,500) + + (Caution: this assumes that the amber pin is redpin+1) + + == + + FTOGGLE(function) + Toggles the current loco function (see FON and FOFF) + + XFTOGGLE(loco,function) + Toggles the function on given loco. (See XFON, XFOFF) + + TOGGLE_TURNOUT(id) + Toggles the turnout (see CLOSE, THROW) + + STEALTH_GLOBAL(code) + ADVANCED C++ users only. + Inserts code such as static variables and functions that + may be utilised by multiple STEALTH operations. + + +// 5.2.34 - Command fopr DCC Extended Accessories. +This command sends an extended accessory packet to the track, Normally used to set +a signal aspect. Aspect numbers are undefined as sdtandards except for 0 which is +always considered a stop. + +// - Exrail ASPECT(address,aspect) for above. + The ASPECT command sents an aspect to a DCC accessory using the same logic as + . + +// - EXRAIL DCCX_SIGNAL(Address,redAspect,amberAspect,greenAspect) + This defines a signal (with id same as dcc address) that can be operated + by the RED/AMBER/GREEN commands. In each case the command uses the signal + address to refer to the signal and the aspect chosen depends on the use of the RED + AMBER or GREEN command sent. Other aspects may be sent but will require the + direct use of the ASPECT command. + The IFRED/IFAMBER/IFGREEN and ONRED/ONAMBER/ONGREEN commands contunue to operate + as for any other signal type. It is important to be aware that use of the ASPECT + or commands will correctly set the IF flags and call the ON handlers if ASPECT + is used to set one of the three aspects defined in the DCCX_SIGNAL command. + Direct use of other aspects does not affect the signal flags. + ASPECT and can be used withput defining any signal if tyhe flag management or + ON event handlers are not required. + +// 5.2.33 - Exrail CONFIGURE_SERVO(vpin,pos1,pos2,profile) + This macro offsers a more convenient way of performing the HAL call in halSetup.h + In halSetup.h --- IODevice::configureServo(101,300,400,PCA9685::slow); + In myAutomation.h --- CONFIGURE_SERVO(101,300,400,slow) + +// 5.2.32 - Railcom Cutout (Initial trial Mega2560 only) + This cutout will only work on a Mega2560 with a single EX8874 motor shield + configured in the normal way with the main track brake pin on pin 9. + Turns on the cutout mechanism. + Tirns off the cutout. (This is the default) + ONLY to be used by developers used for waveform diagnostics. + (In DEBUG mode the main track idle packets are replaced with reset packets, This + makes it far easier to see the preambles and cutouts on a logic analyser or scope.) + +// 5.2.31 - Exrail JMRI_SENSOR(vpin [,count]) creates types. + This Macro causes the creation of JMRI type sensors in a way that is + simpler than repeating lines of commands. + JMRI_SENSOR(100) is equenvelant to + JMRI_SENSOR(100,16) will create type sensors for vpins 100-115. + +// 5.2.26 - Silently ignore overridden HAL defaults +// - include HAL_IGNORE_DEFAULTS macro in EXRAIL + The HAL_IGNORE_DEFAULTS command, anywhere in myAutomation.h will + prevent the startup code from trying the default I2C sensors/servos. +// 5.2.24 - Exrail macro asserts to catch +// : duplicate/missing automation/route/sequence/call ids +// : latches and reserves out of range +// : speeds out of range + Causes compiler time messages for EXRAIL issues that would normally + only be discovered by things going wrong at run time. +// 5.2.13 - EXRAIL STEALTH + Permits a certain level of C++ code to be embedded as a single step in + an exrail sequence. Serious engineers only. + +// 5.2.9 - EXRAIL STASH feature +// - Added ROUTE_DISABLED macro in EXRAIL From 8a5a832b1d5e2301073b1170b977346de15ecc14 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 9 Apr 2024 20:59:57 +0100 Subject: [PATCH 35/89] Reduced EXRAIL diag noise --- EXRAIL2.cpp | 16 ++++++------- Release_Notes/EXRAIL additions.md | 38 ------------------------------- version.h | 8 ++++++- 3 files changed, 15 insertions(+), 47 deletions(-) delete mode 100644 Release_Notes/EXRAIL additions.md diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index ef4387c..9293c96 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -176,7 +176,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { /* static */ void RMFT2::begin() { - DIAG(F("EXRAIL RoutCode at =%P"),RouteCode); + //DIAG(F("EXRAIL RoutCode at =%P"),RouteCode); bool saved_diag=diag; diag=true; @@ -411,7 +411,7 @@ void RMFT2::createNewTask(int route, uint16_t cab) { void RMFT2::driveLoco(byte speed) { if (loco<=0) return; // Prevent broadcast! - if (diag) DIAG(F("EXRAIL drive %d %d %d"),loco,speed,forward^invert); + //if (diag) DIAG(F("EXRAIL drive %d %d %d"),loco,speed,forward^invert); /* TODO..... power on appropriate track if DC or main if dcc if (TrackManager::getMainPowerMode()==POWERMODE::OFF) { @@ -1066,7 +1066,7 @@ void RMFT2::loop2() { case OPCODE_ROUTE: case OPCODE_AUTOMATION: case OPCODE_SEQUENCE: - if (diag) DIAG(F("EXRAIL begin(%d)"),operand); + //if (diag) DIAG(F("EXRAIL begin(%d)"),operand); break; case OPCODE_AUTOSTART: // Handled only during begin process @@ -1146,7 +1146,7 @@ int16_t RMFT2::getSignalSlot(int16_t id) { /* static */ void RMFT2::doSignal(int16_t id,char rag) { if (!(compileFeatures & FEATURE_SIGNAL)) return; // dont compile code below - if (diag) DIAG(F(" doSignal %d %x"),id,rag); + //if (diag) DIAG(F(" doSignal %d %x"),id,rag); // Schedule any event handler for this signal change. // This will work even without a signal definition. @@ -1166,7 +1166,7 @@ int16_t RMFT2::getSignalSlot(int16_t id) { VPIN redpin=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+2); VPIN amberpin=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+4); VPIN greenpin=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+6); - if (diag) DIAG(F("signal %d %d %d %d %d"),sigid,id,redpin,amberpin,greenpin); + //if (diag) DIAG(F("signal %d %d %d %d %d"),sigid,id,redpin,amberpin,greenpin); VPIN sigtype=sigid & ~SIGNAL_ID_MASK; @@ -1174,7 +1174,7 @@ int16_t RMFT2::getSignalSlot(int16_t id) { // 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 (diag) DIAG(F("sigA %d %d"),id,servopos); if (servopos!=0) IODevice::writeAnalogue(id,servopos,PCA9685::Bounce); return; } @@ -1292,7 +1292,7 @@ void RMFT2::rotateEvent(int16_t turntableId, bool change) { void RMFT2::clockEvent(int16_t clocktime, bool change) { // Hunt for an ONTIME for this time if (Diag::CMD) - DIAG(F("Looking for clock event at : %d"), clocktime); + DIAG(F("clockEvent at : %d"), clocktime); if (change) { onClockLookup->handleEvent(F("CLOCK"),clocktime); onClockLookup->handleEvent(F("CLOCK"),25*60+clocktime%60); @@ -1302,7 +1302,7 @@ 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); + DIAG(F("powerEvent : %c"), track); if (overload) { onOverloadLookup->handleEvent(F("POWER"),track); } diff --git a/Release_Notes/EXRAIL additions.md b/Release_Notes/EXRAIL additions.md deleted file mode 100644 index 690ff0a..0000000 --- a/Release_Notes/EXRAIL additions.md +++ /dev/null @@ -1,38 +0,0 @@ - -BLINK(vpin, onMs,offMs) - -which will start a vpin blinking until such time as it is SET, RESET or set by a signal operation such as RED, AMBER, GREEN. - -BLINK returns immediately, the blinking is autonomous. - -This means a signal that always blinks amber could be done like this: -``` -SIGNAL(30,31,32) -ONAMBER(30) BLINK(31,500,500) DONE -``` -The RED or GREEN calls will turn off the amber blink automatically. - -Alternatively a signal that has normal AMBER and flashing AMBER could be like this: - -#define FLASHAMBER(signal) \ - AMBER(signal) \ - BLINK(signal+1,500,500) - - (Caution: this issumes that the amber pin is redpin+1) - - == - - FTOGGLE(function) - Toggles the current loco function (see FON and FOFF) - - XFTOGGLE(loco,function) - Toggles the function on given loco. (See XFON, XFOFF) - - TOGGLE_TURNOUT(id) - Toggles the turnout (see CLOSE, THROW) - - STEALTH_GLOBAL(code) - ADVANCED C++ users only. - Inserts code such as static variables and functions that - may be utilised by multiple STEALTH operations. - \ No newline at end of file diff --git a/version.h b/version.h index a772201..3eaf199 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,13 @@ #include "StringFormatter.h" -#define VERSION "5.2.46" +#define VERSION "5.2.47" +// 5.2.47 - EXRAIL additions: +// STEALTH_GLOBAL +// BLINK +// TOGGLE_TURNOUT +// FTOGGLE, XFTOGGLE +// Reduced code-developmenmt DIAG noise // 5.2.46 - Support for extended consist CV20 in and // - New cmd to handle long/short consist ids // 5.2.45 - ESP32 Trackmanager reset cab number to 0 when track is not DC From 91e60b371667763135207e2ac5ea707f7558f8d7 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Fri, 12 Apr 2024 17:25:00 +0800 Subject: [PATCH 36/89] HALDisplay bug fix --- IO_HALDisplay.h | 11 +++++++---- version.h | 3 ++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/IO_HALDisplay.h b/IO_HALDisplay.h index f2ca3af..24ffde7 100644 --- a/IO_HALDisplay.h +++ b/IO_HALDisplay.h @@ -1,7 +1,9 @@ /* - * © 2023, Neil McKechnie. All rights reserved. + * © 2024, Paul Antoine + * © 2023, Neil McKechnie + * All rights reserved. * - * This file is part of DCC++EX API + * This file is part of DCC-EX API * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -112,13 +114,14 @@ protected: // Fill buffer with spaces memset(_buffer, ' ', _numCols*_numRows); - _displayDriver->clearNative(); - // Add device to list of HAL devices (not necessary but allows // status to be displayed using and device to be // reinitialised using ). IODevice::addDevice(this); + // Moved after addDevice() to ensure I2CManager.begin() has been called fisrt + _displayDriver->clearNative(); + // Also add this display to list of display handlers DisplayInterface::addDisplay(displayNo); diff --git a/version.h b/version.h index 3eaf199..e871738 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.47" +#define VERSION "5.2.48" +// 5.2.48 - Bugfix: HALDisplay was generating I2C traffic prior to I2C being initialised // 5.2.47 - EXRAIL additions: // STEALTH_GLOBAL // BLINK From 4aa97e1731e108651d372c947772485d3974cf10 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 13 Apr 2024 08:12:35 +0100 Subject: [PATCH 37/89] Squashed commit of the following: commit 3fc90c916c54ceae4364f79a4b9a2001bc75fcd5 Merge: 132e2d0 91e60b3 Author: Asbelos Date: Fri Apr 12 15:08:49 2024 +0100 Merge branch 'devel' into devel_chris commit 132e2d0de2c6e72b1a3d85520936fb7cddd8d739 Author: Asbelos Date: Fri Apr 12 15:07:31 2024 +0100 Revert "Merge branch 'master' into devel_chris" This reverts commit 23845f2df2035333c43b4aa05d76e9f7600efe29, reversing changes made to 76755993f146a1deaf46993d22e850b899dcf603. commit 23845f2df2035333c43b4aa05d76e9f7600efe29 Merge: 7675599 28d60d4 Author: Asbelos Date: Fri Apr 12 14:38:22 2024 +0100 Merge branch 'master' into devel_chris commit 76755993f146a1deaf46993d22e850b899dcf603 Author: Asbelos Date: Fri Apr 12 14:37:34 2024 +0100 ONSENSOR/ONBUTTON commit 8987d622e60fb27174203ce47b49462a01ecb61c Author: Asbelos Date: Tue Apr 9 20:44:47 2024 +0100 doc note commit 8f0a5c1ec0fde18dbb262311a1ef5ef79d571807 Author: Asbelos Date: Thu Apr 4 09:45:58 2024 +0100 Exrail notes commit 94083b9ab8a2322c39b7087c522730569194b732 Merge: 72ef199 02bf50b Author: Asbelos Date: Thu Apr 4 09:08:26 2024 +0100 Merge branch 'devel' into devel_chris commit 72ef199315d1c7717331864abb11730890fd3162 Author: Asbelos Date: Thu Apr 4 09:06:50 2024 +0100 TOGGLE_TURNOUT commit e69b777a2f62104dd8b74ba688b950fa92919d54 Author: Asbelos Date: Wed Apr 3 15:17:40 2024 +0100 BLINK command commit c7ed47400d5d89c0b8425ec12b1828e710fb23ec Author: Asbelos Date: Tue Apr 2 10:12:45 2024 +0100 FTOGGLE,XFTOGGLE commit 7a93cf7be856afd30f6976a483b1db4bfc4073a1 Author: Asbelos Date: Fri Mar 29 13:21:35 2024 +0000 EXRAIL STEALTH_GLOBAL commit 28d60d49849c8fc4b0ff0f933222c052ba7c90aa Author: Peter Akers Date: Fri Feb 16 18:02:40 2024 +1000 Update README.md commit 3b162996ad42546486b812e22d3ed6daee857d19 Author: peteGSX Date: Sun Jan 21 07:13:53 2024 +1000 EX-IO fixes in version commit fb414a7a506f078d2a075d65c7b171ae4399ef63 Author: Harald Barth Date: Thu Jan 18 08:20:33 2024 +0100 Bugfix: allocate enough bytes for digital pins. Add more sanity checks when allocating memory commit 818e05b4253a1a0980abb3a0bbef38a8c662bb1a Author: Harald Barth Date: Wed Jan 10 08:37:54 2024 +0100 version 5.0.8 commit c5168f030fa64330a1f0e09d6637a3817fe5e067 Author: Harald Barth Date: Wed Jan 10 08:15:30 2024 +0100 Do not crash on turnouts without description commit 387ea019bdc483667bcbcf45205a56330d615aee Author: Harald Barth Date: Mon Nov 6 22:11:56 2023 +0100 version 5.0.7 commit a981f83bb9c376d01245c328c5de7d7bf25ebfb2 Author: Harald Barth Date: Mon Nov 6 22:11:31 2023 +0100 Only flag 2.2.0.0-dev as broken, not 2.2.0.0 commit 749a859db551113567faae3248c575dbf6440ece Author: Asbelos Date: Wed Nov 1 20:13:05 2023 +0000 Bugfix TURNOUTL commit 659c58b30766a7b8dd2b4d2677d90663af8fefcf Author: Harald Barth Date: Sat Oct 28 19:20:33 2023 +0200 version 5.0.5 commit 0b9ec7460ba461d5602b6e06843d6be8468f385f Author: Harald Barth Date: Sat Oct 28 19:18:59 2023 +0200 Bugfix version detection logic and better message --- EXRAIL2.cpp | 10 +++ EXRAIL2.h | 2 +- EXRAIL2MacroReset.h | 4 ++ EXRAILMacros.h | 2 + EXRAILSensor.cpp | 104 ++++++++++++++++++++++++++++++ EXRAILSensor.h | 50 ++++++++++++++ Release_Notes/EXRAIL additions.md | 38 +++++++++++ 7 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 EXRAILSensor.cpp create mode 100644 EXRAILSensor.h create mode 100644 Release_Notes/EXRAIL additions.md diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 9293c96..adea99e 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -54,6 +54,7 @@ #include "TrackManager.h" #include "Turntables.h" #include "IODevice.h" +#include "EXRAILSensor.h" // One instance of RMFT clas is used for each "thread" in the automation. @@ -251,6 +252,12 @@ if (compileFeatures & FEATURE_SIGNAL) { break; } + case OPCODE_ONSENSOR: + new EXRAILSensor(operand,progCounter+3,true ); + break; + case OPCODE_ONBUTTON: + new EXRAILSensor(operand,progCounter+3,false ); + break; case OPCODE_TURNOUT: { VPIN id=operand; int addr=getOperand(progCounter,1); @@ -480,6 +487,7 @@ bool RMFT2::skipIfBlock() { } void RMFT2::loop() { + EXRAILSensor::checkAll(); // Round Robin call to a RMFT task each time if (loopTask==NULL) return; @@ -1084,6 +1092,8 @@ void RMFT2::loop2() { case OPCODE_ONGREEN: case OPCODE_ONCHANGE: case OPCODE_ONTIME: + case OPCODE_ONBUTTON: + case OPCODE_ONSENSOR: #ifndef IO_NO_HAL case OPCODE_DCCTURNTABLE: // Turntable definition ignored at runtime case OPCODE_EXTTTURNTABLE: // Turntable definition ignored at runtime diff --git a/EXRAIL2.h b/EXRAIL2.h index 7075f26..1042d53 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -73,7 +73,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,OPCODE_TOGGLE_TURNOUT, OPCODE_ROUTE_ACTIVE,OPCODE_ROUTE_INACTIVE,OPCODE_ROUTE_HIDDEN, OPCODE_ROUTE_DISABLED, OPCODE_STASH,OPCODE_CLEAR_STASH,OPCODE_CLEAR_ALL_STASH,OPCODE_PICKUP_STASH, - + OPCODE_ONBUTTON,OPCODE_ONSENSOR, // OPcodes below this point are skip-nesting IF operations // placed here so that they may be skipped as a group // see skipIfBlock() diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index ce242ea..c799ddf 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -114,6 +114,8 @@ #undef ONGREEN #undef ONRED #undef ONROTATE +#undef ONBUTTON +#undef ONSENSOR #undef ONTHROW #undef ONCHANGE #undef PARSE @@ -279,6 +281,8 @@ #define ONROTATE(turntable_id) #define ONTHROW(turnout_id) #define ONCHANGE(sensor_id) +#define ONSENSOR(sensor_id) +#define ONBUTTON(sensor_id) #define PAUSE #define PIN_TURNOUT(id,pin,description...) #define PRINT(msg) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 7db52dc..508540a 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -553,6 +553,8 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #endif #define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id), #define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id), +#define ONSENSOR(sensor_id) OPCODE_ONSENSOR,V(sensor_id), +#define ONBUTTON(sensor_id) OPCODE_ONBUTTON,V(sensor_id), #define PAUSE OPCODE_PAUSE,0,0, #define PICKUP_STASH(id) OPCODE_PICKUP_STASH,V(id), #define PIN_TURNOUT(id,pin,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(pin), diff --git a/EXRAILSensor.cpp b/EXRAILSensor.cpp new file mode 100644 index 0000000..b0a5fa0 --- /dev/null +++ b/EXRAILSensor.cpp @@ -0,0 +1,104 @@ +/* + * © 2024 Chris Harlow + * All rights reserved. + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +/********************************************************************** +EXRAILSensor represents a sensor that should be monitored in order +to call an exrail ONBUTTON or ONCHANGE handler. +These are created at EXRAIL startup and thus need no delete or listing +capability. +The basic logic is similar to that found in the Sensor class +except that on the relevant change an EXRAIL thread is started. +**********************************************************************/ + +#include "EXRAILSensor.h" +#include "EXRAIL2.h" + +void EXRAILSensor::checkAll() { + if (firstSensor == NULL) return; // No sensors to be scanned + if (readingSensor == NULL) { + // Not currently scanning sensor list + unsigned long thisTime = micros(); + if (thisTime - lastReadCycle < cycleInterval) return; + // Required time has elapsed since last read cycle started, + // so initiate new scan through the sensor list + readingSensor = firstSensor; + lastReadCycle = thisTime; + } + + // Loop until either end of list is encountered or we pause for some reason + byte sensorCount = 0; + + while (readingSensor != NULL) { + bool pause=readingSensor->check(); + // Move to next sensor in list. + readingSensor = readingSensor->nextSensor; + // Currently process max of 16 sensors per entry. + // Performance measurements taken during development indicate that, with 128 sensors configured + // on 8x 16-pin MCP23017 GPIO expanders with polling (no change notification), all inputs can be read from the devices + // within 1.4ms (400Mhz I2C bus speed), and a full cycle of checking 128 sensors for changes takes under a millisecond. + if (pause || (++sensorCount)>=16) return; + } +} + +bool EXRAILSensor::check() { + // check for debounced change in this sensor + inputState = IODevice::read(pin); + + // Check if changed since last time, and process changes. + if (inputState == active) {// no change + latchDelay = minReadCount; // Reset counter + return false; // no change + } + + // Change detected ... has it stayed changed for long enough + if (latchDelay > 0) { + latchDelay--; + return false; + } + + // change validated, act on it. + active = inputState; + latchDelay = minReadCount; // Reset debounce counter + if (onChange || active) { + new RMFT2(progCounter); + return true; // Don't check any more sensors on this entry + } + return false; +} + +EXRAILSensor::EXRAILSensor(VPIN _pin, int _progCounter, bool _onChange) { + // Add to the start of the list + //DIAG(F("ONthing vpin=%d at %d"), _pin, _progCounter); + nextSensor = firstSensor; + firstSensor = this; + + pin=_pin; + progCounter=_progCounter; + onChange=_onChange; + + IODevice::configureInput(pin, true); + active = IODevice::read(pin); + inputState = active; + latchDelay = minReadCount; +} + +EXRAILSensor *EXRAILSensor::firstSensor=NULL; +EXRAILSensor *EXRAILSensor::readingSensor=NULL; +unsigned long EXRAILSensor::lastReadCycle=0; diff --git a/EXRAILSensor.h b/EXRAILSensor.h new file mode 100644 index 0000000..b5b00c6 --- /dev/null +++ b/EXRAILSensor.h @@ -0,0 +1,50 @@ +/* + * © 2024 Chris Harlow + * All rights reserved. + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +#ifndef EXRAILSensor_h +#define EXRAILSensor_h +#include "IODevice.h" +class EXRAILSensor { + static EXRAILSensor * firstSensor; + static EXRAILSensor * readingSensor; + static unsigned long lastReadCycle; + + public: + static void checkAll(); + + EXRAILSensor(VPIN _pin, int _progCounter, bool _onChange); + bool check(); + + private: + static const unsigned int cycleInterval = 10000; // min time between consecutive reads of each sensor in microsecs. + // should not be less than device scan cycle time. + static const byte minReadCount = 4; // number of additional scans before acting on change + // E.g. 1 means that a change is ignored for one scan and actioned on the next. + // Max value is 63 + + EXRAILSensor* nextSensor; + VPIN pin; + int progCounter; + bool active; + bool inputState; + bool onChange; + byte latchDelay; +}; +#endif \ No newline at end of file diff --git a/Release_Notes/EXRAIL additions.md b/Release_Notes/EXRAIL additions.md new file mode 100644 index 0000000..690ff0a --- /dev/null +++ b/Release_Notes/EXRAIL additions.md @@ -0,0 +1,38 @@ + +BLINK(vpin, onMs,offMs) + +which will start a vpin blinking until such time as it is SET, RESET or set by a signal operation such as RED, AMBER, GREEN. + +BLINK returns immediately, the blinking is autonomous. + +This means a signal that always blinks amber could be done like this: +``` +SIGNAL(30,31,32) +ONAMBER(30) BLINK(31,500,500) DONE +``` +The RED or GREEN calls will turn off the amber blink automatically. + +Alternatively a signal that has normal AMBER and flashing AMBER could be like this: + +#define FLASHAMBER(signal) \ + AMBER(signal) \ + BLINK(signal+1,500,500) + + (Caution: this issumes that the amber pin is redpin+1) + + == + + FTOGGLE(function) + Toggles the current loco function (see FON and FOFF) + + XFTOGGLE(loco,function) + Toggles the function on given loco. (See XFON, XFOFF) + + TOGGLE_TURNOUT(id) + Toggles the turnout (see CLOSE, THROW) + + STEALTH_GLOBAL(code) + ADVANCED C++ users only. + Inserts code such as static variables and functions that + may be utilised by multiple STEALTH operations. + \ No newline at end of file From 7dafe0383d21491bbefb1a9ce3562396aefb421c Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 13 Apr 2024 09:14:55 +0100 Subject: [PATCH 38/89] EXRAIL ONBUTTON --- Release_Notes/EXRAIL additions.md | 38 ------------------------------- Release_Notes/Exrail mods.txt | 21 +++++++++++++++++ version.h | 4 +++- 3 files changed, 24 insertions(+), 39 deletions(-) delete mode 100644 Release_Notes/EXRAIL additions.md diff --git a/Release_Notes/EXRAIL additions.md b/Release_Notes/EXRAIL additions.md deleted file mode 100644 index 690ff0a..0000000 --- a/Release_Notes/EXRAIL additions.md +++ /dev/null @@ -1,38 +0,0 @@ - -BLINK(vpin, onMs,offMs) - -which will start a vpin blinking until such time as it is SET, RESET or set by a signal operation such as RED, AMBER, GREEN. - -BLINK returns immediately, the blinking is autonomous. - -This means a signal that always blinks amber could be done like this: -``` -SIGNAL(30,31,32) -ONAMBER(30) BLINK(31,500,500) DONE -``` -The RED or GREEN calls will turn off the amber blink automatically. - -Alternatively a signal that has normal AMBER and flashing AMBER could be like this: - -#define FLASHAMBER(signal) \ - AMBER(signal) \ - BLINK(signal+1,500,500) - - (Caution: this issumes that the amber pin is redpin+1) - - == - - FTOGGLE(function) - Toggles the current loco function (see FON and FOFF) - - XFTOGGLE(loco,function) - Toggles the function on given loco. (See XFON, XFOFF) - - TOGGLE_TURNOUT(id) - Toggles the turnout (see CLOSE, THROW) - - STEALTH_GLOBAL(code) - ADVANCED C++ users only. - Inserts code such as static variables and functions that - may be utilised by multiple STEALTH operations. - \ No newline at end of file diff --git a/Release_Notes/Exrail mods.txt b/Release_Notes/Exrail mods.txt index 68f57b7..6f8287f 100644 --- a/Release_Notes/Exrail mods.txt +++ b/Release_Notes/Exrail mods.txt @@ -1,3 +1,24 @@ +// 5.2.49 + +Which is a more efficient than the AT/AFTER/IF methods +of handling buttons and switches, especially on MIMIC panels. + +ONBUTTON(vpin) + handles debounce and starts a task if a button is used to + short a pin to ground. + + for example: + ONBUTTON(30) TOGGLE_TURNOUT(30) DONE + +ONSENSOR(vpin) + handles debounce and starts a task if the pin changes. + You may want to check the pin state with an IF ... + +Note the ONBUTTON and ONSENSOR are not generally useful +for track sensors and running trains, because you dont know which +train triggered the sensor. + +// 5.2.47 BLINK(vpin, onMs,offMs) diff --git a/version.h b/version.h index e871738..395630a 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.48" +#define VERSION "5.2.49" +// 5.2.49 - EXRAIL additions: +// ONBUTTON, ONSENSOR // 5.2.48 - Bugfix: HALDisplay was generating I2C traffic prior to I2C being initialised // 5.2.47 - EXRAIL additions: // STEALTH_GLOBAL From ebe8f62cf05d560a33553086a26f20af923acce1 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 13 Apr 2024 10:16:26 +0100 Subject: [PATCH 39/89] ONBUTTON/ONSENSOR use latch --- EXRAIL2.cpp | 9 ++++++--- EXRAIL2.h | 5 +++-- EXRAILMacros.h | 4 ++++ EXRAILSensor.cpp | 2 +- version.h | 3 ++- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index adea99e..6b1b05e 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -253,11 +253,13 @@ if (compileFeatures & FEATURE_SIGNAL) { } case OPCODE_ONSENSOR: + if (compileFeatures & FEATURE_SENSOR) new EXRAILSensor(operand,progCounter+3,true ); - break; + break; case OPCODE_ONBUTTON: + if (compileFeatures & FEATURE_SENSOR) new EXRAILSensor(operand,progCounter+3,false ); - break; + break; case OPCODE_TURNOUT: { VPIN id=operand; int addr=getOperand(progCounter,1); @@ -487,7 +489,8 @@ bool RMFT2::skipIfBlock() { } void RMFT2::loop() { - EXRAILSensor::checkAll(); + if (compileFeatures & FEATURE_SENSOR) + EXRAILSensor::checkAll(); // Round Robin call to a RMFT task each time if (loopTask==NULL) return; diff --git a/EXRAIL2.h b/EXRAIL2.h index 1042d53..794eb86 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -115,6 +115,7 @@ enum BlinkState: byte { static const byte FEATURE_ROUTESTATE= 0x10; static const byte FEATURE_STASH = 0x08; static const byte FEATURE_BLINK = 0x04; + static const byte FEATURE_SENSOR = 0x02; // Flag bits for status of hardware and TPL @@ -185,7 +186,8 @@ class LookList { static const FSH * getTurntableDescription(int16_t id); static const FSH * getTurntablePositionDescription(int16_t turntableId, uint8_t positionId); static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc); - + static bool readSensor(uint16_t sensorId); + private: static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); static bool parseSlash(Print * stream, byte & paramCount, int16_t p[]) ; @@ -208,7 +210,6 @@ private: static RMFT2 * pausingTask; void delayMe(long millisecs); void driveLoco(byte speedo); - bool readSensor(uint16_t sensorId); bool skipIfBlock(); bool readLoco(); void loop2(); diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 508540a..e9e2f77 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -210,6 +210,10 @@ bool exrailHalSetup() { #define STASH(id) | FEATURE_STASH #undef BLINK #define BLINK(vpin,onDuty,offDuty) | FEATURE_BLINK +#undef ONBUTTON +#define ONBUTTON(vpin) | FEATURE_SENSOR +#undef ONSENSOR +#define ONSENSOR(vpin) | FEATURE_SENSOR const byte RMFT2::compileFeatures = 0 #include "myAutomation.h" diff --git a/EXRAILSensor.cpp b/EXRAILSensor.cpp index b0a5fa0..218b970 100644 --- a/EXRAILSensor.cpp +++ b/EXRAILSensor.cpp @@ -59,7 +59,7 @@ void EXRAILSensor::checkAll() { bool EXRAILSensor::check() { // check for debounced change in this sensor - inputState = IODevice::read(pin); + inputState = RMFT2::readSensor(pin); // Check if changed since last time, and process changes. if (inputState == active) {// no change diff --git a/version.h b/version.h index 395630a..0b8c1a6 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.49" +#define VERSION "5.2.50" +// 5.2.50 - EXRAIL ONBUTTON/ONSENSOR observe LATCH // 5.2.49 - EXRAIL additions: // ONBUTTON, ONSENSOR // 5.2.48 - Bugfix: HALDisplay was generating I2C traffic prior to I2C being initialised From c382bd33bc65dc8f8185abbf746bc1fe02f50b9b Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 21 Apr 2024 19:03:24 +0200 Subject: [PATCH 40/89] Distinguish between sighandle and sigid --- EXRAIL2.cpp | 38 +++++++++++++++++++++----------------- EXRAIL2Parser.cpp | 11 ++++++----- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 6b1b05e..b811145 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -205,15 +205,16 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { // Second pass startup, define any turnouts or servos, set signals red // add sequences onRoutines to the lookups -if (compileFeatures & FEATURE_SIGNAL) { - onRedLookup=LookListLoader(OPCODE_ONRED); - onAmberLookup=LookListLoader(OPCODE_ONAMBER); - onGreenLookup=LookListLoader(OPCODE_ONGREEN); - for (int sigslot=0;;sigslot++) { - VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); - if (sigid==0) break; // end of signal list - doSignal(sigid & SIGNAL_ID_MASK, SIGNAL_RED); - } + if (compileFeatures & FEATURE_SIGNAL) { + onRedLookup=LookListLoader(OPCODE_ONRED); + onAmberLookup=LookListLoader(OPCODE_ONAMBER); + onGreenLookup=LookListLoader(OPCODE_ONGREEN); + for (int sigslot=0;;sigslot++) { + int16_t sighandle=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); + if (sighandle==0) break; // end of signal list + VPIN sigid = sighandle & SIGNAL_ID_MASK; + doSignal(sigid, SIGNAL_RED); + } } int progCounter; @@ -1143,16 +1144,17 @@ void RMFT2::kill(const FSH * reason, int operand) { int16_t RMFT2::getSignalSlot(int16_t id) { for (int sigslot=0;;sigslot++) { - int16_t sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); - if (sigid==0) { // end of signal list + int16_t sighandle=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); + if (sighandle==0) { // end of signal list DIAG(F("EXRAIL Signal %d not defined"), id); return -1; } + VPIN sigid = sighandle & SIGNAL_ID_MASK; // 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 & SIGNAL_ID_MASK)!= id) continue; // keep looking + if (sigid != id) continue; // keep looking return sigslot; // relative slot in signals table } } @@ -1175,13 +1177,14 @@ int16_t RMFT2::getSignalSlot(int16_t id) { // Correct signal definition found, get the rag values int16_t sigpos=sigslot*8; - VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos); + int16_t sighandle=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos); VPIN redpin=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+2); VPIN amberpin=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+4); VPIN greenpin=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+6); //if (diag) DIAG(F("signal %d %d %d %d %d"),sigid,id,redpin,amberpin,greenpin); - VPIN sigtype=sigid & ~SIGNAL_ID_MASK; + VPIN sigtype=sighandle & ~SIGNAL_ID_MASK; + VPIN sigid = sighandle & SIGNAL_ID_MASK; if (sigtype == SERVO_SIGNAL_FLAG) { // A servo signal, the pin numbers are actually servo positions @@ -1204,7 +1207,7 @@ int16_t RMFT2::getSignalSlot(int16_t id) { byte value=redpin; if (rag==SIGNAL_AMBER) value=amberpin; if (rag==SIGNAL_GREEN) value=greenpin; - DCC::setExtendedAccessory(sigid & SIGNAL_ID_MASK,value); + DCC::setExtendedAccessory(sigid, value); return; } @@ -1255,8 +1258,9 @@ bool RMFT2::signalAspectEvent(int16_t address, byte aspect ) { int16_t sigslot=getSignalSlot(address); if (sigslot<0) return false; // this is not a defined signal int16_t sigpos=sigslot*8; - VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos); - VPIN sigtype=sigid & ~SIGNAL_ID_MASK; + int16_t sighandle=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos); + VPIN sigtype=sighandle & ~SIGNAL_ID_MASK; + VPIN sigid = sighandle & SIGNAL_ID_MASK; if (sigtype!=DCCX_SIGNAL_FLAG) return false; // not a DCCX signal // Turn an aspect change into a RED/AMBER/GREEN setting if (aspect==GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+2)) { diff --git a/EXRAIL2Parser.cpp b/EXRAIL2Parser.cpp index 95375bb..4023633 100644 --- a/EXRAIL2Parser.cpp +++ b/EXRAIL2Parser.cpp @@ -214,12 +214,13 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { // do the signals // flags[n] represents the state of the nth signal in the table for (int sigslot=0;;sigslot++) { - VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); - if (sigid==0) break; // end of signal list - byte flag=flags[sigslot] & SIGNAL_MASK; // obtain signal flags for this id + int16_t sighandle=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); + if (sighandle==0) break; // end of signal list + VPIN sigid = sighandle & SIGNAL_ID_MASK; + byte flag=flags[sigslot] & SIGNAL_MASK; // obtain signal flags for this id StringFormatter::send(stream,F("\n%S[%d]"), - (flag == SIGNAL_RED)? F("RED") : (flag==SIGNAL_GREEN) ? F("GREEN") : F("AMBER"), - sigid & SIGNAL_ID_MASK); + (flag == SIGNAL_RED)? F("RED") : (flag==SIGNAL_GREEN) ? F("GREEN") : F("AMBER"), + sigid); } } From 3af2f6779221e9ab8008efe94b88acce22b811d9 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 21 Apr 2024 19:41:30 +0200 Subject: [PATCH 41/89] version 5.2.51 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index a9ab348..ab13219 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202404061747Z" +#define GITHUB_SHA "devel-202404211704Z" diff --git a/version.h b/version.h index 0b8c1a6..19b046c 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.50" +#define VERSION "5.2.51" +// 5.2.51 - Bugfix for SIGNAL: Distinguish between sighandle and sigid // 5.2.50 - EXRAIL ONBUTTON/ONSENSOR observe LATCH // 5.2.49 - EXRAIL additions: // ONBUTTON, ONSENSOR From b4e7982099ee2a56f80a077a293425ef1f297112 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 22 Apr 2024 08:12:08 +0200 Subject: [PATCH 42/89] remove forgotten #define DIAG_IO --- Turnouts.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Turnouts.cpp b/Turnouts.cpp index 83603fc..ca5f890 100644 --- a/Turnouts.cpp +++ b/Turnouts.cpp @@ -123,7 +123,6 @@ return true; } -#define DIAG_IO // Static setClosed function is invoked from close(), throw() etc. to perform the // common parts of the turnout operation. Code which is specific to a turnout // type should be placed in the virtual function setClosedInternal(bool) which is From 83d4930124e9286577e380d71e9731f83d38d0e4 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Fri, 26 Apr 2024 19:04:20 +0800 Subject: [PATCH 43/89] Fix F446ZE ADCee support and add STM32 ports G and H --- DCCTimer.h | 4 +++- DCCTimerSTM32.cpp | 22 ++++++++++++++++++---- MotorDriver.cpp | 36 +++++++++++++++++++++++++++++++++++- MotorDriver.h | 19 ++++++++++++++++++- 4 files changed, 74 insertions(+), 7 deletions(-) diff --git a/DCCTimer.h b/DCCTimer.h index 44c85f2..c3fcaf1 100644 --- a/DCCTimer.h +++ b/DCCTimer.h @@ -1,5 +1,5 @@ /* - * © 2022-2023 Paul M. Antoine + * © 2022-2024 Paul M. Antoine * © 2021 Mike S * © 2021-2023 Harald Barth * © 2021 Fred Decker @@ -135,6 +135,8 @@ private: #if defined (ARDUINO_ARCH_STM32) // bit array of used pins (max 32) static uint32_t usedpins; + static uint32_t * analogchans; // Array of channel numbers to be scanned + static ADC_TypeDef * * adcchans; // Array to capture which ADC is each input channel on #else // bit array of used pins (max 16) static uint16_t usedpins; diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index 0c1d5d6..7a13919 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -1,6 +1,6 @@ /* * © 2023 Neil McKechnie - * © 2022-2023 Paul M. Antoine + * © 2022-2024 Paul M. Antoine * © 2021 Mike S * © 2021, 2023 Harald Barth * © 2021 Fred Decker @@ -34,6 +34,7 @@ #include "TrackManager.h" #endif #include "DIAG.h" +#include #if defined(ARDUINO_NUCLEO_F401RE) || defined(ARDUINO_NUCLEO_F411RE) // Nucleo-64 boards don't have additional serial ports defined by default @@ -363,9 +364,9 @@ void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value, bool invert) { uint32_t ADCee::usedpins = 0; // Max of 32 ADC input channels! uint8_t ADCee::highestPin = 0; // Highest pin to scan int * ADCee::analogvals = NULL; // Array of analog values last captured -uint32_t * analogchans = NULL; // Array of channel numbers to be scanned +uint32_t * ADCee::analogchans = NULL; // Array of channel numbers to be scanned // bool adc1configured = false; -ADC_TypeDef * * adcchans = NULL; // Array to capture which ADC is each input channel on +ADC_TypeDef * * ADCee::adcchans = NULL; // Array to capture which ADC is each input channel on int16_t ADCee::ADCmax() { @@ -383,9 +384,10 @@ int ADCee::init(uint8_t pin) { uint32_t adcchan = STM_PIN_CHANNEL(pinmap_function(stmpin, PinMap_ADC)); // find ADC input channel ADC_TypeDef *adc = (ADC_TypeDef *)pinmap_find_peripheral(stmpin, PinMap_ADC); // find which ADC this pin is on ADC1/2/3 etc. int adcnum = 1; + // All variants have ADC1 if (adc == ADC1) DIAG(F("ADCee::init(): found pin %d on ADC1"), pin); -// Checking for ADC2 and ADC3 being defined helps cater for more variants later + // Checking for ADC2 and ADC3 being defined helps cater for more variants #if defined(ADC2) else if (adc == ADC2) { @@ -432,6 +434,18 @@ int ADCee::init(uint8_t pin) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN; //Power up PORTF gpioBase = GPIOF; break; +#endif +#if defined(GPIOG) + case 0x06: + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN; //Power up PORTG + gpioBase = GPIOG; + break; +#endif +#if defined(GPIOH) + case 0x07: + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN; //Power up PORTH + gpioBase = GPIOH; + break; #endif default: return -1023; // some silly value as error diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 66d1b71..da9d3ee 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -1,5 +1,5 @@ /* - * © 2022-2023 Paul M Antoine + * © 2022-2024 Paul M Antoine * © 2021 Mike S * © 2021 Fred Decker * © 2020-2023 Harald Barth @@ -38,6 +38,8 @@ volatile portreg_t shadowPORTC; volatile portreg_t shadowPORTD; volatile portreg_t shadowPORTE; volatile portreg_t shadowPORTF; +volatile portreg_t shadowPORTG; +volatile portreg_t shadowPORTH; #endif MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, int16_t brake_pin, @@ -88,6 +90,16 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i fastSignalPin.shadowinout = fastSignalPin.inout; fastSignalPin.inout = &shadowPORTF; } + if (HAVE_PORTG(fastSignalPin.inout == &PORTG)) { + DIAG(F("Found PORTG pin %d"),signalPin); + fastSignalPin.shadowinout = fastSignalPin.inout; + fastSignalPin.inout = &shadowPORTG; + } + if (HAVE_PORTH(fastSignalPin.inout == &PORTH)) { + DIAG(F("Found PORTH pin %d"),signalPin); + fastSignalPin.shadowinout = fastSignalPin.inout; + fastSignalPin.inout = &shadowPORTF; + } signalPin2=signal_pin2; if (signalPin2!=UNUSED_PIN) { @@ -126,6 +138,16 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i fastSignalPin2.shadowinout = fastSignalPin2.inout; fastSignalPin2.inout = &shadowPORTF; } + if (HAVE_PORTG(fastSignalPin2.inout == &PORTG)) { + DIAG(F("Found PORTG pin %d"),signalPin2); + fastSignalPin2.shadowinout = fastSignalPin2.inout; + fastSignalPin2.inout = &shadowPORTG; + } + if (HAVE_PORTH(fastSignalPin2.inout == &PORTH)) { + DIAG(F("Found PORTH pin %d"),signalPin2); + fastSignalPin2.shadowinout = fastSignalPin2.inout; + fastSignalPin2.inout = &shadowPORTH; + } } else dualSignal=false; @@ -393,6 +415,18 @@ void MotorDriver::setDCSignal(byte speedcode, uint8_t frequency /*default =0*/) setSignal(tDir); HAVE_PORTF(PORTF=shadowPORTF); interrupts(); + } else if (HAVE_PORTG(fastSignalPin.shadowinout == &PORTG)) { + noInterrupts(); + HAVE_PORTG(shadowPORTG=PORTG); + setSignal(tDir); + HAVE_PORTG(PORTG=shadowPORTG); + interrupts(); + } else if (HAVE_PORTH(fastSignalPin.shadowinout == &PORTH)) { + noInterrupts(); + HAVE_PORTH(shadowPORTH=PORTH); + setSignal(tDir); + HAVE_PORTH(PORTH=shadowPORTH); + interrupts(); } else { noInterrupts(); setSignal(tDir); diff --git a/MotorDriver.h b/MotorDriver.h index a6ed1f6..3438c05 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -1,5 +1,5 @@ /* - * © 2022-2023 Paul M. Antoine + * © 2022-2024 Paul M. Antoine * © 2021 Mike S * © 2021 Fred Decker * © 2020 Chris Harlow @@ -26,6 +26,7 @@ #include "FSH.h" #include "IODevice.h" #include "DCCTimer.h" +#include // use powers of two so we can do logical and/or on the track modes in if clauses. // RACK_MODE_DCX is (TRACK_MODE_DC|TRACK_MODE_INV) @@ -83,6 +84,14 @@ enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PRO #define PORTF GPIOF->ODR #define HAVE_PORTF(X) X #endif +#if defined(GPIOG) +#define PORTG GPIOG->ODR +#define HAVE_PORTG(X) X +#endif +#if defined(GPIOH) +#define PORTH GPIOH->ODR +#define HAVE_PORTH(X) X +#endif #endif // if macros not defined as pass-through we define @@ -106,6 +115,12 @@ enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PRO #ifndef HAVE_PORTF #define HAVE_PORTF(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0 #endif +#ifndef HAVE_PORTG +#define HAVE_PORTG(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0 +#endif +#ifndef HAVE_PORTH +#define HAVE_PORTH(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0 +#endif // Virtualised Motor shield 1-track hardware Interface @@ -145,6 +160,8 @@ extern volatile portreg_t shadowPORTC; extern volatile portreg_t shadowPORTD; extern volatile portreg_t shadowPORTE; extern volatile portreg_t shadowPORTF; +extern volatile portreg_t shadowPORTG; +extern volatile portreg_t shadowPORTH; enum class POWERMODE : byte { OFF, ON, OVERLOAD, ALERT }; From 742b100f65e3f7182e3e52917e84d4ef64e0cb22 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 2 May 2024 09:48:18 +0100 Subject: [PATCH 44/89] Comments only --- DCCEXParser.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index b9ac3a9..431093f 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -70,8 +70,8 @@ Once a new OPCODE is decided upon, update this list. L, Reserved for LCC interface (implemented in EXRAIL) m, message to throttles broadcast M, Write DCC packet - n, - N, + n, Reserved for SensorCam + N, Reserved for Sensorcam o, O, Output broadcast p, Broadcast power state @@ -91,10 +91,10 @@ Once a new OPCODE is decided upon, update this list. w, Write CV on main W, Write CV x, - X, Invalid command - y, + X, Invalid command response + y, Y, Output broadcast - z, + z, Direct output Z, Output configuration/control */ From 76ad3ee48d769a653981fb2e8b46fa6b7f91887f Mon Sep 17 00:00:00 2001 From: pmantoine Date: Tue, 7 May 2024 11:10:39 +0800 Subject: [PATCH 45/89] STM32 bug fixes, port usage --- version.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 19b046c..0677dec 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.51" +#define VERSION "5.2.52" +// 5.2.52 - Bugfix for ADCee() to handle ADC2 and ADC3 channel inputs on F446ZE and others +// - Add support for ports G and H on STM32 for ADCee() and MotorDriver pins/shadow regs // 5.2.51 - Bugfix for SIGNAL: Distinguish between sighandle and sigid // 5.2.50 - EXRAIL ONBUTTON/ONSENSOR observe LATCH // 5.2.49 - EXRAIL additions: From 16214fad66fbfa217356a510192d93c12b042c54 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Tue, 7 May 2024 11:13:19 +0800 Subject: [PATCH 46/89] EX-Fastclock bugfix for address check --- IO_EXFastclock.h | 1 + version.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/IO_EXFastclock.h b/IO_EXFastclock.h index 5ed237e..11aaea7 100644 --- a/IO_EXFastclock.h +++ b/IO_EXFastclock.h @@ -51,6 +51,7 @@ static void create(I2CAddress i2cAddress) { // Start by assuming we will find the clock // Check if specified I2C address is responding (blocking operation) // Returns I2C_STATUS_OK (0) if OK, or error code. + I2CManager.begin(); uint8_t _checkforclock = I2CManager.checkAddress(i2cAddress); DIAG(F("Clock check result - %d"), _checkforclock); // XXXX change thistosave2 bytes diff --git a/version.h b/version.h index 0677dec..443475b 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.52" +#define VERSION "5.2.53" +// 5.2.53 - Bugfix for EX-Fastclock, call I2CManager.begin() before checking I2C address // 5.2.52 - Bugfix for ADCee() to handle ADC2 and ADC3 channel inputs on F446ZE and others // - Add support for ports G and H on STM32 for ADCee() and MotorDriver pins/shadow regs // 5.2.51 - Bugfix for SIGNAL: Distinguish between sighandle and sigid From bd11cfbf8b8a8030d1eb58d462c5b639cde32d5f Mon Sep 17 00:00:00 2001 From: pmantoine Date: Tue, 7 May 2024 11:29:49 +0800 Subject: [PATCH 47/89] Bugfix EXRAIL active high signal handling --- EXRAIL2.cpp | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index b811145..96763d5 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1218,7 +1218,7 @@ int16_t RMFT2::getSignalSlot(int16_t id) { 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; + bool aHigh=sighandle & ACTIVE_HIGH_SIGNAL_FLAG; // set the three pins if (redpin) { diff --git a/version.h b/version.h index 443475b..3260ebb 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.53" +#define VERSION "5.2.54" +// 5.2.54 - Bugfix for EXRAIL signal handling for active high // 5.2.53 - Bugfix for EX-Fastclock, call I2CManager.begin() before checking I2C address // 5.2.52 - Bugfix for ADCee() to handle ADC2 and ADC3 channel inputs on F446ZE and others // - Add support for ports G and H on STM32 for ADCee() and MotorDriver pins/shadow regs From 1449dc7bac4d375c630a5bd51515d0008b3913df Mon Sep 17 00:00:00 2001 From: pmantoine Date: Tue, 7 May 2024 15:12:37 +0800 Subject: [PATCH 48/89] EXRAIL move isSignal to public for STEALTH --- EXRAIL2.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/EXRAIL2.h b/EXRAIL2.h index 794eb86..8750e41 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -187,6 +187,7 @@ class LookList { static const FSH * getTurntablePositionDescription(int16_t turntableId, uint8_t positionId); static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc); static bool readSensor(uint16_t sensorId); + static bool isSignal(int16_t id,char rag); private: static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); @@ -196,7 +197,6 @@ private: static bool getFlag(VPIN id,byte mask); static int16_t progtrackLocoId; static void doSignal(int16_t id,char rag); - static bool isSignal(int16_t id,char rag); static int16_t getSignalSlot(int16_t id); static void setTurnoutHiddenState(Turnout * t); #ifndef IO_NO_HAL diff --git a/version.h b/version.h index 3260ebb..bcf6470 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.54" +#define VERSION "5.2.55" +// 5.2.55 - Move EXRAIL isSignal() to public to allow use in STEALTH call // 5.2.54 - Bugfix for EXRAIL signal handling for active high // 5.2.53 - Bugfix for EX-Fastclock, call I2CManager.begin() before checking I2C address // 5.2.52 - Bugfix for ADCee() to handle ADC2 and ADC3 channel inputs on F446ZE and others From a610e83f6ec4e1c943d06f63b913ec802f1723e2 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Tue, 7 May 2024 18:20:37 +0800 Subject: [PATCH 49/89] Bugfix and refactor for EXRAIL getSignalSlot --- EXRAIL2.cpp | 30 ++++++++++++++++++------------ version.h | 3 ++- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 96763d5..38fd394 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1,4 +1,5 @@ /* + * © 2024 Paul M. Antoine * © 2021 Neil McKechnie * © 2021-2023 Harald Barth * © 2020-2023 Chris Harlow @@ -1143,20 +1144,25 @@ void RMFT2::kill(const FSH * reason, int operand) { } int16_t RMFT2::getSignalSlot(int16_t id) { - for (int sigslot=0;;sigslot++) { - int16_t sighandle=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); - if (sighandle==0) { // end of signal list - DIAG(F("EXRAIL Signal %d not defined"), id); - return -1; - } - VPIN sigid = sighandle & SIGNAL_ID_MASK; + + if (id > 0) { + int sigslot = 0; + int16_t sighandle = 0; + // Trundle down the signal list until we reach the end + while ((sighandle = GETHIGHFLASHW(RMFT2::SignalDefinitions, sigslot * 8)) != 0) + { // 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 != id) continue; // keep looking - return sigslot; // relative slot in signals table - } + // but for a servo signal it will also have SERVO_SIGNAL_FLAG set. + VPIN sigid = sighandle & SIGNAL_ID_MASK; + if (sigid == (VPIN)id) + return sigslot; // found it + sigslot++; // keep looking + }; + } + // We did not find the signal + DIAG(F("EXRAIL Signal %d not defined"), id); + return -1; } /* static */ void RMFT2::doSignal(int16_t id,char rag) { diff --git a/version.h b/version.h index bcf6470..eafba6c 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.55" +#define VERSION "5.2.56" +// 5.2.56 - Bugfix and refactor for EXRAIL getSignalSlot // 5.2.55 - Move EXRAIL isSignal() to public to allow use in STEALTH call // 5.2.54 - Bugfix for EXRAIL signal handling for active high // 5.2.53 - Bugfix for EX-Fastclock, call I2CManager.begin() before checking I2C address From 86310aea4f331dd2cf3d4a727643e085f59e1800 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 9 May 2024 15:18:00 +0200 Subject: [PATCH 50/89] getSignalSlot minor typo (and indent/comments) fix --- EXRAIL2.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 38fd394..f401eba 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1145,7 +1145,7 @@ void RMFT2::kill(const FSH * reason, int operand) { int16_t RMFT2::getSignalSlot(int16_t id) { - if (id > 0) { + if (id >= 0) { int sigslot = 0; int16_t sighandle = 0; // Trundle down the signal list until we reach the end @@ -1155,14 +1155,14 @@ int16_t RMFT2::getSignalSlot(int16_t id) { // 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 = sighandle & SIGNAL_ID_MASK; - if (sigid == (VPIN)id) - return sigslot; // found it - sigslot++; // keep looking + if (sigid == (VPIN)id) // cast to keep compiler happy but id is positive + return sigslot; // found it + sigslot++; // keep looking }; } - // We did not find the signal + // If we got here, we did not find the signal DIAG(F("EXRAIL Signal %d not defined"), id); - return -1; + return -1; } /* static */ void RMFT2::doSignal(int16_t id,char rag) { From 91818ed80c2d01d70634bf3746fcbb887b126397 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 9 May 2024 17:06:33 +0200 Subject: [PATCH 51/89] apply mode by binart bit match and not by equality --- TrackManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index d66b999..512452d 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -35,7 +35,7 @@ #define APPLY_BY_MODE(findmode,function) \ FOR_EACH_TRACK(t) \ - if (track[t]->getMode()==findmode) \ + if (track[t]->getMode() & findmode) \ track[t]->function; MotorDriver * TrackManager::track[MAX_TRACKS] = { NULL }; @@ -398,7 +398,7 @@ bool TrackManager::parseEqualSign(Print *stream, int16_t params, int16_t p[]) if (params==2 && p[1]=="AUTO"_hk) // <= id AUTO> return setTrackMode(p[0], track[p[0]]->getMode() | TRACK_MODE_AUTOINV); - if (params==2 && p[1]=="INV"_hk) // <= id AUTO> + if (params==2 && p[1]=="INV"_hk) // <= id INV> return setTrackMode(p[0], track[p[0]]->getMode() | TRACK_MODE_INV); if (params==3 && p[1]=="DC"_hk && p[2]>0) // <= id DC cab> From 6689a1d35fc2927f0e5aa1da99525cc8b2b0bf82 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 9 May 2024 17:11:09 +0200 Subject: [PATCH 52/89] version 5.2.57 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index ab13219..8fd0549 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202404211704Z" +#define GITHUB_SHA "devel-202404091507Z" diff --git a/version.h b/version.h index eafba6c..dc6dfc3 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.56" +#define VERSION "5.2.57" +// 5.2.57 - Bugfix autoreverse: Apply mode by binart bit match and not by equality // 5.2.56 - Bugfix and refactor for EXRAIL getSignalSlot // 5.2.55 - Move EXRAIL isSignal() to public to allow use in STEALTH call // 5.2.54 - Bugfix for EXRAIL signal handling for active high From 66791b19f53bf2470d22c36fd28768f41d71455c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 11 May 2024 07:43:24 +0200 Subject: [PATCH 53/89] remove stupid comment --- DCCTimerAVR.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCTimerAVR.cpp b/DCCTimerAVR.cpp index cb9e685..2806b07 100644 --- a/DCCTimerAVR.cpp +++ b/DCCTimerAVR.cpp @@ -186,7 +186,7 @@ int DCCTimer::freeMemory() { void DCCTimer::reset() { wdt_enable( WDTO_15MS); // set Arduino watchdog timer for 15ms - delay(50); // wait for the prescaller time to expire + delay(50); // wait for it to happen } From 86291cbec41ca4b9f53a73a6212d21fa9198c239 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 11 May 2024 07:45:28 +0200 Subject: [PATCH 54/89] signal id of 0 does not work --- EXRAIL2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index f401eba..6980f03 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1145,7 +1145,7 @@ void RMFT2::kill(const FSH * reason, int operand) { int16_t RMFT2::getSignalSlot(int16_t id) { - if (id >= 0) { + if (id > 0) { int sigslot = 0; int16_t sighandle = 0; // Trundle down the signal list until we reach the end From 2172d2e1754fd9dbe87cf2c8c80a3ddab0bf9185 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 11 May 2024 08:46:25 +0200 Subject: [PATCH 55/89] make WDT time longer to work around bootloader bug --- DCCTimerAVR.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/DCCTimerAVR.cpp b/DCCTimerAVR.cpp index 2806b07..656ba7e 100644 --- a/DCCTimerAVR.cpp +++ b/DCCTimerAVR.cpp @@ -185,8 +185,10 @@ int DCCTimer::freeMemory() { } void DCCTimer::reset() { - wdt_enable( WDTO_15MS); // set Arduino watchdog timer for 15ms - delay(50); // wait for it to happen + // 250ms chosen to circumwent bootloader bug which + // hangs at too short timepout (like 15ms) + wdt_enable( WDTO_250MS); // set Arduino watchdog timer for 250ms + delay(500); // wait for it to happen } From 06b8995861f0dfac3300f9171dc6657cc0c754e1 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 21 May 2024 20:04:11 +0100 Subject: [PATCH 56/89] ALIAS(named pins) --- EXRAILMacros.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index e9e2f77..827c1d2 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -75,7 +75,7 @@ // Pass 1 Implements aliases #include "EXRAIL2MacroReset.h" #undef ALIAS -#define ALIAS(name,value...) const int name= 1##value##0 ==10 ? -__COUNTER__ : value##0/10; +#define ALIAS(name,value...) const int name= #value[0] ? value+0: -__COUNTER__ ; #include "myAutomation.h" // Pass 1d Detect sequence duplicates. diff --git a/version.h b/version.h index dc6dfc3..e5e0241 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.57" +#define VERSION "5.2.58" +// 5.2.58 - EXRAIL ALIAS allows named pins // 5.2.57 - Bugfix autoreverse: Apply mode by binart bit match and not by equality // 5.2.56 - Bugfix and refactor for EXRAIL getSignalSlot // 5.2.55 - Move EXRAIL isSignal() to public to allow use in STEALTH call From 449a5f1670ae12000020fcbf72e1a4ee6bac7511 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Wed, 22 May 2024 07:16:25 +0800 Subject: [PATCH 57/89] STM32 updates for serial ports etc. --- DCCTimerSTM32.cpp | 17 +++++++++++++++-- WifiInterface.cpp | 3 ++- version.h | 4 +++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index 7a13919..0917455 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -36,7 +36,20 @@ #include "DIAG.h" #include -#if defined(ARDUINO_NUCLEO_F401RE) || defined(ARDUINO_NUCLEO_F411RE) +#if defined(ARDUINO_NUCLEO_F401RE) +// Nucleo-64 boards don't have additional serial ports defined by default +// Serial1 is available on the F401RE, but not hugely convenient. +// Rx pin on PB7 is useful, but all the Tx pins map to Arduino digital pins, specifically: +// PA9 == D8 +// PB6 == D10 +// of which D8 is needed by the standard and EX8874 motor shields. D10 would be used if a second +// EX8874 is stacked. So only disable this if using a second motor shield. +HardwareSerial Serial1(PB7, PB6); // Rx=PB7, Tx=PB6 -- CN7 pin 17 and CN10 pin 17 +// Serial2 is defined to use USART2 by default, but is in fact used as the diag console +// via the debugger on the Nucleo-64. It is therefore unavailable for other DCC-EX uses like WiFi, DFPlayer, etc. +// Let's define Serial6 as an additional serial port (the only other option for the F401RE) +HardwareSerial Serial6(PA12, PA11); // Rx=PA12, Tx=PA11 -- CN10 pins 12 and 14 - F401RE +#elif defined(ARDUINO_NUCLEO_F411RE) // Nucleo-64 boards don't have additional serial ports defined by default HardwareSerial Serial1(PB7, PA15); // Rx=PB7, Tx=PA15 -- CN7 pins 17 and 21 - F411RE // Serial2 is defined to use USART2 by default, but is in fact used as the diag console @@ -54,7 +67,7 @@ HardwareSerial Serial3(PC11, PC10); // Rx=PC11, Tx=PC10 -- USART3 - F446RE HardwareSerial Serial5(PD2, PC12); // Rx=PD2, Tx=PC12 -- UART5 - F446RE // On the F446RE, Serial4 and Serial6 also use pins we can't readily map while using the Arduino pins #elif defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F446ZE) || \ - defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F439ZI) + defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F439ZI) || defined(ARDUINO_NUCLEO_F4X9ZI) // Nucleo-144 boards don't have Serial1 defined by default HardwareSerial Serial6(PG9, PG14); // Rx=PG9, Tx=PG14 -- USART6 HardwareSerial Serial5(PD2, PC12); // Rx=PD2, Tx=PC12 -- UART5 diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 87d5437..fcf9932 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -1,4 +1,5 @@ /* + * © 2022-2024 Paul M. Antoine * © 2021 Fred Decker * © 2020-2022 Harald Barth * © 2020-2022 Chris Harlow @@ -70,7 +71,7 @@ Stream * WifiInterface::wifiStream; #define SERIAL3 Serial5 #elif defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F429ZI) \ || defined(ARDUINO_NUCLEO_F446ZE) || defined(ARDUINO_NUCLEO_F412ZG) \ - || defined(ARDUINO_NUCLEO_F439ZI) + || defined(ARDUINO_NUCLEO_F439ZI) || defined(ARDUINO_NUCLEO_F4X9ZI) #define NUM_SERIAL 2 #define SERIAL1 Serial6 #else diff --git a/version.h b/version.h index e5e0241..2079a1e 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.58" +#define VERSION "5.2.59" +// 5.2.59 - STM32 bugfix correct Serial1 definition for Nucleo-F401RE +// - STM32 add support for ARDUINO_NUCLEO_F4X9ZI type to span F429/F439 in upcoming STM32duino release v2.8 as a result of our PR // 5.2.58 - EXRAIL ALIAS allows named pins // 5.2.57 - Bugfix autoreverse: Apply mode by binart bit match and not by equality // 5.2.56 - Bugfix and refactor for EXRAIL getSignalSlot From b17dc5a0dd4e28e87f1173233c7ef9f3b8a2340a Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 23 May 2024 22:39:43 +0200 Subject: [PATCH 58/89] Bugfix: Opcode AFTEROVERLOAD does not have an argument that is a pin and needs to be initialized --- EXRAIL2.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 6980f03..088b79b 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -228,7 +228,6 @@ 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; From 843fa42692b50570a28dc4eb06c4781379071750 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 23 May 2024 22:41:50 +0200 Subject: [PATCH 59/89] Remove inrush throttle after half good time so that we go to mode overload if problem persists --- MotorDriver.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MotorDriver.cpp b/MotorDriver.cpp index da9d3ee..28fbfa3 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -638,6 +638,10 @@ void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) { } throttleInrush(false); setPower(POWERMODE::ON); + break; + } + if (goodtime > POWER_SAMPLE_ALERT_GOOD/2) { + throttleInrush(false); } break; } From 0c96d4ffc2e1af26b97977b6f575fd4451b407e5 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 23 May 2024 22:46:47 +0200 Subject: [PATCH 60/89] version 5.2.60 --- GITHUB_SHA.h | 2 +- version.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 8fd0549..4807e45 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202404091507Z" +#define GITHUB_SHA "devel-202405232026Z" diff --git a/version.h b/version.h index 2079a1e..ddbcfac 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.59" +#define VERSION "5.2.60" +// 5.2.60 - Bugfix: Opcode AFTEROVERLOAD does not have an argument that is a pin and needs to be initialized +// - Remove inrush throttle after half good time so that we go to mode overload if problem persists // 5.2.59 - STM32 bugfix correct Serial1 definition for Nucleo-F401RE // - STM32 add support for ARDUINO_NUCLEO_F4X9ZI type to span F429/F439 in upcoming STM32duino release v2.8 as a result of our PR // 5.2.58 - EXRAIL ALIAS allows named pins From 264a53dacfaf502654f80206c972153db3378ac6 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 2 Jun 2024 21:10:57 +0200 Subject: [PATCH 61/89] ESP32: Refuse IDF5 --- DCCTimerESP.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/DCCTimerESP.cpp b/DCCTimerESP.cpp index 93711aa..8927573 100644 --- a/DCCTimerESP.cpp +++ b/DCCTimerESP.cpp @@ -76,8 +76,13 @@ int DCCTimer::freeMemory() { #endif //////////////////////////////////////////////////////////////////////// - #ifdef ARDUINO_ARCH_ESP32 + +#include "esp_idf_version.h" +#if ESP_IDF_VERSION_MAJOR > 4 +#error "DCC-EX does not support compiling with IDF version 5.0 or later. Downgrade your ESP32 library to a version that contains version 4 Arduino ESP32 library 3.0.0 is too new. Use 2.0.9 to 2.0.17" +#endif + #include "DIAG.h" #include #include From a26610bc7fecee556e3b46f0bffeca878a15f100 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 2 Jun 2024 21:44:25 +0200 Subject: [PATCH 62/89] ESP32: More version locking --- DCCTimerESP.cpp | 2 +- platformio.ini | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/DCCTimerESP.cpp b/DCCTimerESP.cpp index 8927573..e57f6af 100644 --- a/DCCTimerESP.cpp +++ b/DCCTimerESP.cpp @@ -80,7 +80,7 @@ int DCCTimer::freeMemory() { #include "esp_idf_version.h" #if ESP_IDF_VERSION_MAJOR > 4 -#error "DCC-EX does not support compiling with IDF version 5.0 or later. Downgrade your ESP32 library to a version that contains version 4 Arduino ESP32 library 3.0.0 is too new. Use 2.0.9 to 2.0.17" +#error "DCC-EX does not support compiling with IDF version 5.0 or later. Downgrade your ESP32 library to a version that contains IDE version 4. Arduino ESP32 library 3.0.0 is too new. Downgrade to one of 2.0.9 to 2.0.17" #endif #include "DIAG.h" diff --git a/platformio.ini b/platformio.ini index a03ff61..b39b136 100644 --- a/platformio.ini +++ b/platformio.ini @@ -164,7 +164,11 @@ monitor_echo = yes build_flags = -mcall-prologues [env:ESP32] -platform = espressif32 +; Lock version to 6.7.0 as that is +; Arduino v2.0.16 (based on IDF v4.4.7) +; which is the latest version based +; on IDF v4. We can not use IDF v5. +platform = espressif32 @ 6.7.0 board = esp32dev framework = arduino lib_deps = ${env.lib_deps} From 5f65fd5944d6def87673a3d21881b4d21bfcac68 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 2 Jun 2024 21:45:43 +0200 Subject: [PATCH 63/89] tag with date --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 4807e45..6abb9b3 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202405232026Z" +#define GITHUB_SHA "devel-202406021945Z" From fba9a3081347570c2854b555474ff596e6f17e34 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 11 Jun 2024 23:09:41 +0200 Subject: [PATCH 64/89] ESP32: Espressif deprecated ADC_ATTEN_DB_11 --- DCCTimerESP.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/DCCTimerESP.cpp b/DCCTimerESP.cpp index e57f6af..39824a5 100644 --- a/DCCTimerESP.cpp +++ b/DCCTimerESP.cpp @@ -297,7 +297,12 @@ void DCCTimer::DCCEXInrushControlOn(uint8_t pin, int duty, bool inverted) { int ADCee::init(uint8_t pin) { pinMode(pin, ANALOG); adc1_config_width(ADC_WIDTH_BIT_12); +// Espressif deprecated ADC_ATTEN_DB_11 somewhere between 2.0.9 and 2.0.17 +#ifdef ADC_ATTEN_11db + adc1_config_channel_atten(pinToADC1Channel(pin),ADC_ATTEN_11db); +#else adc1_config_channel_atten(pinToADC1Channel(pin),ADC_ATTEN_DB_11); +#endif return adc1_get_raw(pinToADC1Channel(pin)); } int16_t ADCee::ADCmax() { From 3d6c935308098e463ae0abc8b6ea59e5f19a2afa Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 11 Jun 2024 23:10:18 +0200 Subject: [PATCH 65/89] EXCSB1 motor driver definitions --- MotorDrivers.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MotorDrivers.h b/MotorDrivers.h index 9e5f85b..d51ab16 100644 --- a/MotorDrivers.h +++ b/MotorDrivers.h @@ -97,6 +97,18 @@ new MotorDriver(25/* 3*/, 19/*12*/, UNUSED_PIN, 13/*9*/, 35/*A2*/, 1.27, 5000, 36 /*A4*/), \ new MotorDriver(23/*11*/, 18/*13*/, UNUSED_PIN, 12/*8*/, 34/*A3*/, 1.27, 5000, 39 /*A5*/) +// EX-CSB1 with integrated motor driver definition +#define EXCSB1 F("EXCSB1"),\ + new MotorDriver(25, 0, UNUSED_PIN, -14, 34, 2.23, 5000, 19), \ + new MotorDriver(27, 15, UNUSED_PIN, -2, 35, 2.23, 5000, 23) + +// EX-CSB1 with EX-8874 stacked on top for 4 outputs +#define EXCSB1_WITH_EX8874 F("EXCSB1_WITH_EX8874"),\ + new MotorDriver(25, 0, UNUSED_PIN, -14, 34, 2.23, 5000, 19), \ + new MotorDriver(27, 15, UNUSED_PIN, -2, 35, 2.23, 5000, 23), \ + new MotorDriver(26, 5, UNUSED_PIN, 13, 36, 1.52, 5000, 18), \ + new MotorDriver(16, 4, UNUSED_PIN, 12, 39, 1.52, 5000, 17) + #else // STANDARD shield on any Arduino Uno or Mega compatible with the original specification. #define STANDARD_MOTOR_SHIELD F("STANDARD_MOTOR_SHIELD"), \ From 97f9fb48136da2b51678c2f2ac5693f825c483b2 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 13 Jun 2024 13:01:33 +0100 Subject: [PATCH 66/89] Squashed commit of the following: commit a2b3ee8b5d52c2eefa461ace8f95c7f782a58efc Merge: fc1217b 3d6c935 Author: Asbelos Date: Thu Jun 13 11:58:00 2024 +0100 Merge branch 'devel' into devel_merg commit fc1217b8fa27a83174a4cf3bb82666f075103637 Author: Asbelos Date: Thu Jun 13 11:57:12 2024 +0100 Update EXRAIL2Parser.cpp commit b89508671ccf5701c6f2cf1dd4f6a6d106a558c0 Author: Asbelos Date: Wed Jun 12 16:25:17 2024 +0100 Separate polling cycle commit 9f1257bc6ce8e36b507f8e7362d26fd3a1656028 Merge: a2fb585 5f65fd5 Author: Asbelos Date: Wed Jun 12 10:57:09 2024 +0100 Merge branch 'devel' into devel_merg commit a2fb58584f489c24ace18f089da954d70b15bdac Author: Asbelos Date: Fri May 31 19:49:39 2024 +0100 ACON/ACOF 32 bit + 1=OFF commit fca4ea052ef28425f3749a9c5696710326cf3bf8 Author: Asbelos Date: Fri May 31 12:09:38 2024 +0100 Rename to ACON/ACOF terminology commit 0d07aa6271813b38dbcc74dd00d86a9c0dc6f060 Author: Asbelos Date: Thu May 30 19:59:29 2024 +0100 MERG macris in exrail --- EXRAIL2.cpp | 10 ++++++ EXRAIL2.h | 2 ++ EXRAIL2MacroReset.h | 8 +++++ EXRAIL2Parser.cpp | 84 ++++++++++++++++++++++++++++++++------------- EXRAILMacros.h | 18 +++++++++- 5 files changed, 98 insertions(+), 24 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 088b79b..b2f29e2 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1000,6 +1000,14 @@ void RMFT2::loop2() { if ((compileFeatures & FEATURE_LCC) && LCCSerial) StringFormatter::send(LCCSerial,F(""),(uint16_t)operand); break; + + case OPCODE_ACON: // MERG adapter + case OPCODE_ACOF: + if ((compileFeatures & FEATURE_LCC) && LCCSerial) + StringFormatter::send(LCCSerial,F(""), + opcode==OPCODE_ACON?'0':'1', + (uint16_t)operand,getOperand(progCounter,1)); + break; case OPCODE_LCCX: // long form LCC if ((compileFeatures & FEATURE_LCC) && LCCSerial) @@ -1088,6 +1096,8 @@ void RMFT2::loop2() { case OPCODE_PINTURNOUT: // Turnout definition ignored at runtime case OPCODE_ONCLOSE: // Turnout event catchers ignored here case OPCODE_ONLCC: // LCC event catchers ignored here + case OPCODE_ONACON: // MERG event catchers ignored here + case OPCODE_ONACOF: // MERG event catchers ignored here case OPCODE_ONTHROW: case OPCODE_ONACTIVATE: // Activate event catchers ignored here case OPCODE_ONDEACTIVATE: diff --git a/EXRAIL2.h b/EXRAIL2.h index 8750e41..9271bba 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -69,6 +69,8 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,OPCODE_TOGGLE_TURNOUT, OPCODE_TTADDPOSITION,OPCODE_DCCTURNTABLE,OPCODE_EXTTTURNTABLE, OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_WAITFORTT, OPCODE_LCC,OPCODE_LCCX,OPCODE_ONLCC, + OPCODE_ACON, OPCODE_ACOF, + OPCODE_ONACON, OPCODE_ONACOF, OPCODE_ONOVERLOAD, OPCODE_ROUTE_ACTIVE,OPCODE_ROUTE_INACTIVE,OPCODE_ROUTE_HIDDEN, OPCODE_ROUTE_DISABLED, diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index c799ddf..8927fbe 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -99,6 +99,10 @@ #undef LCCX #undef LCN #undef MOVETT +#undef ACON +#undef ACOF +#undef ONACON +#undef ONACOF #undef MESSAGE #undef ONACTIVATE #undef ONACTIVATEL @@ -265,6 +269,10 @@ #define LCN(msg) #define MESSAGE(msg) #define MOVETT(id,steps,activity) +#define ACON(eventid) +#define ACOF(eventid) +#define ONACON(eventid) +#define ONACOF(eventid) #define ONACTIVATE(addr,subaddr) #define ONACTIVATEL(linear) #define ONAMBER(signal_id) diff --git a/EXRAIL2Parser.cpp b/EXRAIL2Parser.cpp index 4023633..8c498ab 100644 --- a/EXRAIL2Parser.cpp +++ b/EXRAIL2Parser.cpp @@ -61,47 +61,85 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16 case 'L': // This entire code block is compiled out if LLC macros not used if (!(compileFeatures & FEATURE_LCC)) return; - + static int lccProgCounter=0; + static int lccEventIndex=0; + if (paramCount==0) { // LCC adapter introducing self LCCSerial=stream; // now we know where to send events we raise + opcode=0; // flag command as intercepted - // loop through all possible sent events - for (int progCounter=0;; SKIPOP) { - byte opcode=GET_OPCODE; - if (opcode==OPCODE_ENDEXRAIL) break; - if (opcode==OPCODE_LCC) StringFormatter::send(stream,F("\n"),getOperand(progCounter,0)); - if (opcode==OPCODE_LCCX) { // long form LCC - StringFormatter::send(stream,F("\n"), + // loop through all possible sent/waited events + for (int progCounter=lccProgCounter;; SKIPOP) { + byte exrailOpcode=GET_OPCODE; + switch (exrailOpcode) { + case OPCODE_ENDEXRAIL: + stream->print(F("\n")); // ready to roll + lccProgCounter=0; // allow a second pass + lccEventIndex=0; + return; + + case OPCODE_LCC: + StringFormatter::send(stream,F("\n"),getOperand(progCounter,0)); + SKIPOP; + lccProgCounter=progCounter; + return; + + case OPCODE_LCCX: // long form LCC + StringFormatter::send(stream,F("\n"), getOperand(progCounter,1), getOperand(progCounter,2), getOperand(progCounter,3), getOperand(progCounter,0) - ); - }} + ); + SKIPOP;SKIPOP;SKIPOP;SKIPOP; + lccProgCounter=progCounter; + return; + + case OPCODE_ACON: // CBUS ACON + case OPCODE_ACOF: // CBUS ACOF + StringFormatter::send(stream,F("\n"), + exrailOpcode==OPCODE_ACOF?'1':'0', + getOperand(progCounter,0),getOperand(progCounter,1)); + SKIPOP;SKIPOP; + lccProgCounter=progCounter; + return; // we stream the hex events we wish to listen to // and at the same time build the event index looku. - - int eventIndex=0; - for (int progCounter=0;; SKIPOP) { - byte opcode=GET_OPCODE; - if (opcode==OPCODE_ENDEXRAIL) break; - if (opcode==OPCODE_ONLCC) { - onLCCLookup[eventIndex]=progCounter; // TODO skip... + case OPCODE_ONLCC: StringFormatter::send(stream,F("\n"), - eventIndex, + lccEventIndex, getOperand(progCounter,1), getOperand(progCounter,2), getOperand(progCounter,3), getOperand(progCounter,0) ); - eventIndex++; - } + SKIPOP;SKIPOP;SKIPOP;SKIPOP; + // start on handler at next + onLCCLookup[lccEventIndex]=progCounter; + lccEventIndex++; + lccProgCounter=progCounter; + return; + + case OPCODE_ONACON: + case OPCODE_ONACOF: + StringFormatter::send(stream,F("\n"), + lccEventIndex, + exrailOpcode==OPCODE_ONACOF?'1':'0', + getOperand(progCounter,0),getOperand(progCounter,1) + ); + SKIPOP;SKIPOP; + // start on handler at next + onLCCLookup[lccEventIndex]=progCounter; + lccEventIndex++; + lccProgCounter=progCounter; + return; + + default: + break; + } } - StringFormatter::send(stream,F("\n")); // Ready to rumble - opcode=0; - break; } if (paramCount==1) { // LCC event arrived from adapter int16_t eventid=p[0]; diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 827c1d2..e4d72a1 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -189,6 +189,14 @@ bool exrailHalSetup() { #define LCCX(senderid,eventid) | FEATURE_LCC #undef ONLCC #define ONLCC(senderid,eventid) | FEATURE_LCC +#undef ACON +#define ACON(eventid) | FEATURE_LCC +#undef ACOF +#define ACOF(eventid) | FEATURE_LCC +#undef ONACON +#define ONACON(eventid) | FEATURE_LCC +#undef ONACOF +#define ONACOF(eventid) | FEATURE_LCC #undef ROUTE_ACTIVE #define ROUTE_ACTIVE(id) | FEATURE_ROUTESTATE #undef ROUTE_INACTIVE @@ -429,10 +437,14 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #include "myAutomation.h" 0,0,0,0 }; -// Pass 9 ONLCC counter and lookup array +// Pass 9 ONLCC/ ONMERG counter and lookup array #include "EXRAIL2MacroReset.h" #undef ONLCC #define ONLCC(sender,event) +1 +#undef ONACON +#define ONACON(event) +1 +#undef ONACOF +#define ONACOF(event) +1 const int RMFT2::countLCCLookup=0 #include "myAutomation.h" @@ -529,6 +541,10 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; OPCODE_PAD,V((((uint64_t)sender)>>32)&0xFFFF),\ OPCODE_PAD,V((((uint64_t)sender)>>16)&0xFFFF),\ OPCODE_PAD,V((((uint64_t)sender)>>0)&0xFFFF), +#define ACON(eventid) OPCODE_ACON,V(((uint32_t)eventid >>16) & 0xFFFF),OPCODE_PAD,V(eventid & 0xFFFF), +#define ACOF(eventid) OPCODE_ACOF,V(((uint32_t)eventid >>16) & 0xFFFF),OPCODE_PAD,V(eventid & 0xFFFF), +#define ONACON(eventid) OPCODE_ONACON,V((uint32_t)(eventid) >>16),OPCODE_PAD,V(eventid & 0xFFFF), +#define ONACOF(eventid) OPCODE_ONACOF,V((uint32_t)(eventid) >>16),OPCODE_PAD,V(eventid & 0xFFFF), #define LCD(id,msg) PRINT(msg) #define SCREEN(display,id,msg) PRINT(msg) #define STEALTH(code...) PRINT(dummy) From 6ef312b510239dc15b11710f0303bef158a95bf7 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 13 Jun 2024 13:08:40 +0100 Subject: [PATCH 67/89] 5.2.61 --- version.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index ddbcfac..f9c40c9 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,10 @@ #include "StringFormatter.h" -#define VERSION "5.2.60" +#define VERSION "5.2.61" +// 5.2.61 - Merg CBUS ACON/ACOF/ONACON/ONACOF Adapter interface. +// - LCC Adapter interface throttled startup, +// (Breaking change woith Adapter base code) // 5.2.60 - Bugfix: Opcode AFTEROVERLOAD does not have an argument that is a pin and needs to be initialized // - Remove inrush throttle after half good time so that we go to mode overload if problem persists // 5.2.59 - STM32 bugfix correct Serial1 definition for Nucleo-F401RE From 7dadecb5df9530af48f5d7d84f377a51e1811e86 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 13 Jun 2024 16:09:28 +0100 Subject: [PATCH 68/89] Typo in KeywordHasher --- KeywordHasher.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KeywordHasher.h b/KeywordHasher.h index d232bd2..4b1e0fb 100644 --- a/KeywordHasher.h +++ b/KeywordHasher.h @@ -26,7 +26,7 @@ Thus "MAIN"_hk generates exactly the same run time vakue as const int16_t HASH_KEYWORD_MAIN=11339 */ -#ifndef KeywordHAsher_h +#ifndef KeywordHasher_h #define KeywordHasher_h #include From 2481f1c5d673787fcf26603b33a0c02a2e0f4b8d Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 18 Jun 2024 22:18:23 +0200 Subject: [PATCH 69/89] Allow acks longer than 65535us and specify ack length in the format --- DCCACK.cpp | 12 ++++++------ DCCACK.h | 10 +++++----- DCCEXParser.cpp | 19 ++++++++++++++----- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/DCCACK.cpp b/DCCACK.cpp index 517d513..9d33529 100644 --- a/DCCACK.cpp +++ b/DCCACK.cpp @@ -27,8 +27,8 @@ #include "DCCWaveform.h" #include "TrackManager.h" -unsigned int DCCACK::minAckPulseDuration = 2000; // micros -unsigned int DCCACK::maxAckPulseDuration = 20000; // micros +unsigned long DCCACK::minAckPulseDuration = 2000; // micros +unsigned long DCCACK::maxAckPulseDuration = 20000; // micros MotorDriver * DCCACK::progDriver=NULL; ackOp const * DCCACK::ackManagerProg; @@ -50,8 +50,8 @@ volatile uint8_t DCCACK::numAckSamples=0; uint8_t DCCACK::trailingEdgeCounter=0; - unsigned int DCCACK::ackPulseDuration; // micros - unsigned long DCCACK::ackPulseStart; // micros +unsigned long DCCACK::ackPulseDuration; // micros +unsigned long DCCACK::ackPulseStart; // micros volatile bool DCCACK::ackDetected; unsigned long DCCACK::ackCheckStart; // millis volatile bool DCCACK::ackPending; @@ -127,7 +127,7 @@ bool DCCACK::checkResets(uint8_t numResets) { void DCCACK::setAckBaseline() { int baseline=progDriver->getCurrentRaw(); ackThreshold= baseline + progDriver->mA2raw(ackLimitmA); - if (Diag::ACK) DIAG(F("ACK baseline=%d/%dmA Threshold=%d/%dmA Duration between %uus and %uus"), + if (Diag::ACK) DIAG(F("ACK baseline=%d/%dmA Threshold=%d/%dmA Duration between %lus and %lus"), baseline,progDriver->raw2mA(baseline), ackThreshold,progDriver->raw2mA(ackThreshold), minAckPulseDuration, maxAckPulseDuration); @@ -146,7 +146,7 @@ void DCCACK::setAckPending() { byte DCCACK::getAck() { if (ackPending) return (2); // still waiting - if (Diag::ACK) DIAG(F("%S after %dmS max=%d/%dmA pulse=%uuS samples=%d gaps=%d"),ackDetected?F("ACK"):F("NO-ACK"), ackCheckDuration, + if (Diag::ACK) DIAG(F("%S after %dmS max=%d/%dmA pulse=%luS samples=%d gaps=%d"),ackDetected?F("ACK"):F("NO-ACK"), ackCheckDuration, ackMaxCurrent,progDriver->raw2mA(ackMaxCurrent), ackPulseDuration, numAckSamples, numAckGaps); if (ackDetected) return (1); // Yes we had an ack return(0); // pending set off but not detected means no ACK. diff --git a/DCCACK.h b/DCCACK.h index fa03387..c50dbbd 100644 --- a/DCCACK.h +++ b/DCCACK.h @@ -79,10 +79,10 @@ class DCCACK { static inline void setAckLimit(int mA) { ackLimitmA = mA; } - static inline void setMinAckPulseDuration(unsigned int i) { + static inline void setMinAckPulseDuration(unsigned long i) { minAckPulseDuration = i; } - static inline void setMaxAckPulseDuration(unsigned int i) { + static inline void setMaxAckPulseDuration(unsigned long i) { maxAckPulseDuration = i; } @@ -126,11 +126,11 @@ class DCCACK { static unsigned long ackCheckStart; // millis static unsigned int ackCheckDuration; // millis - static unsigned int ackPulseDuration; // micros + static unsigned long ackPulseDuration; // micros static unsigned long ackPulseStart; // micros - static unsigned int minAckPulseDuration ; // micros - static unsigned int maxAckPulseDuration ; // micros + static unsigned long minAckPulseDuration ; // micros + static unsigned long maxAckPulseDuration ; // micros static MotorDriver* progDriver; static volatile uint8_t numAckGaps; static volatile uint8_t numAckSamples; diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 431093f..34117eb 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1073,15 +1073,24 @@ bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) { #ifndef DISABLE_PROG case "ACK"_hk: // if (params >= 3) { + long duration; if (p[1] == "LIMIT"_hk) { DCCACK::setAckLimit(p[2]); - LCD(1, F("Ack Limit=%dmA"), p[2]); // + LCD(1, F("Ack Limit=%dmA"), p[2]); // } else if (p[1] == "MIN"_hk) { - DCCACK::setMinAckPulseDuration(p[2]); - LCD(0, F("Ack Min=%uus"), p[2]); // + if (params == 4 && p[3] == "MS"_hk) + duration = p[2] * 1000L; + else + duration = p[2]; + DCCACK::setMinAckPulseDuration(duration); + LCD(0, F("Ack Min=%lus"), duration); // } else if (p[1] == "MAX"_hk) { - DCCACK::setMaxAckPulseDuration(p[2]); - LCD(0, F("Ack Max=%uus"), p[2]); // + if (params == 4 && p[3] == "MS"_hk) // + duration = p[2] * 1000L; + else + duration = p[2]; + DCCACK::setMaxAckPulseDuration(duration); + LCD(0, F("Ack Max=%lus"), duration); // } else if (p[1] == "RETRY"_hk) { if (p[2] >255) p[2]=3; LCD(0, F("Ack Retry=%d Sum=%d"), p[2], DCCACK::setAckRetry(p[2])); // From 023c0048420f29765333e0140e699cc8264f0867 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 18 Jun 2024 22:21:51 +0200 Subject: [PATCH 70/89] version 5.2.62 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 6abb9b3..48e2fdf 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202406021945Z" +#define GITHUB_SHA "devel-202406182019Z" diff --git a/version.h b/version.h index f9c40c9..d91ff41 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.61" +#define VERSION "5.2.62" +// 5.2.62 - Allow acks way longer than standard // 5.2.61 - Merg CBUS ACON/ACOF/ONACON/ONACOF Adapter interface. // - LCC Adapter interface throttled startup, // (Breaking change woith Adapter base code) From df1f365c1e8b22b76371bc766fdf9c2f0317487e Mon Sep 17 00:00:00 2001 From: pmantoine Date: Sat, 29 Jun 2024 16:22:23 +0800 Subject: [PATCH 71/89] Add WIFI_LED option for ESP32, edits for config.example.h --- WifiESP32.cpp | 13 +++++++++++++ config.example.h | 14 ++++++++++++-- version.h | 5 ++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/WifiESP32.cpp b/WifiESP32.cpp index e45d0e8..83dd2e1 100644 --- a/WifiESP32.cpp +++ b/WifiESP32.cpp @@ -147,6 +147,12 @@ bool WifiESP::setup(const char *SSid, // enableCoreWDT(1); // disableCoreWDT(0); +#ifdef WIFI_LED + // Turn off Wifi LED + pinMode(WIFI_LED, OUTPUT); + digitalWrite(WIFI_LED, 0); +#endif + // clean start WiFi.mode(WIFI_STA); WiFi.disconnect(true); @@ -247,6 +253,13 @@ bool WifiESP::setup(const char *SSid, // no idea to go on return false; } +#ifdef WIFI_LED + else{ + // Turn on Wifi connected LED + digitalWrite(WIFI_LED, 1); + } +#endif + // Now Wifi is up, register the mDNS service if(!MDNS.begin(hostname)) { diff --git a/config.example.h b/config.example.h index 3fc86c3..7c4e254 100644 --- a/config.example.h +++ b/config.example.h @@ -307,11 +307,21 @@ The configuration file for DCC-EX Command Station // //#define SERIAL_BT_COMMANDS -// BOOSTER PIN INPUT ON ESP32 +// BOOSTER PIN INPUT ON ESP32 CS // On ESP32 you have the possibility to define a pin as booster input -// Arduio pin D2 is GPIO 26 on ESPDuino32 // +// Arduino pin D2 is GPIO 26 is Booster Input on ESPDuino32 //#define BOOSTER_INPUT 26 +// +// GPIO 32 is Booster Input on EX-CSB1 +//#define BOOSTER_INPUT 32 + +// ESP32 LED Wifi Indicator +// GPIO 2 on ESPduino32 +//#define WIFI_LED 2 +// +// GPIO 33 on EX-CSB1 +//#define WIFI_LED 33 // SABERTOOTH // diff --git a/version.h b/version.h index d91ff41..78bf055 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,10 @@ #include "StringFormatter.h" -#define VERSION "5.2.62" +#define VERSION "5.2.63" +// 5.2.63 - Implement WIFI_LED for ESP32, ESPduino32 and EX-CSB1, that is turned on when STA mode connects or AP mode is up +// - Add BOOSTER_INPUT definitions for ESPduino32 and EX-CSB1 to config.example.h +// - Add WIFI_LED definitions for ESPduino32 and EX-CSB1 to config.example.h // 5.2.62 - Allow acks way longer than standard // 5.2.61 - Merg CBUS ACON/ACOF/ONACON/ONACOF Adapter interface. // - LCC Adapter interface throttled startup, From c97284c15f446ab6b11ba1aa5cb101e9d358e9c2 Mon Sep 17 00:00:00 2001 From: Ash-4 Date: Sun, 30 Jun 2024 20:32:08 -0500 Subject: [PATCH 72/89] inrush overfault on stm32EC-Ash --- DCCEXParser.cpp | 3 ++- version.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 34117eb..902326e 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -2,7 +2,7 @@ * © 2022 Paul M Antoine * © 2021 Neil McKechnie * © 2021 Mike S - * © 2021 Herb Morton + * © 2021-2024 Herb Morton * © 2020-2023 Harald Barth * © 2020-2021 M Steve Todd * © 2020-2021 Fred Decker @@ -563,6 +563,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } #ifndef DISABLE_PROG else if (p[0]=="PROG"_hk) { // <0 PROG> + TrackManager::setJoin(false); TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off TrackManager::setTrackPower(TRACK_MODE_PROG, POWERMODE::OFF); } diff --git a/version.h b/version.h index 78bf055..96d7fbf 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.63" +#define VERSION "5.2.64" +// 5.2.64 - Bugfix: <0 PROG> updated to undo JOIN // 5.2.63 - Implement WIFI_LED for ESP32, ESPduino32 and EX-CSB1, that is turned on when STA mode connects or AP mode is up // - Add BOOSTER_INPUT definitions for ESPduino32 and EX-CSB1 to config.example.h // - Add WIFI_LED definitions for ESPduino32 and EX-CSB1 to config.example.h From d705626f4a6bd3131e591001fc36f814c31b6b74 Mon Sep 17 00:00:00 2001 From: Ash-4 <81280775+Ash-4@users.noreply.github.com> Date: Sun, 30 Jun 2024 21:29:34 -0500 Subject: [PATCH 73/89] <0 PROG> updated to undo JOIN Update of the commit message for 5.2.64 --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index 96d7fbf..1a75b01 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,7 @@ #include "StringFormatter.h" #define VERSION "5.2.64" -// 5.2.64 - Bugfix: <0 PROG> updated to undo JOIN +// 5.2.64 - Bugfix: <0 PROG> updated to undo JOIN // 5.2.63 - Implement WIFI_LED for ESP32, ESPduino32 and EX-CSB1, that is turned on when STA mode connects or AP mode is up // - Add BOOSTER_INPUT definitions for ESPduino32 and EX-CSB1 to config.example.h // - Add WIFI_LED definitions for ESPduino32 and EX-CSB1 to config.example.h From a8b4e397336a8355f87d9f5a9db2ebae89041a5b Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 4 Jul 2024 17:20:37 +0100 Subject: [PATCH 74/89] Speedup SETFREQ Avoid calling the DCC packets for setfreq macro. --- DCC.cpp | 14 ++++++++++++++ DCC.h | 1 + EXRAIL2.cpp | 36 +----------------------------------- version.h | 3 ++- 4 files changed, 18 insertions(+), 36 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 0aa623f..99ee71f 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -271,6 +271,20 @@ uint32_t DCC::getFunctionMap(int cab) { return (reg<0)?0:speedTable[reg].functions; } +// saves DC frequency (0..3) in spare functions 29,30,31 +void DCC::setDCFreq(int cab,byte freq) { + if (cab==0 || freq>3) return; + auto reg=lookupSpeedTable(cab,true); + // drop and replace F29,30,31 (top 3 bits) + auto newFunctions=speedTable[reg].functions & 0x1FFFFFFFUL; + if (freq==1) newFunctions |= (1UL<<29); // F29 + else if (freq==2) newFunctions |= (1UL<<30); // F30 + else if (freq==3) newFunctions |= (1UL<<31); // F31 + if (newFunctions==speedTable[reg].functions) return; // no change + speedTable[reg].functions=newFunctions; + CommandDistributor::broadcastLoco(reg); +} + void DCC::setAccessory(int address, byte port, bool gate, byte onoff /*= 2*/) { // onoff is tristate: // 0 => send off packet diff --git a/DCC.h b/DCC.h index 4bc222c..38c1952 100644 --- a/DCC.h +++ b/DCC.h @@ -70,6 +70,7 @@ public: static void changeFn(int cab, int16_t functionNumber); static int8_t getFn(int cab, int16_t functionNumber); static uint32_t getFunctionMap(int cab); + static void setDCFreq(int cab,byte freq); static void updateGroupflags(byte &flags, int16_t functionNumber); static void setAccessory(int address, byte port, bool gate, byte onoff = 2); static bool setExtendedAccessory(int16_t address, int16_t value, byte repeats=3); diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index b2f29e2..676d31c 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -716,41 +716,7 @@ void RMFT2::loop2() { case OPCODE_SETFREQ: // Frequency is default 0, or 1, 2,3 - //if (loco) DCC::setFn(loco,operand,true); - switch (operand) { - case 0: // default - all F-s off - if (loco) { - DCC::setFn(loco,29,false); - DCC::setFn(loco,30,false); - DCC::setFn(loco,31,false); - } - break; - case 1: - if (loco) { - DCC::setFn(loco,29,true); - DCC::setFn(loco,30,false); - DCC::setFn(loco,31,false); - } - break; - case 2: - if (loco) { - DCC::setFn(loco,29,false); - DCC::setFn(loco,30,true); - DCC::setFn(loco,31,false); - } - break; - case 3: - if (loco) { - DCC::setFn(loco,29,false); - DCC::setFn(loco,30,false); - DCC::setFn(loco,31,true); - } - break; - default: - ; // do nothing - break; - } - + DCC::setDCFreq(loco,operand); break; case OPCODE_RESUME: diff --git a/version.h b/version.h index 1a75b01..de4fdcc 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.64" +#define VERSION "5.2.65" +// 5.2.65 - Speedup Exrail SETFREQ // 5.2.64 - Bugfix: <0 PROG> updated to undo JOIN // 5.2.63 - Implement WIFI_LED for ESP32, ESPduino32 and EX-CSB1, that is turned on when STA mode connects or AP mode is up // - Add BOOSTER_INPUT definitions for ESPduino32 and EX-CSB1 to config.example.h From 60e564df517f5c144b15745a87feea5d18c20b88 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 10 Jul 2024 09:57:03 +0100 Subject: [PATCH 75/89] SETFREQ and \n"), sp->loco,slot,sp->speedCode,sp->functions); + broadcastReply(COMMAND_TYPE, F("\n"), sp->loco,slot,sp->speedCode,sp->functions); #ifdef SABERTOOTH if (Serial2 && sp->loco == SABERTOOTH) { static uint8_t rampingmode = 0; diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 902326e..fd13a41 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -642,6 +642,13 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) case 'F': // New command to call the new Loco Function API if(params!=3) break; + + if (p[1]=="DCFREQ"_hk) { // + if (p[2]<0 || p[2]>3) break; + DCC::setDCFreq(p[0],p[2]); + return; + } + if (Diag::CMD) DIAG(F("Setting loco %d F%d %S"), p[0], p[1], p[2] ? F("ON") : F("OFF")); if (DCC::setFn(p[0], p[1], p[2] == 1)) return; diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 8927fbe..309a27c 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -334,7 +334,7 @@ #define SET_TRACK(track,mode) #define SET_POWER(track,onoff) #define SETLOCO(loco) -#define SETFREQ(loco,freq) +#define SETFREQ(freq) #define SIGNAL(redpin,amberpin,greenpin) #define SIGNALH(redpin,amberpin,greenpin) #define SPEED(speed) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index e4d72a1..ebea75e 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -620,7 +620,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define SET_TRACK(track,mode) OPCODE_SET_TRACK,V(TRACK_MODE_##mode <<8 | TRACK_NUMBER_##track), #define SET_POWER(track,onoff) OPCODE_SET_POWER,V(TRACK_POWER_##onoff),OPCODE_PAD, V(TRACK_NUMBER_##track), #define SETLOCO(loco) OPCODE_SETLOCO,V(loco), -#define SETFREQ(loco,freq) OPCODE_SETLOCO,V(loco), OPCODE_SETFREQ,V(freq), +#define SETFREQ(freq) OPCODE_SETFREQ,V(freq), #define SIGNAL(redpin,amberpin,greenpin) #define SIGNALH(redpin,amberpin,greenpin) #define SPEED(speed) OPCODE_SPEED,V(speed), diff --git a/StringFormatter.cpp b/StringFormatter.cpp index 9c69877..192e1cc 100644 --- a/StringFormatter.cpp +++ b/StringFormatter.cpp @@ -139,6 +139,7 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) { case 'd': printPadded(stream,va_arg(args, int), formatWidth, formatLeft); break; case 'u': printPadded(stream,va_arg(args, unsigned int), formatWidth, formatLeft); break; case 'l': printPadded(stream,va_arg(args, long), formatWidth, formatLeft); break; + case 'L': stream->print(va_arg(args, unsigned long), DEC); break; case 'b': stream->print(va_arg(args, int), BIN); break; case 'o': stream->print(va_arg(args, int), OCT); break; case 'x': stream->print((unsigned int)va_arg(args, unsigned int), HEX); break; diff --git a/version.h b/version.h index de4fdcc..7fb4fb5 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.65" +#define VERSION "5.2.66" +// 5.2.66 - +// - EXRAIL SETFREQ drop loco param (breaking since 5.2.28) // 5.2.65 - Speedup Exrail SETFREQ // 5.2.64 - Bugfix: <0 PROG> updated to undo JOIN // 5.2.63 - Implement WIFI_LED for ESP32, ESPduino32 and EX-CSB1, that is turned on when STA mode connects or AP mode is up @@ -12,7 +14,7 @@ // 5.2.62 - Allow acks way longer than standard // 5.2.61 - Merg CBUS ACON/ACOF/ONACON/ONACOF Adapter interface. // - LCC Adapter interface throttled startup, -// (Breaking change woith Adapter base code) +// (Breaking change with Adapter base code) // 5.2.60 - Bugfix: Opcode AFTEROVERLOAD does not have an argument that is a pin and needs to be initialized // - Remove inrush throttle after half good time so that we go to mode overload if problem persists // 5.2.59 - STM32 bugfix correct Serial1 definition for Nucleo-F401RE From 96c4757cc633f807e618f6b66c07ea5c8e815cf8 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 10 Jul 2024 10:58:22 +0100 Subject: [PATCH 76/89] EXTAIL AFTER debounce time --- EXRAIL2.cpp | 8 +++++--- EXRAIL2MacroReset.h | 2 +- EXRAILMacros.h | 2 +- version.h | 5 ++++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 676d31c..1a7f888 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -628,14 +628,16 @@ void RMFT2::loop2() { skipIf=blinkState!=at_timeout; break; - case OPCODE_AFTER: // waits for sensor to hit and then remain off for 0.5 seconds. (must come after an AT operation) + case OPCODE_AFTER: // waits for sensor to hit and then remain off for x mS. + // Note, this must come after an AT operation, which is + // automatically inserted by the AFTER macro. if (readSensor(operand)) { - // reset timer to half a second and keep waiting + // reset timer and keep waiting waitAfter=millis(); delayMe(50); return; } - if (millis()-waitAfter < 500 ) return; + if (millis()-waitAfter < getOperand(1) ) return; break; case OPCODE_AFTEROVERLOAD: // waits for the power to be turned back on - either by power routine or button diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 309a27c..8f2845c 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -195,7 +195,7 @@ #ifndef RMFT2_UNDEF_ONLY #define ACTIVATE(addr,subaddr) #define ACTIVATEL(addr) -#define AFTER(sensor_id) +#define AFTER(sensor_id,timer...) #define AFTEROVERLOAD(track_id) #define ALIAS(name,value...) #define AMBER(signal_id) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index ebea75e..edc1088 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -463,7 +463,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #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 AFTER(sensor_id,timer...) OPCODE_AT,V(sensor_id),OPCODE_AFTER,V(sensor_id),OPCODE_PAD,V(#timer[0]?timer+0:500), #define AFTEROVERLOAD(track_id) OPCODE_AFTEROVERLOAD,V(TRACK_NUMBER_##track_id), #define ALIAS(name,value...) #define AMBER(signal_id) OPCODE_AMBER,V(signal_id), diff --git a/version.h b/version.h index 7fb4fb5..8bd3d5b 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,10 @@ #include "StringFormatter.h" -#define VERSION "5.2.66" +#define VERSION "5.2.67" +// 5.2.67 - EXRAIL AFTER optional debounce time variable (default 500mS) +// - AFTER(42) == AFTER(42,500) sets time sensor must +// - be continuously off. // 5.2.66 - // - EXRAIL SETFREQ drop loco param (breaking since 5.2.28) // 5.2.65 - Speedup Exrail SETFREQ From e6047f66932952a68ec66ee78354ef59d4be6573 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 12 Jul 2024 10:25:11 +0100 Subject: [PATCH 77/89] Revert for F31 --- CommandDistributor.cpp | 2 +- version.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 57ad9f9..e7087ad 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -209,7 +209,7 @@ int16_t CommandDistributor::retClockTime() { void CommandDistributor::broadcastLoco(byte slot) { DCC::LOCO * sp=&DCC::speedTable[slot]; - broadcastReply(COMMAND_TYPE, F("\n"), sp->loco,slot,sp->speedCode,sp->functions); + broadcastReply(COMMAND_TYPE, F("\n"), sp->loco,slot,sp->speedCode,sp->functions); #ifdef SABERTOOTH if (Serial2 && sp->loco == SABERTOOTH) { static uint8_t rampingmode = 0; diff --git a/version.h b/version.h index 8bd3d5b..b428c75 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.67" +#define VERSION "5.2.68" +// 5.2.68 - Revert function map to signed (from 5.2.66) to avoid +// incompatibilities with ED etc for F31 frequency flag. // 5.2.67 - EXRAIL AFTER optional debounce time variable (default 500mS) // - AFTER(42) == AFTER(42,500) sets time sensor must // - be continuously off. From c518dcdc0b4163a217332bf5f71876821f964fc8 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 12 Jul 2024 13:18:26 +0100 Subject: [PATCH 78/89] IO_RocoDriver --- IO_RocoDriver.h | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ version.h | 3 +- 2 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 IO_RocoDriver.h diff --git a/IO_RocoDriver.h b/IO_RocoDriver.h new file mode 100644 index 0000000..08ab73b --- /dev/null +++ b/IO_RocoDriver.h @@ -0,0 +1,157 @@ +/* + * © 2024, Chris Harlow. All rights reserved. + * + * This file is part of EX-CommandStation + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . +*/ + +/* +* The IO_RocoDriver device driver uses a rotary encoder connected to vpins +* to drive a loco. +* Loco id is selected by writeAnalog. +*/ + +#ifndef IO_ROCODRIVER_H +#define IO_ROCODRIVER_H + +#include "DCC.h" +#include "IODevice.h" +#include "DIAG.h" + +const byte _DIR_CW = 0x10; // Clockwise step +const byte _DIR_CCW = 0x20; // Counter-clockwise step + +const byte transition_table[5][4]= { + {0,1,3,0}, // 0: 00 + {1,1,1,2 | _DIR_CW}, // 1: 00->01 + {2,2,0,2}, // 2: 00->01->11 + {3,3,3,4 | _DIR_CCW}, // 3: 00->10 + {4,0,4,4} // 4: 00->10->11 +}; + +const byte _STATE_MASK = 0x07; +const byte _DIR_MASK = 0x30; + + +class RocoDriver : public IODevice { +public: + + static void create(VPIN firstVpin, int dtPin, int clkPin, int clickPin, byte notch=10) { + if (checkNoOverlap(firstVpin)) new RocoDriver(firstVpin, dtPin,clkPin,clickPin,notch); + } + +private: + int _dtPin,_clkPin,_clickPin, _locoid, _notch,_prevpinstate; + enum {xrSTOP,xrFWD,xrREV} _stopState; + byte _rocoState; + + + // Constructor + RocoDriver(VPIN firstVpin, int dtPin, int clkPin, int clickPin, byte notch){ + _firstVpin = firstVpin; + _nPins = 1; + _I2CAddress = 0; + _dtPin=dtPin; + _clkPin=clkPin; + _clickPin=clickPin; + _notch=notch; + _locoid=0; + _stopState=xrSTOP; + _rocoState=0; + _prevpinstate=4; // not 01..11 + IODevice::configureInput(dtPin,true); + IODevice::configureInput(clkPin,true); + IODevice::configureInput(clickPin,true); + addDevice(this); + _display(); + } + + + + void _loop(unsigned long currentMicros) override { + if (_locoid==0) return; // not in use + + // Clicking down on the roco, stops the loco and sets the direction as unknown. + if (IODevice::read(_clickPin)) { + if (_stopState==xrSTOP) return; // debounced multiple stops + DCC::setThrottle(_locoid,1,DCC::getThrottleDirection(_locoid)); + _stopState=xrSTOP; + DIAG(F("DRIVE %d STOP"),_locoid); + return; + } + + // read roco pins and detect state change + byte pinstate = (IODevice::read(_dtPin) << 1) | IODevice::read(_clkPin); + if (pinstate==_prevpinstate) return; + _prevpinstate=pinstate; + + _rocoState = transition_table[_rocoState & _STATE_MASK][pinstate]; + if ((_rocoState & _DIR_MASK) == 0) return; // no value change + + int change=(_rocoState & _DIR_CW)?+1:-1; + // handle roco change -1 or +1 (clockwise) + + if (_stopState==xrSTOP) { + // first move after button press sets the direction. (clockwise=fwd) + _stopState=change>0?xrFWD:xrREV; + } + + // when going fwd, clockwise increases speed. + // but when reversing, anticlockwise increases speed. + // This is similar to a center-zero pot control but with + // the added safety that you cant panic-spin into the other + // direction. + if (_stopState==xrREV) change=-change; + // manage limits + int oldspeed=DCC::getThrottleSpeed(_locoid); + if (oldspeed==1)oldspeed=0; // break out of estop + int newspeed=change>0 ? (min((oldspeed+_notch),126)) : (max(0,(oldspeed-_notch))); + if (newspeed==1) newspeed=0; // normal decelereated stop. + if (oldspeed!=newspeed) { + DIAG(F("DRIVE %d notch %S %d %S"),_locoid, + change>0?F("UP"):F("DOWN"),_notch, + _stopState==xrFWD?F("FWD"):F("REV")); + DCC::setThrottle(_locoid,newspeed,_stopState==xrFWD); + } +} + + // Selocoid as analog value to start drive + // use + void _writeAnalogue(VPIN vpin, int value, uint8_t param1, uint16_t param2) override { + (void) param2; + _locoid=value; + if (param1>0) _notch=param1; + _rocoState=0; + + // If loco is moving, we inherit direction from it. + _stopState=xrSTOP; + if (_locoid>0) { + auto speedbyte=DCC::getThrottleSpeedByte(_locoid); + if ((speedbyte & 0x7f) >1) { + // loco is moving + _stopState= (speedbyte & 0x80)?xrFWD:xrREV; + } + } + _display(); + } + + + void _display() override { + DIAG(F("DRIVE vpin %d loco %d notch %d"),_firstVpin,_locoid,_notch); + } + + }; + +#endif diff --git a/version.h b/version.h index b428c75..084751d 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.68" +#define VERSION "5.2.69" +// 5.2.69 - IO_RocoDriver. Direct drive train with rotary encoder hw. // 5.2.68 - Revert function map to signed (from 5.2.66) to avoid // incompatibilities with ED etc for F31 frequency flag. // 5.2.67 - EXRAIL AFTER optional debounce time variable (default 500mS) From dc2eae499f8de1eecf432530a413147f6f9dbfdd Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 18 Jul 2024 09:39:32 +0100 Subject: [PATCH 79/89] RocoDriver->EncoderThrottle --- IODevice.h | 2 +- IO_RocoDriver.h => IO_EncoderThrottle.cpp | 31 ++++--------- IO_EncoderThrottle.h | 53 +++++++++++++++++++++++ version.h | 4 +- 4 files changed, 66 insertions(+), 24 deletions(-) rename IO_RocoDriver.h => IO_EncoderThrottle.cpp (84%) create mode 100644 IO_EncoderThrottle.h diff --git a/IODevice.h b/IODevice.h index 6c70f5f..d507cd6 100644 --- a/IODevice.h +++ b/IODevice.h @@ -547,6 +547,6 @@ protected: #include "IO_duinoNodes.h" #include "IO_EXIOExpander.h" #include "IO_trainbrains.h" - +#include "IO_EncoderThrottle.h" #endif // iodevice_h diff --git a/IO_RocoDriver.h b/IO_EncoderThrottle.cpp similarity index 84% rename from IO_RocoDriver.h rename to IO_EncoderThrottle.cpp index 08ab73b..76a5f85 100644 --- a/IO_RocoDriver.h +++ b/IO_EncoderThrottle.cpp @@ -18,17 +18,14 @@ */ /* -* The IO_RocoDriver device driver uses a rotary encoder connected to vpins +* The IO_EncoderThrottle device driver uses a rotary encoder connected to vpins * to drive a loco. * Loco id is selected by writeAnalog. */ -#ifndef IO_ROCODRIVER_H -#define IO_ROCODRIVER_H - -#include "DCC.h" #include "IODevice.h" #include "DIAG.h" +#include "DCC.h" const byte _DIR_CW = 0x10; // Clockwise step const byte _DIR_CCW = 0x20; // Counter-clockwise step @@ -45,21 +42,14 @@ const byte _STATE_MASK = 0x07; const byte _DIR_MASK = 0x30; -class RocoDriver : public IODevice { -public: - - static void create(VPIN firstVpin, int dtPin, int clkPin, int clickPin, byte notch=10) { - if (checkNoOverlap(firstVpin)) new RocoDriver(firstVpin, dtPin,clkPin,clickPin,notch); - } -private: - int _dtPin,_clkPin,_clickPin, _locoid, _notch,_prevpinstate; - enum {xrSTOP,xrFWD,xrREV} _stopState; - byte _rocoState; + void EncoderThrottle::create(VPIN firstVpin, int dtPin, int clkPin, int clickPin, byte notch) { + if (checkNoOverlap(firstVpin)) new EncoderThrottle(firstVpin, dtPin,clkPin,clickPin,notch); + } // Constructor - RocoDriver(VPIN firstVpin, int dtPin, int clkPin, int clickPin, byte notch){ + EncoderThrottle::EncoderThrottle(VPIN firstVpin, int dtPin, int clkPin, int clickPin, byte notch){ _firstVpin = firstVpin; _nPins = 1; _I2CAddress = 0; @@ -80,7 +70,7 @@ private: - void _loop(unsigned long currentMicros) override { + void EncoderThrottle::_loop(unsigned long currentMicros) { if (_locoid==0) return; // not in use // Clicking down on the roco, stops the loco and sets the direction as unknown. @@ -129,7 +119,7 @@ private: // Selocoid as analog value to start drive // use - void _writeAnalogue(VPIN vpin, int value, uint8_t param1, uint16_t param2) override { + void EncoderThrottle::_writeAnalogue(VPIN vpin, int value, uint8_t param1, uint16_t param2) { (void) param2; _locoid=value; if (param1>0) _notch=param1; @@ -148,10 +138,7 @@ private: } - void _display() override { + void EncoderThrottle::_display() { DIAG(F("DRIVE vpin %d loco %d notch %d"),_firstVpin,_locoid,_notch); } - }; - -#endif diff --git a/IO_EncoderThrottle.h b/IO_EncoderThrottle.h new file mode 100644 index 0000000..05ce2eb --- /dev/null +++ b/IO_EncoderThrottle.h @@ -0,0 +1,53 @@ +/* + * © 2024, Chris Harlow. All rights reserved. + * + * This file is part of EX-CommandStation + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . +*/ + +/* +* The IO_EncoderThrottle device driver uses a rotary encoder connected to vpins +* to drive a loco. +* Loco id is selected by writeAnalog. +*/ + +#ifndef IO_EncoderThrottle_H +#define IO_EncoderThrottle_H +#include "IODevice.h" + +class EncoderThrottle : public IODevice { +public: + + static void create(VPIN firstVpin, int dtPin, int clkPin, int clickPin, byte notch=10); + +private: + int _dtPin,_clkPin,_clickPin, _locoid, _notch,_prevpinstate; + enum {xrSTOP,xrFWD,xrREV} _stopState; + byte _rocoState; + + // Constructor + EncoderThrottle(VPIN firstVpin, int dtPin, int clkPin, int clickPin, byte notch); + + void _loop(unsigned long currentMicros) override ; + + // Selocoid as analog value to start drive + // use + void _writeAnalogue(VPIN vpin, int value, uint8_t param1, uint16_t param2) override; + + void _display() override ; + + }; + +#endif diff --git a/version.h b/version.h index 084751d..1746bd5 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.69" +#define VERSION "5.2.70" +// 5.2.70 - IO_RocoDriver renamed to IO_EncoderThrottle. +// - and included in IODEvice.h (circular dependency removed) // 5.2.69 - IO_RocoDriver. Direct drive train with rotary encoder hw. // 5.2.68 - Revert function map to signed (from 5.2.66) to avoid // incompatibilities with ED etc for F31 frequency flag. From 27dc8059d78a6666ebb2c844ad64f952416d89bf Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 19 Jul 2024 09:29:43 +0100 Subject: [PATCH 80/89] Broadcast loco forgets. --- CommandDistributor.cpp | 4 ++++ CommandDistributor.h | 1 + DCC.cpp | 6 +++++- version.h | 3 ++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index e7087ad..d2cf3f9 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -248,6 +248,10 @@ void CommandDistributor::broadcastLoco(byte slot) { #endif } +void CommandDistributor::broadcastForgetLoco(int16_t loco) { + broadcastReply(COMMAND_TYPE, F("\n<- %d>\n"), loco,loco); +} + void CommandDistributor::broadcastPower() { char pstr[] = "? x"; for(byte t=0; t=0) { speedTable[reg].loco=0; setThrottle2(cab,1); // ESTOP if this loco still on track + CommandDistributor::broadcastForgetLoco(cab); } } void DCC::forgetAllLocos() { // removes all speed reminders setThrottle2(0,1); // ESTOP all locos still on track - for (int i=0;i Date: Tue, 23 Jul 2024 15:42:35 +0100 Subject: [PATCH 81/89] Squashed commit of the following: commit 3ac2fff70d2339992654a7b0da5784dcc9450fcb Author: Asbelos Date: Tue Jul 23 15:40:36 2024 +0100 Create momentum.md commit a08195332f2387d5a9e4ad32bb4128fe0d4ad406 Author: Asbelos Date: Mon Jul 22 21:57:47 2024 +0100 Cleanup of DCC Class reminders commit 002ec5f176645b3a616a387b0357f54d84f99ede Author: Asbelos Date: Mon Jul 22 12:42:43 2024 +0100 Cleaning access to speedByte commit 854ddb0c6c696c808586eb8febaabb52261266bb Author: Asbelos Date: Sun Jul 21 10:15:07 2024 +0100 Fix momentum algorithm commit 916d3baf63eeab5d49682e748ba15a527cd8afa9 Merge: ab72a75 27dc805 Author: Asbelos Date: Fri Jul 19 10:14:06 2024 +0100 Merge branch 'devel' into devel_momentum commit ab72a75d8fe4ac3859655ede97de2f25973a8864 Author: Asbelos Date: Fri Jul 19 08:33:50 2024 +0100 EXRAIL MOMENTUM commit 8a623aa1cbd6616126983e76ecb2fb0c3595b177 Author: Asbelos Date: Thu Jul 18 20:31:58 2024 +0100 Momentum --- CommandDistributor.cpp | 10 +- CommandDistributor.h | 3 +- DCC.cpp | 289 +++++++++++++++++++++++--------------- DCC.h | 16 ++- DCCEXParser.cpp | 19 +-- EXRAIL2.cpp | 6 +- EXRAIL2.h | 1 + EXRAIL2MacroReset.h | 2 + EXRAIL2Parser.cpp | 2 +- EXRAILMacros.h | 1 + Release_Notes/momentum.md | 20 +++ TrackManager.cpp | 2 +- 12 files changed, 237 insertions(+), 134 deletions(-) create mode 100644 Release_Notes/momentum.md diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index d2cf3f9..4c6fa46 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -207,9 +207,13 @@ int16_t CommandDistributor::retClockTime() { return lastclocktime; } -void CommandDistributor::broadcastLoco(byte slot) { - DCC::LOCO * sp=&DCC::speedTable[slot]; - broadcastReply(COMMAND_TYPE, F("\n"), sp->loco,slot,sp->speedCode,sp->functions); +void CommandDistributor::broadcastLoco(DCC::LOCO* sp) { + if (!sp) { + broadcastReply(COMMAND_TYPE,F("\n")); + return; + } + broadcastReply(COMMAND_TYPE, F("\n"), + sp->loco,sp->targetSpeed,sp->functions); #ifdef SABERTOOTH if (Serial2 && sp->loco == SABERTOOTH) { static uint8_t rampingmode = 0; diff --git a/CommandDistributor.h b/CommandDistributor.h index d86b87f..c2e600d 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -28,6 +28,7 @@ #include "StringBuffer.h" #include "defines.h" #include "EXRAIL2.h" +#include "DCC.h" #if WIFI_ON | ETHERNET_ON // Command Distributor must handle a RingStream of clients @@ -46,7 +47,7 @@ private: #endif public : static void parse(byte clientId,byte* buffer, RingStream * ring); - static void broadcastLoco(byte slot); + static void broadcastLoco(DCC::LOCO * slot); static void broadcastForgetLoco(int16_t loco); static void broadcastSensor(int16_t id, bool value); static void broadcastTurnout(int16_t id, bool isClosed); diff --git a/DCC.cpp b/DCC.cpp index e60cf25..9bbd626 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -60,6 +60,8 @@ const byte FN_GROUP_5=0x10; FSH* DCC::shieldName=NULL; byte DCC::globalSpeedsteps=128; +#define SLOTLOOP for (auto slot=&speedTable[0];slot!=&speedTable[MAX_LOCOS];slot++) + void DCC::begin() { StringFormatter::send(&USB_SERIAL,F("\n"), F(VERSION), F(ARDUINO_TYPE), shieldName, F(GITHUB_SHA)); #ifndef DISABLE_EEPROM @@ -72,13 +74,29 @@ void DCC::begin() { #endif } +int16_t DCC::defaultMomentum=0; void DCC::setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection) { + if (cab==0) { + if (tSpeed==1) estopAll(); // ESTOP broadcast fix + return; + } byte speedCode = (tSpeed & 0x7F) + tDirection * 128; - setThrottle2(cab, speedCode); - TrackManager::setDCSignal(cab,speedCode); // in case this is a dcc track on this addr - // retain speed for loco reminders - updateLocoReminder(cab, speedCode ); + LOCO * slot=lookupSpeedTable(cab); + if (slot->targetSpeed==speedCode) return; + slot->targetSpeed=speedCode; + auto momentum=slot->millis_per_notch; + if (momentum<0) momentum=defaultMomentum; + if (momentum>0 && tSpeed!=1) { // not ESTOP + // we dont throttle speed, we just let the reminders take it to target + slot->momentum_base=millis(); + } + else { // Momentum not involved, throttle now. + slot->speedCode = speedCode; + setThrottle2(cab, speedCode); + TrackManager::setDCSignal(cab,speedCode); // in case this is a dcc track on this addr + } + CommandDistributor::broadcastLoco(slot); } void DCC::setThrottle2( uint16_t cab, byte speedCode) { @@ -139,18 +157,22 @@ void DCC::setFunctionInternal(int cab, byte byte1, byte byte2, byte count) { // returns speed steps 0 to 127 (1 == emergency stop) // or -1 on "loco not found" int8_t DCC::getThrottleSpeed(int cab) { - int reg=lookupSpeedTable(cab); - if (reg<0) return -1; - return speedTable[reg].speedCode & 0x7F; + return getThrottleSpeedByte(cab) & 0x7F; } // returns speed code byte // or 128 (speed 0, dir forward) on "loco not found". +// This is the throttle set speed uint8_t DCC::getThrottleSpeedByte(int cab) { - int reg=lookupSpeedTable(cab); - if (reg<0) - return 128; - return speedTable[reg].speedCode; + LOCO * slot=lookupSpeedTable(cab,false); + return slot?slot->targetSpeed:128; +} +// returns speed code byte for loco. +// This is the most recently send DCC speed packet byte +// or 128 (speed 0, dir forward) on "loco not found". +uint8_t DCC::getLocoSpeedByte(int cab) { + LOCO* slot=lookupSpeedTable(cab,false); + return slot?slot->speedCode:128; } // returns 0 to 7 for frequency @@ -159,12 +181,11 @@ uint8_t DCC::getThrottleFrequency(int cab) { (void)cab; return 0; #else - int reg=lookupSpeedTable(cab); - if (reg<0) - return 0; // use default frequency + LOCO* slot=lookupSpeedTable(cab); + if (!slot) return 0; // use default frequency // shift out first 29 bits so we have the 3 "frequency bits" left - uint8_t res = (uint8_t)(speedTable[reg].functions >>29); - //DIAG(F("Speed table %d functions %l shifted %d"), reg, speedTable[reg].functions, res); + uint8_t res = (uint8_t)(slot->functions >>29); + //DIAG(F("Speed table %d functions %l shifted %d"), reg, slot->functions, res); return res; #endif } @@ -172,9 +193,7 @@ uint8_t DCC::getThrottleFrequency(int cab) { // returns direction on loco // or true/forward on "loco not found" bool DCC::getThrottleDirection(int cab) { - int reg=lookupSpeedTable(cab); - if (reg<0) return true; - return (speedTable[reg].speedCode & 0x80) !=0; + return getThrottleSpeedByte(cab) % 0x80; } // Set function to value on or off @@ -207,22 +226,21 @@ bool DCC::setFn( int cab, int16_t functionNumber, bool on) { if (functionNumber > 31) return true; - int reg = lookupSpeedTable(cab); - if (reg<0) return false; - + LOCO * slot = lookupSpeedTable(cab); + // Take care of functions: // Set state of function - uint32_t previous=speedTable[reg].functions; + uint32_t previous=slot->functions; uint32_t funcmask = (1UL<functions |= funcmask; } else { - speedTable[reg].functions &= ~funcmask; + slot->functions &= ~funcmask; } - if (speedTable[reg].functions != previous) { + if (slot->functions != previous) { if (functionNumber <= 28) - updateGroupflags(speedTable[reg].groupFlags, functionNumber); - CommandDistributor::broadcastLoco(reg); + updateGroupflags(slot->groupFlags, functionNumber); + CommandDistributor::broadcastLoco(slot); } return true; } @@ -230,14 +248,13 @@ bool DCC::setFn( int cab, int16_t functionNumber, bool on) { // Flip function state (used from withrottle protocol) void DCC::changeFn( int cab, int16_t functionNumber) { if (cab<=0 || functionNumber>31) return; - int reg = lookupSpeedTable(cab); - if (reg<0) return; + auto slot=lookupSpeedTable(cab); unsigned long funcmask = (1UL<functions ^= funcmask; if (functionNumber <= 28) { - updateGroupflags(speedTable[reg].groupFlags, functionNumber); + updateGroupflags(slot->groupFlags, functionNumber); } - CommandDistributor::broadcastLoco(reg); + CommandDistributor::broadcastLoco(slot); } // Report function state (used from withrottle protocol) @@ -245,12 +262,10 @@ void DCC::changeFn( int cab, int16_t functionNumber) { int8_t DCC::getFn( int cab, int16_t functionNumber) { if (cab<=0 || functionNumber>31) return -1; // unknown - int reg = lookupSpeedTable(cab); - if (reg<0) - return -1; - + auto slot = lookupSpeedTable(cab); + unsigned long funcmask = (1UL<functions & funcmask)? 1 : 0; } // Set the group flag to say we have touched the particular group. @@ -267,22 +282,22 @@ void DCC::updateGroupflags(byte & flags, int16_t functionNumber) { uint32_t DCC::getFunctionMap(int cab) { if (cab<=0) return 0; // unknown pretend all functions off - int reg = lookupSpeedTable(cab); - return (reg<0)?0:speedTable[reg].functions; + auto slot = lookupSpeedTable(cab,false); + return slot?slot->functions:0; } // saves DC frequency (0..3) in spare functions 29,30,31 void DCC::setDCFreq(int cab,byte freq) { if (cab==0 || freq>3) return; - auto reg=lookupSpeedTable(cab,true); + auto slot=lookupSpeedTable(cab,true); // drop and replace F29,30,31 (top 3 bits) - auto newFunctions=speedTable[reg].functions & 0x1FFFFFFFUL; + auto newFunctions=slot->functions & 0x1FFFFFFFUL; if (freq==1) newFunctions |= (1UL<<29); // F29 else if (freq==2) newFunctions |= (1UL<<30); // F30 else if (freq==3) newFunctions |= (1UL<<31); // F31 - if (newFunctions==speedTable[reg].functions) return; // no change - speedTable[reg].functions=newFunctions; - CommandDistributor::broadcastLoco(reg); + if (newFunctions==slot->functions) return; // no change + slot->functions=newFunctions; + CommandDistributor::broadcastLoco(slot); } void DCC::setAccessory(int address, byte port, bool gate, byte onoff /*= 2*/) { @@ -738,10 +753,9 @@ void DCC::setConsistId(int id,bool reverse,ACK_CALLBACK callback) { void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco setThrottle2(cab,1); // ESTOP this loco if still on track - int reg=lookupSpeedTable(cab, false); - if (reg>=0) { - speedTable[reg].loco=0; - setThrottle2(cab,1); // ESTOP if this loco still on track + auto slot=lookupSpeedTable(cab, false); + if (slot) { + slot->loco=-1; // no longer used but not end of world CommandDistributor::broadcastForgetLoco(cab); } } @@ -749,7 +763,7 @@ void DCC::forgetAllLocos() { // removes all speed reminders setThrottle2(0,1); // ESTOP all locos still on track for (int i=0;i highestUsedReg) reg = 0; // Go to start of table - if (speedTable[reg].loco > 0) { - // have found loco to remind - if (issueReminder(reg)) - lastLocoReminder = reg; - } else - lastLocoReminder = reg; + auto slot = nextLocoReminder; + if (slot >= &speedTable[MAX_LOCOS]) slot=&speedTable[0]; // Go to start of table + if (slot->loco > 0) + if (!issueReminder(slot)) + return; + // a loco=0 is at the end of the list, a loco <0 is deleted + if (slot->loco==0) nextLocoReminder = &speedTable[0]; + else nextLocoReminder=slot+1; } -bool DCC::issueReminder(int reg) { - unsigned long functions=speedTable[reg].functions; - int loco=speedTable[reg].loco; - byte flags=speedTable[reg].groupFlags; +int16_t normalize(byte speed) { + if (speed & 0x80) return speed & 0x7F; + return 0-1-speed; +} +byte dccalize(int16_t speed) { + if (speed>127) return 0xFF; // 127 forward + if (speed<-127) return 0x7F; // 127 reverse + if (speed >=0) return speed | 0x80; + // negative speeds... -1==dcc 0, -2==dcc 1 + return (int16_t)-1 - speed; +} + +bool DCC::issueReminder(LOCO * slot) { + unsigned long functions=slot->functions; + int loco=slot->loco; + byte flags=slot->groupFlags; switch (loopStatus) { - case 0: - // DIAG(F("Reminder %d speed %d"),loco,speedTable[reg].speedCode); - setThrottle2(loco, speedTable[reg].speedCode); - break; + case 0: { + // calculate any momentum change going on + auto sc=slot->speedCode; + if (slot->targetSpeed!=sc) { + // calculate new speed code + auto now=millis(); + int16_t delay=now-slot->momentum_base; + auto millisPerNotch=slot->millis_per_notch; + if (millisPerNotch<0) millisPerNotch=defaultMomentum; + // allow for momentum change to 0 while accelerating/slowing + auto ticks=(millisPerNotch>0)?(delay/millisPerNotch):500; + if (ticks>0) { + auto current=normalize(sc); // -128..+127 + auto target=normalize(slot->targetSpeed); + // DIAG(F("Momentum l=%d ti=%d sc=%d c=%d t=%d"),loco,ticks,sc,current,target); + if (currenttarget) current=target; + } + else { // slow + current-=ticks; + if (currentspeedCode=sc; + TrackManager::setDCSignal(loco,sc); // in case this is a dcc track on this addr + slot->momentum_base=now; + } + } + // DIAG(F("Reminder %d speed %d"),loco,slot->speedCode); + setThrottle2(loco, sc); + } + break; case 1: // remind function group 1 (F0-F4) if (flags & FN_GROUP_1) #ifndef DISABLE_FUNCTION_REMINDERS @@ -844,70 +900,75 @@ byte DCC::cv2(int cv) { return lowByte(cv); } -int DCC::lookupSpeedTable(int locoId, bool autoCreate) { +DCC::LOCO * DCC::lookupSpeedTable(int locoId, bool autoCreate) { // determine speed reg for this loco - int firstEmpty = MAX_LOCOS; - int reg; - for (reg = 0; reg < MAX_LOCOS; reg++) { - if (speedTable[reg].loco == locoId) break; - if (speedTable[reg].loco == 0 && firstEmpty == MAX_LOCOS) firstEmpty = reg; + LOCO * firstEmpty=nullptr; + SLOTLOOP { + if (firstEmpty==nullptr && slot->loco<=0) firstEmpty=slot; + if (slot->loco == locoId) return slot; + if (slot->loco==0) break; } - - // 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")); - return -1; + if (!autoCreate) return nullptr; + if (firstEmpty==nullptr) { + // return last slot if full + DIAG(F("Too many locos, reusing last slot")); + firstEmpty=&speedTable[MAX_LOCOS-1]; } - if (reg==firstEmpty){ - speedTable[reg].loco = locoId; - speedTable[reg].speedCode=128; // default direction forward - speedTable[reg].groupFlags=0; - speedTable[reg].functions=0; - } - if (reg > highestUsedReg) highestUsedReg = reg; - return reg; + // fill first empty slot with new entry + firstEmpty->loco = locoId; + firstEmpty->speedCode=128; // default direction forward + firstEmpty->targetSpeed=128; // default direction forward + firstEmpty->groupFlags=0; + firstEmpty->functions=0; + firstEmpty->millis_per_notch=-1; // use default + return firstEmpty; } -void DCC::updateLocoReminder(int loco, byte speedCode) { - - if (loco==0) { - // broadcast stop/estop but dont change direction - for (int reg = 0; reg <= highestUsedReg; reg++) { - if (speedTable[reg].loco==0) continue; - byte newspeed=(speedTable[reg].speedCode & 0x80) | (speedCode & 0x7f); - if (speedTable[reg].speedCode != newspeed) { - speedTable[reg].speedCode = newspeed; - CommandDistributor::broadcastLoco(reg); - } - } - return; +bool DCC::setMomentum(int locoId,int16_t millis_per_notch) { + if (locoId==0 && millis_per_notch>=0) { + defaultMomentum=millis_per_notch; + return true; } + // millis=-1 is ok and means this loco should use the default. + // We dont copy the default here because it can be changed + // while running and have immediate effect on all locos using -1. + if (locoId<=0 || millis_per_notch<-1) return false; + lookupSpeedTable(locoId,true)->millis_per_notch=millis_per_notch; + return true; +} - // determine speed reg for this loco - int reg=lookupSpeedTable(loco); - if (reg>=0 && speedTable[reg].speedCode!=speedCode) { - speedTable[reg].speedCode = speedCode; - CommandDistributor::broadcastLoco(reg); + +void DCC::estopAll() { + setThrottle2(0,1); // estop all locos + TrackManager::setDCSignal(0,1); + + // remind stop/estop but dont change direction + SLOTLOOP { + if (slot->loco<=0) continue; + byte newspeed=(slot->targetSpeed & 0x80) | 0x01; + slot->speedCode = newspeed; + slot->targetSpeed = newspeed; + CommandDistributor::broadcastLoco(slot); } } + DCC::LOCO DCC::speedTable[MAX_LOCOS]; -int DCC::lastLocoReminder = 0; -int DCC::highestUsedReg = 0; +DCC::LOCO * DCC::nextLocoReminder = &DCC::speedTable[0]; void DCC::displayCabList(Print * stream) { - + StringFormatter::send(stream,F("<*\n")); int used=0; - for (int reg = 0; reg <= highestUsedReg; reg++) { - if (speedTable[reg].loco>0) { + SLOTLOOP { + if (slot->loco==0) break; // no more locos + if (slot->loco>0) { used ++; - StringFormatter::send(stream,F("cab=%d, speed=%d, dir=%c \n"), - speedTable[reg].loco, speedTable[reg].speedCode & 0x7f,(speedTable[reg].speedCode & 0x80) ? 'F':'R'); + StringFormatter::send(stream,F("cab=%d, speed=%d, target=%d momentum=%d\n"), + slot->loco, slot->speedCode, slot->targetSpeed, + slot->millis_per_notch); } } - StringFormatter::send(stream,F("Used=%d, max=%d\n"),used,MAX_LOCOS); - + StringFormatter::send(stream,F("Used=%d, max=%d, momentum=%d *>\n"), + used,MAX_LOCOS, DCC::defaultMomentum); } diff --git a/DCC.h b/DCC.h index 38c1952..faa43f7 100644 --- a/DCC.h +++ b/DCC.h @@ -59,8 +59,10 @@ public: // Public DCC API functions static void setThrottle(uint16_t cab, uint8_t tSpeed, bool tDirection); + static void estopAll(); static int8_t getThrottleSpeed(int cab); static uint8_t getThrottleSpeedByte(int cab); + static uint8_t getLocoSpeedByte(int cab); // may lag throttle static uint8_t getThrottleFrequency(int cab); static bool getThrottleDirection(int cab); static void writeCVByteMain(int cab, int cv, byte bValue); @@ -102,20 +104,24 @@ public: byte speedCode; byte groupFlags; uint32_t functions; + // Momentum management variables + uint32_t momentum_base; // millis() when speed modified under momentum + int16_t millis_per_notch; // 0=no momentum, -1=defaultMomentum + byte targetSpeed; // speed set by throttle }; static LOCO speedTable[MAX_LOCOS]; - static int lookupSpeedTable(int locoId, bool autoCreate=true); + static LOCO * lookupSpeedTable(int locoId, bool autoCreate=true); static byte cv1(byte opcode, int cv); static byte cv2(int cv); + static bool setMomentum(int locoId,int16_t millis_per_notch); private: static byte loopStatus; + static int16_t defaultMomentum; // Millis per speed step static void setThrottle2(uint16_t cab, uint8_t speedCode); - static void updateLocoReminder(int loco, byte speedCode); static void setFunctionInternal(int cab, byte fByte, byte eByte, byte count); - static bool issueReminder(int reg); - static int lastLocoReminder; - static int highestUsedReg; + static bool issueReminder(LOCO * slot); + static LOCO* nextLocoReminder; static FSH *shieldName; static byte globalSpeedsteps; diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index fd13a41..27462fe 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -68,7 +68,8 @@ Once a new OPCODE is decided upon, update this list. K, Reserved for future use - Potentially Railcom l, Loco speedbyte/function map broadcast L, Reserved for LCC interface (implemented in EXRAIL) - m, message to throttles broadcast + m, message to throttles (broadcast output) + m, set momentum M, Write DCC packet n, Reserved for SensorCam N, Reserved for Sensorcam @@ -288,12 +289,9 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) int16_t direction; if (params==1) { // display state - int16_t slot=DCC::lookupSpeedTable(p[0],false); - if (slot>=0) - CommandDistributor::broadcastLoco(slot); - else // send dummy state speed 0 fwd no functions. - StringFormatter::send(stream,F("\n"),p[0]); - return; + if (p[0]<=0) break; + CommandDistributor::broadcastLoco(DCC::lookupSpeedTable(p[0],false)); + return; } if (params == 4) @@ -431,6 +429,11 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) DCC::writeCVBitMain(p[0], p[1], p[2], p[3]); return; #endif + + case 'm': // + if (params!=2) break; + if (DCC::setMomentum(p[0],p[1])) return; + break; case 'M': // WRITE TRANSPARENT DCC PACKET MAIN #ifndef DISABLE_PROG @@ -580,7 +583,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } case '!': // ESTOP ALL - DCC::setThrottle(0,1,1); // this broadcasts speed 1(estop) and sets all reminders to speed 1. + DCC::estopAll(); // this broadcasts speed 1(estop) and sets all reminders to speed 1. return; #ifdef HAS_ENOUGH_MEMORY diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 1a7f888..965d0c4 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -566,6 +566,10 @@ void RMFT2::loop2() { forward=DCC::getThrottleDirection(loco)^invert; driveLoco(operand); break; + + case OPCODE_MOMENTUM: + DCC::setMomentum(loco,operand); + break; case OPCODE_FORGET: if (loco!=0) { @@ -679,7 +683,7 @@ void RMFT2::loop2() { break; case OPCODE_PAUSE: - DCC::setThrottle(0,1,true); // pause all locos on the track + DCC::estopAll(); // pause all locos on the track pausingTask=this; break; diff --git a/EXRAIL2.h b/EXRAIL2.h index 9271bba..b0ed324 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -35,6 +35,7 @@ // enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,OPCODE_TOGGLE_TURNOUT, OPCODE_FWD,OPCODE_REV,OPCODE_SPEED,OPCODE_INVERT_DIRECTION, + OPCODE_MOMENTUM, OPCODE_RESERVE,OPCODE_FREE, OPCODE_AT,OPCODE_AFTER, OPCODE_AFTEROVERLOAD,OPCODE_AUTOSTART, diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 8f2845c..8773787 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -98,6 +98,7 @@ #undef LCC #undef LCCX #undef LCN +#undef MOMENTUM #undef MOVETT #undef ACON #undef ACOF @@ -265,6 +266,7 @@ #define LCC(eventid) #define LCCX(senderid,eventid) #define LCD(row,msg) +#define MOMENTUM(mspertick) #define SCREEN(display,row,msg) #define LCN(msg) #define MESSAGE(msg) diff --git a/EXRAIL2Parser.cpp b/EXRAIL2Parser.cpp index 8c498ab..780c125 100644 --- a/EXRAIL2Parser.cpp +++ b/EXRAIL2Parser.cpp @@ -276,7 +276,7 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { switch (p[0]) { case "PAUSE"_hk: // if (paramCount!=1) return false; - DCC::setThrottle(0,1,true); // pause all locos on the track + DCC::estopAll(); // pause all locos on the track pausingTask=(RMFT2 *)1; // Impossible task address return true; diff --git a/EXRAILMacros.h b/EXRAILMacros.h index edc1088..4f7d41a 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -551,6 +551,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define STEALTH_GLOBAL(code...) #define LCN(msg) PRINT(msg) #define MESSAGE(msg) PRINT(msg) +#define MOMENTUM(mspertick) OPCODE_MOMENTUM,V(mspertick), #define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0), #define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr), #define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3), diff --git a/Release_Notes/momentum.md b/Release_Notes/momentum.md new file mode 100644 index 0000000..ac5f280 --- /dev/null +++ b/Release_Notes/momentum.md @@ -0,0 +1,20 @@ +New Momentum feature notes: + +The command station can apply momentum to throttle movements in the same way that a standards compliant DCC decoder can be set to do. This momentum can be defaulted system wide and overridden on individual locos. It does not use or alter the loco CV values and so it also works when driving DC locos. +The momentum is applied regardless of the throttle type used (or even EXRAIL). + +Momentum is specified in mS / throttle_step. + +There is a new command `` + +For example: +`` sets loco 3 to no momentum. +`` sets loco 3 to 21 mS/step. +`` sets the default momentum to 21mS/Step for all current and future locos that have not been specifically set. +`` sets loco 3 to track the default momentum value. + +EXRAIL + A new macro `MOMENTUM(mSecPerStep)` sets the momentum value of the current tasks loco. + +Note: Setting Momentum 7,14,21 etc is similar in effect to setting a decoder CV03 to 1,2,3. At present the same momentum value is used for acceleration and deceleration. The `` command may be extended in future to separate these values. + diff --git a/TrackManager.cpp b/TrackManager.cpp index 512452d..61cf7c8 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -358,7 +358,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr } void TrackManager::applyDCSpeed(byte t) { - track[t]->setDCSignal(DCC::getThrottleSpeedByte(trackDCAddr[t]), + track[t]->setDCSignal(DCC::getLocoSpeedByte(trackDCAddr[t]), DCC::getThrottleFrequency(trackDCAddr[t])); } From f39fd89fbd466f2008d026d322598d0992b0efaf Mon Sep 17 00:00:00 2001 From: pmantoine Date: Thu, 25 Jul 2024 13:58:04 +0800 Subject: [PATCH 82/89] STM32 bugfix for PORTG and PORTH with thanks to Ash --- MotorDriver.cpp | 3 ++- TrackManager.cpp | 10 ++++++++++ version.h | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 28fbfa3..749c425 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -1,5 +1,6 @@ /* * © 2022-2024 Paul M Antoine + * © 2024 Herb Morton * © 2021 Mike S * © 2021 Fred Decker * © 2020-2023 Harald Barth @@ -98,7 +99,7 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i if (HAVE_PORTH(fastSignalPin.inout == &PORTH)) { DIAG(F("Found PORTH pin %d"),signalPin); fastSignalPin.shadowinout = fastSignalPin.inout; - fastSignalPin.inout = &shadowPORTF; + fastSignalPin.inout = &shadowPORTH; } signalPin2=signal_pin2; diff --git a/TrackManager.cpp b/TrackManager.cpp index 61cf7c8..dcb7c69 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -1,6 +1,8 @@ /* * © 2022 Chris Harlow * © 2022-2024 Harald Barth + * © 2023-2024 Paul M. Antoine + * © 2024 Herb Morton * © 2023 Colin Murdoch * All rights reserved. * @@ -149,6 +151,8 @@ void TrackManager::setDCCSignal( bool on) { HAVE_PORTD(shadowPORTD=PORTD); HAVE_PORTE(shadowPORTE=PORTE); HAVE_PORTF(shadowPORTF=PORTF); + HAVE_PORTF(shadowPORTF=PORTG); + HAVE_PORTF(shadowPORTF=PORTH); APPLY_BY_MODE(TRACK_MODE_MAIN,setSignal(on)); HAVE_PORTA(PORTA=shadowPORTA); HAVE_PORTB(PORTB=shadowPORTB); @@ -156,6 +160,8 @@ void TrackManager::setDCCSignal( bool on) { HAVE_PORTD(PORTD=shadowPORTD); HAVE_PORTE(PORTE=shadowPORTE); HAVE_PORTF(PORTF=shadowPORTF); + HAVE_PORTF(shadowPORTF=PORTG); + HAVE_PORTF(shadowPORTF=PORTH); } // setPROGSignal(), called from interrupt context @@ -167,6 +173,8 @@ void TrackManager::setPROGSignal( bool on) { HAVE_PORTD(shadowPORTD=PORTD); HAVE_PORTE(shadowPORTE=PORTE); HAVE_PORTF(shadowPORTF=PORTF); + HAVE_PORTF(shadowPORTF=PORTG); + HAVE_PORTF(shadowPORTF=PORTH); APPLY_BY_MODE(TRACK_MODE_PROG,setSignal(on)); HAVE_PORTA(PORTA=shadowPORTA); HAVE_PORTB(PORTB=shadowPORTB); @@ -174,6 +182,8 @@ void TrackManager::setPROGSignal( bool on) { HAVE_PORTD(PORTD=shadowPORTD); HAVE_PORTE(PORTE=shadowPORTE); HAVE_PORTF(PORTF=shadowPORTF); + HAVE_PORTF(shadowPORTF=PORTG); + HAVE_PORTF(shadowPORTF=PORTH); } // setDCSignal(), called from normal context diff --git a/version.h b/version.h index 52fbbb9..184dad6 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.71" +#define VERSION "5.2.72" +// 5.2.72 - Bugfix: added shadowPORT entries in TrackManager.cpp for PORTG and PORTH on STM32, fixed typo in MotorDriver.cpp // 5.2.71 - Broadcasts of loco forgets. // 5.2.70 - IO_RocoDriver renamed to IO_EncoderThrottle. // - and included in IODEvice.h (circular dependency removed) From 9c263062e44df9da23f5e170cbdaa2a1871c67c2 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Sun, 4 Aug 2024 18:08:27 +0800 Subject: [PATCH 83/89] STM32 bugfix PORTG and PORTH shadow ports --- TrackManager.cpp | 16 ++++++++-------- version.h | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index dcb7c69..f67c266 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -151,8 +151,8 @@ void TrackManager::setDCCSignal( bool on) { HAVE_PORTD(shadowPORTD=PORTD); HAVE_PORTE(shadowPORTE=PORTE); HAVE_PORTF(shadowPORTF=PORTF); - HAVE_PORTF(shadowPORTF=PORTG); - HAVE_PORTF(shadowPORTF=PORTH); + HAVE_PORTG(shadowPORTF=PORTG); + HAVE_PORTH(shadowPORTF=PORTH); APPLY_BY_MODE(TRACK_MODE_MAIN,setSignal(on)); HAVE_PORTA(PORTA=shadowPORTA); HAVE_PORTB(PORTB=shadowPORTB); @@ -160,8 +160,8 @@ void TrackManager::setDCCSignal( bool on) { HAVE_PORTD(PORTD=shadowPORTD); HAVE_PORTE(PORTE=shadowPORTE); HAVE_PORTF(PORTF=shadowPORTF); - HAVE_PORTF(shadowPORTF=PORTG); - HAVE_PORTF(shadowPORTF=PORTH); + HAVE_PORTG(shadowPORTF=PORTG); + HAVE_PORTH(shadowPORTF=PORTH); } // setPROGSignal(), called from interrupt context @@ -173,8 +173,8 @@ void TrackManager::setPROGSignal( bool on) { HAVE_PORTD(shadowPORTD=PORTD); HAVE_PORTE(shadowPORTE=PORTE); HAVE_PORTF(shadowPORTF=PORTF); - HAVE_PORTF(shadowPORTF=PORTG); - HAVE_PORTF(shadowPORTF=PORTH); + HAVE_PORTG(shadowPORTF=PORTG); + HAVE_PORTH(shadowPORTF=PORTH); APPLY_BY_MODE(TRACK_MODE_PROG,setSignal(on)); HAVE_PORTA(PORTA=shadowPORTA); HAVE_PORTB(PORTB=shadowPORTB); @@ -182,8 +182,8 @@ void TrackManager::setPROGSignal( bool on) { HAVE_PORTD(PORTD=shadowPORTD); HAVE_PORTE(PORTE=shadowPORTE); HAVE_PORTF(PORTF=shadowPORTF); - HAVE_PORTF(shadowPORTF=PORTG); - HAVE_PORTF(shadowPORTF=PORTH); + HAVE_PORTG(shadowPORTF=PORTG); + HAVE_PORTH(shadowPORTF=PORTH); } // setDCSignal(), called from normal context diff --git a/version.h b/version.h index 184dad6..191530e 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.72" +#define VERSION "5.2.73" +// 5.2.73 - Bugfix: STM32 further fixes to shadowPORT entries in TrackManager.cpp for PORTG and PORTH // 5.2.72 - Bugfix: added shadowPORT entries in TrackManager.cpp for PORTG and PORTH on STM32, fixed typo in MotorDriver.cpp // 5.2.71 - Broadcasts of loco forgets. // 5.2.70 - IO_RocoDriver renamed to IO_EncoderThrottle. From c5c5609fc63e91106ca3ef51f6e61b9050001755 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 6 Aug 2024 07:30:01 +0200 Subject: [PATCH 84/89] ESP32: Turn always on the JOINed PROG track when it acts as MAIN --- TrackManager.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index f67c266..5264166 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -641,23 +641,25 @@ void TrackManager::setJoinRelayPin(byte joinRelayPin) { void TrackManager::setJoin(bool joined) { #ifdef ARDUINO_ARCH_ESP32 - if (joined) { + if (joined) { // if we go into joined mode (PROG acts as MAIN) FOR_EACH_TRACK(t) { - if (track[t]->getMode() & TRACK_MODE_PROG) { - tempProgTrack = t; + if (track[t]->getMode() & TRACK_MODE_PROG) { // find PROG track + tempProgTrack = t; // remember PROG track setTrackMode(t, TRACK_MODE_MAIN); - break; + track[t]->setPower(POWERMODE::ON); // if joined, always on + break; // there is only one prog track, done } } } 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 + // setTrackMode defaults to power off, so we + // need to preserve that state. + POWERMODE tPTmode = track[tempProgTrack]->getPower(); // get current power status of this track + setTrackMode(tempProgTrack, TRACK_MODE_PROG); // set track mode back to prog + track[tempProgTrack]->setPower(tPTmode); // set power status as it was before tempProgTrack = MAX_TRACKS+1; + } else { + DIAG(F("Unjoin but no remembered prog track")); } } #endif From 05e77c924e1014562e3eed557e7797eea760ff4c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 8 Aug 2024 10:45:44 +0200 Subject: [PATCH 85/89] Revert momentum additions, squashed commit 4e57a80265c7e4c716574fc7f7a304e122013ada. --- CommandDistributor.cpp | 10 +- CommandDistributor.h | 3 +- DCC.cpp | 289 +++++++++++++++----------------------- DCC.h | 16 +-- DCCEXParser.cpp | 19 ++- EXRAIL2.cpp | 6 +- EXRAIL2.h | 1 - EXRAIL2MacroReset.h | 2 - EXRAIL2Parser.cpp | 2 +- EXRAILMacros.h | 1 - Release_Notes/momentum.md | 20 --- TrackManager.cpp | 2 +- 12 files changed, 134 insertions(+), 237 deletions(-) delete mode 100644 Release_Notes/momentum.md diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 4c6fa46..d2cf3f9 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -207,13 +207,9 @@ int16_t CommandDistributor::retClockTime() { return lastclocktime; } -void CommandDistributor::broadcastLoco(DCC::LOCO* sp) { - if (!sp) { - broadcastReply(COMMAND_TYPE,F("\n")); - return; - } - broadcastReply(COMMAND_TYPE, F("\n"), - sp->loco,sp->targetSpeed,sp->functions); +void CommandDistributor::broadcastLoco(byte slot) { + DCC::LOCO * sp=&DCC::speedTable[slot]; + broadcastReply(COMMAND_TYPE, F("\n"), sp->loco,slot,sp->speedCode,sp->functions); #ifdef SABERTOOTH if (Serial2 && sp->loco == SABERTOOTH) { static uint8_t rampingmode = 0; diff --git a/CommandDistributor.h b/CommandDistributor.h index c2e600d..d86b87f 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -28,7 +28,6 @@ #include "StringBuffer.h" #include "defines.h" #include "EXRAIL2.h" -#include "DCC.h" #if WIFI_ON | ETHERNET_ON // Command Distributor must handle a RingStream of clients @@ -47,7 +46,7 @@ private: #endif public : static void parse(byte clientId,byte* buffer, RingStream * ring); - static void broadcastLoco(DCC::LOCO * slot); + static void broadcastLoco(byte slot); static void broadcastForgetLoco(int16_t loco); static void broadcastSensor(int16_t id, bool value); static void broadcastTurnout(int16_t id, bool isClosed); diff --git a/DCC.cpp b/DCC.cpp index 9bbd626..e60cf25 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -60,8 +60,6 @@ const byte FN_GROUP_5=0x10; FSH* DCC::shieldName=NULL; byte DCC::globalSpeedsteps=128; -#define SLOTLOOP for (auto slot=&speedTable[0];slot!=&speedTable[MAX_LOCOS];slot++) - void DCC::begin() { StringFormatter::send(&USB_SERIAL,F("\n"), F(VERSION), F(ARDUINO_TYPE), shieldName, F(GITHUB_SHA)); #ifndef DISABLE_EEPROM @@ -74,29 +72,13 @@ void DCC::begin() { #endif } -int16_t DCC::defaultMomentum=0; void DCC::setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection) { - if (cab==0) { - if (tSpeed==1) estopAll(); // ESTOP broadcast fix - return; - } byte speedCode = (tSpeed & 0x7F) + tDirection * 128; - LOCO * slot=lookupSpeedTable(cab); - if (slot->targetSpeed==speedCode) return; - slot->targetSpeed=speedCode; - auto momentum=slot->millis_per_notch; - if (momentum<0) momentum=defaultMomentum; - if (momentum>0 && tSpeed!=1) { // not ESTOP - // we dont throttle speed, we just let the reminders take it to target - slot->momentum_base=millis(); - } - else { // Momentum not involved, throttle now. - slot->speedCode = speedCode; - setThrottle2(cab, speedCode); - TrackManager::setDCSignal(cab,speedCode); // in case this is a dcc track on this addr - } - CommandDistributor::broadcastLoco(slot); + setThrottle2(cab, speedCode); + TrackManager::setDCSignal(cab,speedCode); // in case this is a dcc track on this addr + // retain speed for loco reminders + updateLocoReminder(cab, speedCode ); } void DCC::setThrottle2( uint16_t cab, byte speedCode) { @@ -157,22 +139,18 @@ void DCC::setFunctionInternal(int cab, byte byte1, byte byte2, byte count) { // returns speed steps 0 to 127 (1 == emergency stop) // or -1 on "loco not found" int8_t DCC::getThrottleSpeed(int cab) { - return getThrottleSpeedByte(cab) & 0x7F; + int reg=lookupSpeedTable(cab); + if (reg<0) return -1; + return speedTable[reg].speedCode & 0x7F; } // returns speed code byte // or 128 (speed 0, dir forward) on "loco not found". -// This is the throttle set speed uint8_t DCC::getThrottleSpeedByte(int cab) { - LOCO * slot=lookupSpeedTable(cab,false); - return slot?slot->targetSpeed:128; -} -// returns speed code byte for loco. -// This is the most recently send DCC speed packet byte -// or 128 (speed 0, dir forward) on "loco not found". -uint8_t DCC::getLocoSpeedByte(int cab) { - LOCO* slot=lookupSpeedTable(cab,false); - return slot?slot->speedCode:128; + int reg=lookupSpeedTable(cab); + if (reg<0) + return 128; + return speedTable[reg].speedCode; } // returns 0 to 7 for frequency @@ -181,11 +159,12 @@ uint8_t DCC::getThrottleFrequency(int cab) { (void)cab; return 0; #else - LOCO* slot=lookupSpeedTable(cab); - if (!slot) return 0; // use default frequency + int reg=lookupSpeedTable(cab); + if (reg<0) + return 0; // use default frequency // shift out first 29 bits so we have the 3 "frequency bits" left - uint8_t res = (uint8_t)(slot->functions >>29); - //DIAG(F("Speed table %d functions %l shifted %d"), reg, slot->functions, res); + uint8_t res = (uint8_t)(speedTable[reg].functions >>29); + //DIAG(F("Speed table %d functions %l shifted %d"), reg, speedTable[reg].functions, res); return res; #endif } @@ -193,7 +172,9 @@ uint8_t DCC::getThrottleFrequency(int cab) { // returns direction on loco // or true/forward on "loco not found" bool DCC::getThrottleDirection(int cab) { - return getThrottleSpeedByte(cab) % 0x80; + int reg=lookupSpeedTable(cab); + if (reg<0) return true; + return (speedTable[reg].speedCode & 0x80) !=0; } // Set function to value on or off @@ -226,21 +207,22 @@ bool DCC::setFn( int cab, int16_t functionNumber, bool on) { if (functionNumber > 31) return true; - LOCO * slot = lookupSpeedTable(cab); - + int reg = lookupSpeedTable(cab); + if (reg<0) return false; + // Take care of functions: // Set state of function - uint32_t previous=slot->functions; + uint32_t previous=speedTable[reg].functions; uint32_t funcmask = (1UL<functions |= funcmask; + speedTable[reg].functions |= funcmask; } else { - slot->functions &= ~funcmask; + speedTable[reg].functions &= ~funcmask; } - if (slot->functions != previous) { + if (speedTable[reg].functions != previous) { if (functionNumber <= 28) - updateGroupflags(slot->groupFlags, functionNumber); - CommandDistributor::broadcastLoco(slot); + updateGroupflags(speedTable[reg].groupFlags, functionNumber); + CommandDistributor::broadcastLoco(reg); } return true; } @@ -248,13 +230,14 @@ bool DCC::setFn( int cab, int16_t functionNumber, bool on) { // Flip function state (used from withrottle protocol) void DCC::changeFn( int cab, int16_t functionNumber) { if (cab<=0 || functionNumber>31) return; - auto slot=lookupSpeedTable(cab); + int reg = lookupSpeedTable(cab); + if (reg<0) return; unsigned long funcmask = (1UL<functions ^= funcmask; + speedTable[reg].functions ^= funcmask; if (functionNumber <= 28) { - updateGroupflags(slot->groupFlags, functionNumber); + updateGroupflags(speedTable[reg].groupFlags, functionNumber); } - CommandDistributor::broadcastLoco(slot); + CommandDistributor::broadcastLoco(reg); } // Report function state (used from withrottle protocol) @@ -262,10 +245,12 @@ void DCC::changeFn( int cab, int16_t functionNumber) { int8_t DCC::getFn( int cab, int16_t functionNumber) { if (cab<=0 || functionNumber>31) return -1; // unknown - auto slot = lookupSpeedTable(cab); - + int reg = lookupSpeedTable(cab); + if (reg<0) + return -1; + unsigned long funcmask = (1UL<functions & funcmask)? 1 : 0; + return (speedTable[reg].functions & funcmask)? 1 : 0; } // Set the group flag to say we have touched the particular group. @@ -282,22 +267,22 @@ void DCC::updateGroupflags(byte & flags, int16_t functionNumber) { uint32_t DCC::getFunctionMap(int cab) { if (cab<=0) return 0; // unknown pretend all functions off - auto slot = lookupSpeedTable(cab,false); - return slot?slot->functions:0; + int reg = lookupSpeedTable(cab); + return (reg<0)?0:speedTable[reg].functions; } // saves DC frequency (0..3) in spare functions 29,30,31 void DCC::setDCFreq(int cab,byte freq) { if (cab==0 || freq>3) return; - auto slot=lookupSpeedTable(cab,true); + auto reg=lookupSpeedTable(cab,true); // drop and replace F29,30,31 (top 3 bits) - auto newFunctions=slot->functions & 0x1FFFFFFFUL; + auto newFunctions=speedTable[reg].functions & 0x1FFFFFFFUL; if (freq==1) newFunctions |= (1UL<<29); // F29 else if (freq==2) newFunctions |= (1UL<<30); // F30 else if (freq==3) newFunctions |= (1UL<<31); // F31 - if (newFunctions==slot->functions) return; // no change - slot->functions=newFunctions; - CommandDistributor::broadcastLoco(slot); + if (newFunctions==speedTable[reg].functions) return; // no change + speedTable[reg].functions=newFunctions; + CommandDistributor::broadcastLoco(reg); } void DCC::setAccessory(int address, byte port, bool gate, byte onoff /*= 2*/) { @@ -753,9 +738,10 @@ void DCC::setConsistId(int id,bool reverse,ACK_CALLBACK callback) { void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco setThrottle2(cab,1); // ESTOP this loco if still on track - auto slot=lookupSpeedTable(cab, false); - if (slot) { - slot->loco=-1; // no longer used but not end of world + int reg=lookupSpeedTable(cab, false); + if (reg>=0) { + speedTable[reg].loco=0; + setThrottle2(cab,1); // ESTOP if this loco still on track CommandDistributor::broadcastForgetLoco(cab); } } @@ -763,7 +749,7 @@ void DCC::forgetAllLocos() { // removes all speed reminders setThrottle2(0,1); // ESTOP all locos still on track for (int i=0;i= &speedTable[MAX_LOCOS]) slot=&speedTable[0]; // Go to start of table - if (slot->loco > 0) - if (!issueReminder(slot)) - return; - // a loco=0 is at the end of the list, a loco <0 is deleted - if (slot->loco==0) nextLocoReminder = &speedTable[0]; - else nextLocoReminder=slot+1; + int reg = lastLocoReminder+1; + if (reg > highestUsedReg) reg = 0; // Go to start of table + if (speedTable[reg].loco > 0) { + // have found loco to remind + if (issueReminder(reg)) + lastLocoReminder = reg; + } else + lastLocoReminder = reg; } -int16_t normalize(byte speed) { - if (speed & 0x80) return speed & 0x7F; - return 0-1-speed; -} -byte dccalize(int16_t speed) { - if (speed>127) return 0xFF; // 127 forward - if (speed<-127) return 0x7F; // 127 reverse - if (speed >=0) return speed | 0x80; - // negative speeds... -1==dcc 0, -2==dcc 1 - return (int16_t)-1 - speed; -} - -bool DCC::issueReminder(LOCO * slot) { - unsigned long functions=slot->functions; - int loco=slot->loco; - byte flags=slot->groupFlags; +bool DCC::issueReminder(int reg) { + unsigned long functions=speedTable[reg].functions; + int loco=speedTable[reg].loco; + byte flags=speedTable[reg].groupFlags; switch (loopStatus) { - case 0: { - // calculate any momentum change going on - auto sc=slot->speedCode; - if (slot->targetSpeed!=sc) { - // calculate new speed code - auto now=millis(); - int16_t delay=now-slot->momentum_base; - auto millisPerNotch=slot->millis_per_notch; - if (millisPerNotch<0) millisPerNotch=defaultMomentum; - // allow for momentum change to 0 while accelerating/slowing - auto ticks=(millisPerNotch>0)?(delay/millisPerNotch):500; - if (ticks>0) { - auto current=normalize(sc); // -128..+127 - auto target=normalize(slot->targetSpeed); - // DIAG(F("Momentum l=%d ti=%d sc=%d c=%d t=%d"),loco,ticks,sc,current,target); - if (currenttarget) current=target; - } - else { // slow - current-=ticks; - if (currentspeedCode=sc; - TrackManager::setDCSignal(loco,sc); // in case this is a dcc track on this addr - slot->momentum_base=now; - } - } - // DIAG(F("Reminder %d speed %d"),loco,slot->speedCode); - setThrottle2(loco, sc); - } - break; + case 0: + // DIAG(F("Reminder %d speed %d"),loco,speedTable[reg].speedCode); + setThrottle2(loco, speedTable[reg].speedCode); + break; case 1: // remind function group 1 (F0-F4) if (flags & FN_GROUP_1) #ifndef DISABLE_FUNCTION_REMINDERS @@ -900,75 +844,70 @@ byte DCC::cv2(int cv) { return lowByte(cv); } -DCC::LOCO * DCC::lookupSpeedTable(int locoId, bool autoCreate) { +int DCC::lookupSpeedTable(int locoId, bool autoCreate) { // determine speed reg for this loco - LOCO * firstEmpty=nullptr; - SLOTLOOP { - if (firstEmpty==nullptr && slot->loco<=0) firstEmpty=slot; - if (slot->loco == locoId) return slot; - if (slot->loco==0) break; + int firstEmpty = MAX_LOCOS; + int reg; + for (reg = 0; reg < MAX_LOCOS; reg++) { + if (speedTable[reg].loco == locoId) break; + if (speedTable[reg].loco == 0 && firstEmpty == MAX_LOCOS) firstEmpty = reg; } - if (!autoCreate) return nullptr; - if (firstEmpty==nullptr) { - // return last slot if full - DIAG(F("Too many locos, reusing last slot")); - firstEmpty=&speedTable[MAX_LOCOS-1]; + + // 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")); + return -1; } - // fill first empty slot with new entry - firstEmpty->loco = locoId; - firstEmpty->speedCode=128; // default direction forward - firstEmpty->targetSpeed=128; // default direction forward - firstEmpty->groupFlags=0; - firstEmpty->functions=0; - firstEmpty->millis_per_notch=-1; // use default - return firstEmpty; + if (reg==firstEmpty){ + speedTable[reg].loco = locoId; + speedTable[reg].speedCode=128; // default direction forward + speedTable[reg].groupFlags=0; + speedTable[reg].functions=0; + } + if (reg > highestUsedReg) highestUsedReg = reg; + return reg; } -bool DCC::setMomentum(int locoId,int16_t millis_per_notch) { - if (locoId==0 && millis_per_notch>=0) { - defaultMomentum=millis_per_notch; - return true; +void DCC::updateLocoReminder(int loco, byte speedCode) { + + if (loco==0) { + // broadcast stop/estop but dont change direction + for (int reg = 0; reg <= highestUsedReg; reg++) { + if (speedTable[reg].loco==0) continue; + byte newspeed=(speedTable[reg].speedCode & 0x80) | (speedCode & 0x7f); + if (speedTable[reg].speedCode != newspeed) { + speedTable[reg].speedCode = newspeed; + CommandDistributor::broadcastLoco(reg); + } + } + return; } - // millis=-1 is ok and means this loco should use the default. - // We dont copy the default here because it can be changed - // while running and have immediate effect on all locos using -1. - if (locoId<=0 || millis_per_notch<-1) return false; - lookupSpeedTable(locoId,true)->millis_per_notch=millis_per_notch; - return true; -} - -void DCC::estopAll() { - setThrottle2(0,1); // estop all locos - TrackManager::setDCSignal(0,1); - - // remind stop/estop but dont change direction - SLOTLOOP { - if (slot->loco<=0) continue; - byte newspeed=(slot->targetSpeed & 0x80) | 0x01; - slot->speedCode = newspeed; - slot->targetSpeed = newspeed; - CommandDistributor::broadcastLoco(slot); + // determine speed reg for this loco + int reg=lookupSpeedTable(loco); + if (reg>=0 && speedTable[reg].speedCode!=speedCode) { + speedTable[reg].speedCode = speedCode; + CommandDistributor::broadcastLoco(reg); } } - DCC::LOCO DCC::speedTable[MAX_LOCOS]; -DCC::LOCO * DCC::nextLocoReminder = &DCC::speedTable[0]; +int DCC::lastLocoReminder = 0; +int DCC::highestUsedReg = 0; void DCC::displayCabList(Print * stream) { - StringFormatter::send(stream,F("<*\n")); + int used=0; - SLOTLOOP { - if (slot->loco==0) break; // no more locos - if (slot->loco>0) { + for (int reg = 0; reg <= highestUsedReg; reg++) { + if (speedTable[reg].loco>0) { used ++; - StringFormatter::send(stream,F("cab=%d, speed=%d, target=%d momentum=%d\n"), - slot->loco, slot->speedCode, slot->targetSpeed, - slot->millis_per_notch); + StringFormatter::send(stream,F("cab=%d, speed=%d, dir=%c \n"), + speedTable[reg].loco, speedTable[reg].speedCode & 0x7f,(speedTable[reg].speedCode & 0x80) ? 'F':'R'); } } - StringFormatter::send(stream,F("Used=%d, max=%d, momentum=%d *>\n"), - used,MAX_LOCOS, DCC::defaultMomentum); + StringFormatter::send(stream,F("Used=%d, max=%d\n"),used,MAX_LOCOS); + } diff --git a/DCC.h b/DCC.h index faa43f7..38c1952 100644 --- a/DCC.h +++ b/DCC.h @@ -59,10 +59,8 @@ public: // Public DCC API functions static void setThrottle(uint16_t cab, uint8_t tSpeed, bool tDirection); - static void estopAll(); static int8_t getThrottleSpeed(int cab); static uint8_t getThrottleSpeedByte(int cab); - static uint8_t getLocoSpeedByte(int cab); // may lag throttle static uint8_t getThrottleFrequency(int cab); static bool getThrottleDirection(int cab); static void writeCVByteMain(int cab, int cv, byte bValue); @@ -104,24 +102,20 @@ public: byte speedCode; byte groupFlags; uint32_t functions; - // Momentum management variables - uint32_t momentum_base; // millis() when speed modified under momentum - int16_t millis_per_notch; // 0=no momentum, -1=defaultMomentum - byte targetSpeed; // speed set by throttle }; static LOCO speedTable[MAX_LOCOS]; - static LOCO * lookupSpeedTable(int locoId, bool autoCreate=true); + static int lookupSpeedTable(int locoId, bool autoCreate=true); static byte cv1(byte opcode, int cv); static byte cv2(int cv); - static bool setMomentum(int locoId,int16_t millis_per_notch); private: static byte loopStatus; - static int16_t defaultMomentum; // Millis per speed step static void setThrottle2(uint16_t cab, uint8_t speedCode); + static void updateLocoReminder(int loco, byte speedCode); static void setFunctionInternal(int cab, byte fByte, byte eByte, byte count); - static bool issueReminder(LOCO * slot); - static LOCO* nextLocoReminder; + static bool issueReminder(int reg); + static int lastLocoReminder; + static int highestUsedReg; static FSH *shieldName; static byte globalSpeedsteps; diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 27462fe..fd13a41 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -68,8 +68,7 @@ Once a new OPCODE is decided upon, update this list. K, Reserved for future use - Potentially Railcom l, Loco speedbyte/function map broadcast L, Reserved for LCC interface (implemented in EXRAIL) - m, message to throttles (broadcast output) - m, set momentum + m, message to throttles broadcast M, Write DCC packet n, Reserved for SensorCam N, Reserved for Sensorcam @@ -289,9 +288,12 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) int16_t direction; if (params==1) { // display state - if (p[0]<=0) break; - CommandDistributor::broadcastLoco(DCC::lookupSpeedTable(p[0],false)); - return; + int16_t slot=DCC::lookupSpeedTable(p[0],false); + if (slot>=0) + CommandDistributor::broadcastLoco(slot); + else // send dummy state speed 0 fwd no functions. + StringFormatter::send(stream,F("\n"),p[0]); + return; } if (params == 4) @@ -429,11 +431,6 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) DCC::writeCVBitMain(p[0], p[1], p[2], p[3]); return; #endif - - case 'm': // - if (params!=2) break; - if (DCC::setMomentum(p[0],p[1])) return; - break; case 'M': // WRITE TRANSPARENT DCC PACKET MAIN #ifndef DISABLE_PROG @@ -583,7 +580,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } case '!': // ESTOP ALL - DCC::estopAll(); // this broadcasts speed 1(estop) and sets all reminders to speed 1. + DCC::setThrottle(0,1,1); // this broadcasts speed 1(estop) and sets all reminders to speed 1. return; #ifdef HAS_ENOUGH_MEMORY diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 965d0c4..1a7f888 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -566,10 +566,6 @@ void RMFT2::loop2() { forward=DCC::getThrottleDirection(loco)^invert; driveLoco(operand); break; - - case OPCODE_MOMENTUM: - DCC::setMomentum(loco,operand); - break; case OPCODE_FORGET: if (loco!=0) { @@ -683,7 +679,7 @@ void RMFT2::loop2() { break; case OPCODE_PAUSE: - DCC::estopAll(); // pause all locos on the track + DCC::setThrottle(0,1,true); // pause all locos on the track pausingTask=this; break; diff --git a/EXRAIL2.h b/EXRAIL2.h index b0ed324..9271bba 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -35,7 +35,6 @@ // enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,OPCODE_TOGGLE_TURNOUT, OPCODE_FWD,OPCODE_REV,OPCODE_SPEED,OPCODE_INVERT_DIRECTION, - OPCODE_MOMENTUM, OPCODE_RESERVE,OPCODE_FREE, OPCODE_AT,OPCODE_AFTER, OPCODE_AFTEROVERLOAD,OPCODE_AUTOSTART, diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 8773787..8f2845c 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -98,7 +98,6 @@ #undef LCC #undef LCCX #undef LCN -#undef MOMENTUM #undef MOVETT #undef ACON #undef ACOF @@ -266,7 +265,6 @@ #define LCC(eventid) #define LCCX(senderid,eventid) #define LCD(row,msg) -#define MOMENTUM(mspertick) #define SCREEN(display,row,msg) #define LCN(msg) #define MESSAGE(msg) diff --git a/EXRAIL2Parser.cpp b/EXRAIL2Parser.cpp index 780c125..8c498ab 100644 --- a/EXRAIL2Parser.cpp +++ b/EXRAIL2Parser.cpp @@ -276,7 +276,7 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { switch (p[0]) { case "PAUSE"_hk: // if (paramCount!=1) return false; - DCC::estopAll(); // pause all locos on the track + DCC::setThrottle(0,1,true); // pause all locos on the track pausingTask=(RMFT2 *)1; // Impossible task address return true; diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 4f7d41a..edc1088 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -551,7 +551,6 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define STEALTH_GLOBAL(code...) #define LCN(msg) PRINT(msg) #define MESSAGE(msg) PRINT(msg) -#define MOMENTUM(mspertick) OPCODE_MOMENTUM,V(mspertick), #define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0), #define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr), #define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3), diff --git a/Release_Notes/momentum.md b/Release_Notes/momentum.md deleted file mode 100644 index ac5f280..0000000 --- a/Release_Notes/momentum.md +++ /dev/null @@ -1,20 +0,0 @@ -New Momentum feature notes: - -The command station can apply momentum to throttle movements in the same way that a standards compliant DCC decoder can be set to do. This momentum can be defaulted system wide and overridden on individual locos. It does not use or alter the loco CV values and so it also works when driving DC locos. -The momentum is applied regardless of the throttle type used (or even EXRAIL). - -Momentum is specified in mS / throttle_step. - -There is a new command `` - -For example: -`` sets loco 3 to no momentum. -`` sets loco 3 to 21 mS/step. -`` sets the default momentum to 21mS/Step for all current and future locos that have not been specifically set. -`` sets loco 3 to track the default momentum value. - -EXRAIL - A new macro `MOMENTUM(mSecPerStep)` sets the momentum value of the current tasks loco. - -Note: Setting Momentum 7,14,21 etc is similar in effect to setting a decoder CV03 to 1,2,3. At present the same momentum value is used for acceleration and deceleration. The `` command may be extended in future to separate these values. - diff --git a/TrackManager.cpp b/TrackManager.cpp index 5264166..ba9776b 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -368,7 +368,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr } void TrackManager::applyDCSpeed(byte t) { - track[t]->setDCSignal(DCC::getLocoSpeedByte(trackDCAddr[t]), + track[t]->setDCSignal(DCC::getThrottleSpeedByte(trackDCAddr[t]), DCC::getThrottleFrequency(trackDCAddr[t])); } From ed853eef1dc60f9c87e2e019baab2a2e86db903c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 8 Aug 2024 10:49:59 +0200 Subject: [PATCH 86/89] version 5.2.74 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 48e2fdf..c1d7c5e 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202406182019Z" +#define GITHUB_SHA "devel-202408080849Z" diff --git a/version.h b/version.h index 191530e..4d42e30 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.73" +#define VERSION "5.2.74" +// 5.2.74 - Bugfix: ESP32 turn on the joined prog (as main) again after a prog operation // 5.2.73 - Bugfix: STM32 further fixes to shadowPORT entries in TrackManager.cpp for PORTG and PORTH // 5.2.72 - Bugfix: added shadowPORT entries in TrackManager.cpp for PORTG and PORTH on STM32, fixed typo in MotorDriver.cpp // 5.2.71 - Broadcasts of loco forgets. From 9a08f2df6330043445d5045b0f703fbc9030619b Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 29 Aug 2024 13:41:37 +0200 Subject: [PATCH 87/89] ESP32: Make Serial2 possible for commands --- SerialManager.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SerialManager.cpp b/SerialManager.cpp index 88bc7cd..8430089 100644 --- a/SerialManager.cpp +++ b/SerialManager.cpp @@ -68,7 +68,11 @@ void SerialManager::init() { new SerialManager(&Serial3); #endif #ifdef SERIAL2_COMMANDS +#ifdef ARDUINO_ARCH_ESP32 + Serial2.begin(115200, SERIAL_8N1, 16, 17); // GPIO 16 RXD2; GPIO 17 TXD2 on ESP32 +#else // not ESP32 Serial2.begin(115200); +#endif // ESP32 new SerialManager(&Serial2); #endif #ifdef SERIAL1_COMMANDS @@ -88,8 +92,10 @@ void SerialManager::init() { } #endif #ifdef SABERTOOTH +#ifdef ARDUINO_ARCH_ESP32 Serial2.begin(9600, SERIAL_8N1, 16, 17); // GPIO 16 RXD2; GPIO 17 TXD2 on ESP32 #endif +#endif } void SerialManager::broadcast(char * stringBuffer) { From 2397b773d748b19902271e7a57600ea6d6765472 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 29 Aug 2024 13:44:51 +0200 Subject: [PATCH 88/89] Bugfix: Enable CommandDistributor even for serials 4 to 6 --- CommandDistributor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index d2cf3f9..be23577 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -37,7 +37,7 @@ int16_t lastclocktime; int8_t lastclockrate; -#if WIFI_ON || ETHERNET_ON || defined(SERIAL1_COMMANDS) || defined(SERIAL2_COMMANDS) || defined(SERIAL3_COMMANDS) +#if WIFI_ON || ETHERNET_ON || defined(SERIAL1_COMMANDS) || defined(SERIAL2_COMMANDS) || defined(SERIAL3_COMMANDS) || defined(SERIAL4_COMMANDS) || defined(SERIAL5_COMMANDS) || defined(SERIAL6_COMMANDS) // use a buffer to allow broadcast StringBuffer * CommandDistributor::broadcastBufferWriter=new StringBuffer(); template void CommandDistributor::broadcastReply(clientType type, Targs... msg){ From 7395aa4af826e86a28e1dd6cd4356b0762d59317 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 29 Aug 2024 13:46:44 +0200 Subject: [PATCH 89/89] version --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index c1d7c5e..959a003 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202408080849Z" +#define GITHUB_SHA "devel-202408291145Z" diff --git a/version.h b/version.h index 4d42e30..bf08399 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.74" +#define VERSION "5.2.75" +// 5.2.75 - Bugfix: Serial lines 4 to 6 OK // 5.2.74 - Bugfix: ESP32 turn on the joined prog (as main) again after a prog operation // 5.2.73 - Bugfix: STM32 further fixes to shadowPORT entries in TrackManager.cpp for PORTG and PORTH // 5.2.72 - Bugfix: added shadowPORT entries in TrackManager.cpp for PORTG and PORTH on STM32, fixed typo in MotorDriver.cpp