From b17dc5a0dd4e28e87f1173233c7ef9f3b8a2340a Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 23 May 2024 22:39:43 +0200 Subject: [PATCH 01/40] 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 02/40] 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 03/40] 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 04/40] 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 05/40] 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 06/40] 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 07/40] 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 08/40] 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 09/40] 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 10/40] 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 11/40] 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 12/40] 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 13/40] 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 14/40] 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 15/40] 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 16/40] <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 17/40] 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 18/40] 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 19/40] 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 20/40] 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 21/40] 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 22/40] 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 23/40] 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 24/40] 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 25/40] 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 26/40] 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 27/40] 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 28/40] 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 29/40] 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 30/40] 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 31/40] 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 32/40] 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 From c07ac38ab11240fe7d0d8d894d568008599e3b1e Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 4 Sep 2024 09:11:51 +0200 Subject: [PATCH 33/40] EXRAIL: Catch CV read errors in the callback --- EXRAIL2.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 1a7f888..bfa011d 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -478,10 +478,15 @@ bool RMFT2::skipIfBlock() { /* static */ void RMFT2::readLocoCallback(int16_t cv) { + if (cv <= 0) { + DIAG(F("CV read error")); + progtrackLocoId = -1; + return; + } if (cv & LONG_ADDR_MARKER) { // maker bit indicates long addr progtrackLocoId = cv ^ LONG_ADDR_MARKER; // remove marker bit to get real long addr if (progtrackLocoId <= HIGHEST_SHORT_ADDR ) { // out of range for long addr - DIAG(F("Long addr %d <= %d unsupported\n"), progtrackLocoId, HIGHEST_SHORT_ADDR); + DIAG(F("Long addr %d <= %d unsupported"), progtrackLocoId, HIGHEST_SHORT_ADDR); progtrackLocoId = -1; } } else { From 6fa55116702db71b8045d44887767f3308c09521 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 4 Sep 2024 09:13:52 +0200 Subject: [PATCH 34/40] 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 959a003..839dbb0 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202408291145Z" +#define GITHUB_SHA "devel-202409040713Z" diff --git a/version.h b/version.h index bf08399..2220087 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.75" +#define VERSION "5.2.76" +// 5.2.76 - Bugfix: EXRAIL: Catch CV read errors in the callback // 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 From 7ffbd9d0e856f08619db9aa5c7a4cbff100d6758 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 5 Sep 2024 13:01:54 +0200 Subject: [PATCH 35/40] Use port variable --- WifiESP32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WifiESP32.cpp b/WifiESP32.cpp index 83dd2e1..d44672a 100644 --- a/WifiESP32.cpp +++ b/WifiESP32.cpp @@ -265,7 +265,7 @@ bool WifiESP::setup(const char *SSid, if(!MDNS.begin(hostname)) { DIAG(F("Wifi setup failed to start mDNS")); } - if(!MDNS.addService("withrottle", "tcp", 2560)) { + if(!MDNS.addService("withrottle", "tcp", port)) { DIAG(F("Wifi setup failed to add withrottle service to mDNS")); } From b026417efb6e927deee9ac8b492c07865afee9a7 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 6 Sep 2024 09:28:40 +0200 Subject: [PATCH 36/40] EXTRAIL: Propagate a failed loco addr read to EXRAIL so it can be used as IFLOCO(-1) --- EXRAIL2.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index bfa011d..f228f1f 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -926,11 +926,10 @@ void RMFT2::loop2() { delayMe(100); return; // still waiting for callback } - if (progtrackLocoId<0) { - kill(F("No Loco Found"),progtrackLocoId); - return; // still waiting for callback - } + // At failed read will result in loco == -1 + // which is intended so it can be checked + // from within EXRAIL loco=progtrackLocoId; speedo=0; forward=true; From b53384ab51094b1184bc33f8a63e38273d3a3088 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 7 Sep 2024 23:31:02 +0200 Subject: [PATCH 37/40] If anyone ever wants to run a SABERTOOTH motor controller from a Mega2560 --- SerialManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SerialManager.cpp b/SerialManager.cpp index 8430089..fa7ad38 100644 --- a/SerialManager.cpp +++ b/SerialManager.cpp @@ -94,6 +94,8 @@ void SerialManager::init() { #ifdef SABERTOOTH #ifdef ARDUINO_ARCH_ESP32 Serial2.begin(9600, SERIAL_8N1, 16, 17); // GPIO 16 RXD2; GPIO 17 TXD2 on ESP32 +#else + Serial2.begin(9600); #endif #endif } From 2bd9b4680b1356ff09fa26dc1787933372c5d487 Mon Sep 17 00:00:00 2001 From: Ash-4 <81280775+Ash-4@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:35:36 -0500 Subject: [PATCH 38/40] typo fix missing double quote --- IODevice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IODevice.h b/IODevice.h index eb1738a..24fd5c5 100644 --- a/IODevice.h +++ b/IODevice.h @@ -548,6 +548,6 @@ protected: #include "IO_EXIOExpander.h" #include "IO_trainbrains.h #include "IO_EncoderThrottle.h" -#include "IO_EXSensorCAM.h +#include "IO_EXSensorCAM.h" #endif // iodevice_h From e6d10c053260fc28018f788ec175f022dc3a27fd Mon Sep 17 00:00:00 2001 From: Ash-4 <81280775+Ash-4@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:43:37 -0500 Subject: [PATCH 39/40] Update IODevice.h missing double quote --- IODevice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IODevice.h b/IODevice.h index 24fd5c5..cd60bcc 100644 --- a/IODevice.h +++ b/IODevice.h @@ -546,7 +546,7 @@ protected: #include "IO_PCA9555.h" #include "IO_duinoNodes.h" #include "IO_EXIOExpander.h" -#include "IO_trainbrains.h +#include "IO_trainbrains.h" #include "IO_EncoderThrottle.h" #include "IO_EXSensorCAM.h" From eb4d721f8327080ffcb0d371c9f6e752a2160aaa Mon Sep 17 00:00:00 2001 From: Ash-4 <81280775+Ash-4@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:05:25 -0500 Subject: [PATCH 40/40] Update platformio.ini default_env remove uno nano unowifir2 --- platformio.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index b39b136..5cc5008 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,9 +11,9 @@ [platformio] default_envs = mega2560 - uno - unowifiR2 - nano +; uno +; unowifiR2 +; nano samd21-dev-usb samd21-zero-usb ESP32