From cd47782052aaae9ae378aa3d9f32c86bc36c182f Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 6 Feb 2024 20:03:52 +0000 Subject: [PATCH 01/28] reasonable start --- DCCEXParser.cpp | 8 +++++++- DCCTimer.h | 2 ++ DCCTimerAVR.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ DCCWaveform.cpp | 27 ++++++++++++++++++++++---- DCCWaveform.h | 13 ++++++++++++- MotorDriver.cpp | 2 +- TrackManager.cpp | 5 ++--- 7 files changed, 97 insertions(+), 10 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 16f4494..3f61f7b 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1035,7 +1035,13 @@ bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) { DCC::setGlobalSpeedsteps(128); DIAG(F("128 Speedsteps")); return true; - + case "RAILCOM"_hk: + { + bool onOff = (params > 1) && (p[1] == 1 || p[1] == "ON"_hk); // dont care if other stuff or missing... just means off + DIAG(F("Railcom %S") + ,DCCWaveform::setRailcom(onOff)?F("ON"):F("OFF")); + return true; + } #ifndef DISABLE_PROG case "ACK"_hk: // if (params >= 3) { diff --git a/DCCTimer.h b/DCCTimer.h index 3b14fd6..11e1c7f 100644 --- a/DCCTimer.h +++ b/DCCTimer.h @@ -62,6 +62,8 @@ class DCCTimer { static bool isPWMPin(byte pin); static void setPWM(byte pin, bool high); static void clearPWM(); + static void startRailcomTimer(byte brakePin); + static void ackRailcomTimer(); static void DCCEXanalogWriteFrequency(uint8_t pin, uint32_t frequency); static void DCCEXanalogWrite(uint8_t pin, int value); diff --git a/DCCTimerAVR.cpp b/DCCTimerAVR.cpp index 3e6c436..ea601df 100644 --- a/DCCTimerAVR.cpp +++ b/DCCTimerAVR.cpp @@ -39,6 +39,9 @@ INTERRUPT_CALLBACK interruptHandler=0; #define TIMER1_A_PIN 11 #define TIMER1_B_PIN 12 #define TIMER1_C_PIN 13 + #define TIMER2_A_PIN 10 + #define TIMER2_B_PIN 9 + #else #define TIMER1_A_PIN 9 #define TIMER1_B_PIN 10 @@ -55,6 +58,53 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) { interrupts(); } + +void DCCTimer::startRailcomTimer(byte brakePin) { + /* The Railcom timer is started in such a way that it + - First triggers 28uS after the last TIMER1 tick. + This provides an accurate offset (in High Accuracy mode) + for the start of the Railcom cutout. + - Sets the Railcom pin high at first tick, + because its been setup with 100% PWM duty cycle. + + - Cycles at 436uS so the second tick is the + correct distance from the cutout. + + - Waveform code is responsible for altering the PWM + duty cycle to 0% any time between the first and last tick. + (there will be 7 DCC timer1 ticks in which to do this.) + + */ + const int cutoutDuration = 436; // Desired interval in microseconds + + // Set up Timer2 for CTC mode (Clear Timer on Compare Match) + TCCR2A = 0; // Clear Timer2 control register A + TCCR2B = 0; // Clear Timer2 control register B + TCNT2 = 0; // Initialize Timer2 counter value to 0 + // Configure Phase and Frequency Correct PWM mode +//TCCR2A = (1 << COM2B1) | (1 << WGM20) | (1 << WGM21); + + // Set Fast PWM mode with non-inverted output on OC2B (pin 9) + TCCR2A = (1 << WGM21) | (1 << WGM20) | (1 << COM2B1); + // Set Timer 2 prescaler to 32 + TCCR2B = (1 << CS21) | (1 << CS20); // 32 prescaler + + // Set the compare match value for desired interval + OCR2A = (F_CPU / 1000000) * cutoutDuration / 32 - 1; + + // Calculate the compare match value for desired duty cycle + OCR2B = OCR2A+1; // set duty cycle to 100%= OCR2A) + + // Enable Timer2 output on pin 9 (OC2B) + DDRB |= (1 << DDB1); + // TODO Fudge TCNT2 to sync with last tcnt1 tick + 28uS +} + +void DCCTimer::ackRailcomTimer() { + OCR2B= 0x00; // brfake pin pwm duty cycle 0 at next tick +} + + // ISR called by timer interrupt every 58uS ISR(TIMER1_OVF_vect){ interruptHandler(); } diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 93b21a2..707f8f4 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -115,8 +115,19 @@ DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) { bytes_sent = 0; bits_sent = 0; } + +volatile bool DCCWaveform::railcomActive=false; // switched on by user - +bool DCCWaveform::setRailcom(bool on) { + if (on) { + // TODO check possible + railcomActive=true; + } + else { + railcomActive=false; + } + return railcomActive; +} #pragma GCC push_options #pragma GCC optimize ("-O3") @@ -124,16 +135,16 @@ void DCCWaveform::interrupt2() { // calculate the next bit to be sent: // set state WAVE_MID_1 for a 1=bit // or WAVE_HIGH_0 for a 0 bit. - if (remainingPreambles > 0 ) { state=WAVE_MID_1; // switch state to trigger LOW on next interrupt remainingPreambles--; - + // As we get to the end of the preambles, open the reminder window. // This delays any reminder insertion until the last moment so // that the reminder doesn't block a more urgent packet. reminderWindowOpen=transmitRepeats==0 && remainingPreambles<4 && remainingPreambles>1; if (remainingPreambles==1) promotePendingPacket(); + else if (remainingPreambles==10 && isMainTrack && railcomActive) DCCTimer::ackRailcomTimer(); // Update free memory diagnostic as we don't have anything else to do this time. // Allow for checkAck and its called functions using 22 bytes more. else DCCTimer::updateMinimumFreeMemoryISR(22); @@ -157,6 +168,12 @@ void DCCWaveform::interrupt2() { bytes_sent = 0; // preamble for next packet will start... remainingPreambles = requiredPreambles; + + // set the railcom coundown to trigger half way + // through the first preamble bit. + // Note.. we are still sending the last packet bit + // and we then have to allow for the packet end bit + if (isMainTrack && railcomActive) DCCTimer::startRailcomTimer(9); } } } @@ -208,7 +225,9 @@ void DCCWaveform::promotePendingPacket() { // nothing to do, just send idles or resets // Fortunately reset and idle packets are the same length - memcpy( transmitPacket, isMainTrack ? idlePacket : resetPacket, sizeof(idlePacket)); + // TEMPORARY DEBUG FOR RAILCOM + // memcpy( transmitPacket, isMainTrack ? idlePacket : resetPacket, sizeof(idlePacket)); + memcpy( transmitPacket, resetPacket, sizeof(idlePacket)); transmitLength = sizeof(idlePacket); transmitRepeats = 0; if (getResets() < 250) sentResetsSincePacket++; // only place to increment (private!) diff --git a/DCCWaveform.h b/DCCWaveform.h index 1392288..3f6de18 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -40,7 +40,14 @@ const byte MAX_PACKET_SIZE = 5; // NMRA standard extended packets, payload s // The WAVE_STATE enum is deliberately numbered because a change of order would be catastrophic // to the transform array. -enum WAVE_STATE : byte {WAVE_START=0,WAVE_MID_1=1,WAVE_HIGH_0=2,WAVE_MID_0=3,WAVE_LOW_0=4,WAVE_PENDING=5}; +enum WAVE_STATE : byte { + WAVE_START=0, // wave going high at start of bit + WAVE_MID_1=1, // middle of 1 bit + WAVE_HIGH_0=2, // first part of 0 bit high + WAVE_MID_0=3, // middle of 0 bit + WAVE_LOW_0=4, // first part of 0 bit low + WAVE_PENDING=5 // next bit not yet known + }; // NOTE: static functions are used for the overall controller, then // one instance is created for each track. @@ -78,6 +85,8 @@ class DCCWaveform { void schedulePacket(const byte buffer[], byte byteCount, byte repeats); bool isReminderWindowOpen(); void promotePendingPacket(); + static bool setRailcom(bool on); + static bool isRailcom() {return railcomActive;} private: #ifndef ARDUINO_ARCH_ESP32 @@ -103,6 +112,8 @@ class DCCWaveform { byte pendingPacket[MAX_PACKET_SIZE+1]; // +1 for checksum byte pendingLength; byte pendingRepeats; + static volatile bool railcomActive; // switched on by user + #ifdef ARDUINO_ARCH_ESP32 static RMTChannel *rmtMainChannel; static RMTChannel *rmtProgChannel; diff --git a/MotorDriver.cpp b/MotorDriver.cpp index bd25be4..878beea 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -204,7 +204,7 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i } bool MotorDriver::isPWMCapable() { - return (!dualSignal) && DCCTimer::isPWMPin(signalPin); + return (!dualSignal) && DCCTimer::isPWMPin(signalPin); } diff --git a/TrackManager.cpp b/TrackManager.cpp index da96832..328f952 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -157,10 +157,9 @@ void TrackManager::setDCCSignal( bool on) { HAVE_PORTF(PORTF=shadowPORTF); } +// Called by interrupt context void TrackManager::setCutout( bool on) { - (void) on; - // TODO Cutout needs fake ports as well - // TODO APPLY_BY_MODE(TRACK_MODE_MAIN,setCutout(on)); + APPLY_BY_MODE(TRACK_MODE_MAIN,setBrake(on,true)); } // setPROGSignal(), called from interrupt context From 1443ea8df95dcdead26dd9060424ce58ba7f6888 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 7 Feb 2024 19:50:08 +0000 Subject: [PATCH 02/28] Its alive! --- DCCTimerAVR.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/DCCTimerAVR.cpp b/DCCTimerAVR.cpp index ea601df..66b51ba 100644 --- a/DCCTimerAVR.cpp +++ b/DCCTimerAVR.cpp @@ -75,22 +75,22 @@ void DCCTimer::startRailcomTimer(byte brakePin) { (there will be 7 DCC timer1 ticks in which to do this.) */ - const int cutoutDuration = 436; // Desired interval in microseconds + const int cutoutDuration = 430; // Desired interval in microseconds // Set up Timer2 for CTC mode (Clear Timer on Compare Match) TCCR2A = 0; // Clear Timer2 control register A TCCR2B = 0; // Clear Timer2 control register B TCNT2 = 0; // Initialize Timer2 counter value to 0 // Configure Phase and Frequency Correct PWM mode -//TCCR2A = (1 << COM2B1) | (1 << WGM20) | (1 << WGM21); + TCCR2A = (1 << COM2B1); // enable pwm on pin 9 + TCCR2A |= (1 << WGM20); - // Set Fast PWM mode with non-inverted output on OC2B (pin 9) - TCCR2A = (1 << WGM21) | (1 << WGM20) | (1 << COM2B1); + // Set Timer 2 prescaler to 32 TCCR2B = (1 << CS21) | (1 << CS20); // 32 prescaler // Set the compare match value for desired interval - OCR2A = (F_CPU / 1000000) * cutoutDuration / 32 - 1; + OCR2A = (F_CPU / 1000000) * cutoutDuration / 64 - 1; // Calculate the compare match value for desired duty cycle OCR2B = OCR2A+1; // set duty cycle to 100%= OCR2A) @@ -98,10 +98,23 @@ void DCCTimer::startRailcomTimer(byte brakePin) { // Enable Timer2 output on pin 9 (OC2B) DDRB |= (1 << DDB1); // TODO Fudge TCNT2 to sync with last tcnt1 tick + 28uS + + // Previous TIMER1 Tick was at rising end-of-packet bit + // Cutout starts half way through first preamble + // that is 2.5 * 58uS later. + // TCNT1 ticks 8 times / microsecond + // auto microsendsToFirstRailcomTick=(58+58+29)-(TCNT1/8); + // set the railcom timer counter allowing for phase-correct + + // CHris's NOTE: + // I dont kniow quite how this calculation works out but + // it does seems to get a good answer. + + TCNT2=193 + (ICR1 - TCNT1)/8; } void DCCTimer::ackRailcomTimer() { - OCR2B= 0x00; // brfake pin pwm duty cycle 0 at next tick + OCR2B= 0x00; // brake pin pwm duty cycle 0 at next tick } From 7a9e225602247a5f6a433f8b1c88976bcca0d0e0 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 7 Feb 2024 21:24:48 +0000 Subject: [PATCH 03/28] fill in debug and unsupported drivers --- DCCEXParser.cpp | 23 ++++++++++++++++++++--- DCCTimerMEGAAVR.cpp | 8 ++++++++ DCCTimerSAMD.cpp | 8 ++++++++ DCCTimerSTM32.cpp | 8 ++++++++ DCCTimerTEENSY.cpp | 8 ++++++++ DCCWaveform.cpp | 19 +++++++++++++++---- DCCWaveform.h | 3 ++- 7 files changed, 69 insertions(+), 8 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 3f61f7b..5b335d2 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1036,10 +1036,27 @@ bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) { DIAG(F("128 Speedsteps")); return true; case "RAILCOM"_hk: - { - bool onOff = (params > 1) && (p[1] == 1 || p[1] == "ON"_hk); // dont care if other stuff or missing... just means off + { // + if (params<2) return false; + bool on=false; + bool debug=false; + switch (p[1]) { + case "ON"_hk: + case 1: + on=true; + break; + case "DEBUG"_hk: + on=true; + debug=true; + break; + case "OFF"_hk: + case 0: + break; + default: + return false; + } DIAG(F("Railcom %S") - ,DCCWaveform::setRailcom(onOff)?F("ON"):F("OFF")); + ,DCCWaveform::setRailcom(on,debug)?F("ON"):F("OFF")); return true; } #ifndef DISABLE_PROG diff --git a/DCCTimerMEGAAVR.cpp b/DCCTimerMEGAAVR.cpp index 2b2bdab..19eb409 100644 --- a/DCCTimerMEGAAVR.cpp +++ b/DCCTimerMEGAAVR.cpp @@ -80,6 +80,14 @@ extern char *__malloc_heap_start; interruptHandler(); } +void DCCTimer::startRailcomTimer(byte brakePin) { + // TODO: for intended operation see DCCTimerAVR.cpp +} + +void DCCTimer::ackRailcomTimer() { + // TODO: for intended operation see DCCTimerAVR.cpp +} + bool DCCTimer::isPWMPin(byte pin) { (void) pin; return false; // TODO what are the relevant pins? diff --git a/DCCTimerSAMD.cpp b/DCCTimerSAMD.cpp index f878ae5..a0b4da5 100644 --- a/DCCTimerSAMD.cpp +++ b/DCCTimerSAMD.cpp @@ -76,6 +76,14 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) { interrupts(); } +void DCCTimer::startRailcomTimer(byte brakePin) { + // TODO: for intended operation see DCCTimerAVR.cpp +} + +void DCCTimer::ackRailcomTimer() { + // TODO: for intended operation see DCCTimerAVR.cpp +} + // Timer IRQ handlers replace the dummy handlers (in cortex_handlers) // copied from rf24 branch void TCC0_Handler() { diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index f24adc2..eb7e1ca 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -201,6 +201,14 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) { interrupts(); } +void DCCTimer::startRailcomTimer(byte brakePin) { + // TODO: for intended operation see DCCTimerAVR.cpp +} + +void DCCTimer::ackRailcomTimer() { + // TODO: for intended operation see DCCTimerAVR.cpp +} + bool DCCTimer::isPWMPin(byte pin) { //TODO: STM32 whilst this call to digitalPinHasPWM will reveal which pins can do PWM, // there's no support yet for High Accuracy, so for now return false diff --git a/DCCTimerTEENSY.cpp b/DCCTimerTEENSY.cpp index 0619e21..1230180 100644 --- a/DCCTimerTEENSY.cpp +++ b/DCCTimerTEENSY.cpp @@ -39,6 +39,14 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) { myDCCTimer.begin(interruptHandler, DCC_SIGNAL_TIME); } +void DCCTimer::startRailcomTimer(byte brakePin) { + // TODO: for intended operation see DCCTimerAVR.cpp +} + +void DCCTimer::ackRailcomTimer() { + // TODO: for intended operation see DCCTimerAVR.cpp +} + bool DCCTimer::isPWMPin(byte pin) { //Teensy: digitalPinHasPWM, todo (void) pin; diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 707f8f4..2d50929 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -117,14 +117,17 @@ DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) { } volatile bool DCCWaveform::railcomActive=false; // switched on by user +volatile bool DCCWaveform::railcomDebug=false; // switched on by user -bool DCCWaveform::setRailcom(bool on) { +bool DCCWaveform::setRailcom(bool on, bool debug) { if (on) { // TODO check possible railcomActive=true; + railcomDebug=debug; } else { railcomActive=false; + railcomDebug=false; } return railcomActive; } @@ -225,9 +228,11 @@ void DCCWaveform::promotePendingPacket() { // nothing to do, just send idles or resets // Fortunately reset and idle packets are the same length - // TEMPORARY DEBUG FOR RAILCOM - // memcpy( transmitPacket, isMainTrack ? idlePacket : resetPacket, sizeof(idlePacket)); - memcpy( transmitPacket, resetPacket, sizeof(idlePacket)); + // Note: If railcomDebug is on, then we send resets to the main + // track instead of idles. This means that all data will be zeros + // and only the porersets will be ones, making it much + // easier to read on a logic analyser. + memcpy( transmitPacket, (isMainTrack && (!railcomDebug)) ? idlePacket : resetPacket, sizeof(idlePacket)); transmitLength = sizeof(idlePacket); transmitRepeats = 0; if (getResets() < 250) sentResetsSincePacket++; // only place to increment (private!) @@ -316,4 +321,10 @@ bool DCCWaveform::isReminderWindowOpen() { void IRAM_ATTR DCCWaveform::loop() { DCCACK::checkAck(progTrack.getResets()); } + +bool DCCWaveform::setRailcom(bool on, bool debug) { + // TODO... ESP32 railcom waveform + return false; +} + #endif diff --git a/DCCWaveform.h b/DCCWaveform.h index 3f6de18..a3e20da 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -85,7 +85,7 @@ class DCCWaveform { void schedulePacket(const byte buffer[], byte byteCount, byte repeats); bool isReminderWindowOpen(); void promotePendingPacket(); - static bool setRailcom(bool on); + static bool setRailcom(bool on, bool debug); static bool isRailcom() {return railcomActive;} private: @@ -113,6 +113,7 @@ class DCCWaveform { byte pendingLength; byte pendingRepeats; static volatile bool railcomActive; // switched on by user + static volatile bool railcomDebug; // switched on by user #ifdef ARDUINO_ARCH_ESP32 static RMTChannel *rmtMainChannel; From 25cb878060bd50f829a2788338355984e369ef54 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 7 Feb 2024 21:33:06 +0000 Subject: [PATCH 04/28] remove dross --- TrackManager.cpp | 5 ----- TrackManager.h | 1 - version.h | 2 +- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index 328f952..ca309ed 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -157,11 +157,6 @@ void TrackManager::setDCCSignal( bool on) { HAVE_PORTF(PORTF=shadowPORTF); } -// Called by interrupt context -void TrackManager::setCutout( bool on) { - APPLY_BY_MODE(TRACK_MODE_MAIN,setBrake(on,true)); -} - // setPROGSignal(), called from interrupt context // does assume ports are shadowed if they can be void TrackManager::setPROGSignal( bool on) { diff --git a/TrackManager.h b/TrackManager.h index 6310030..c1f314a 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -57,7 +57,6 @@ class TrackManager { ); static void setDCCSignal( bool on); - static void setCutout( bool on); static void setPROGSignal( bool on); static void setDCSignal(int16_t cab, byte speedbyte); static MotorDriver * getProgDriver(); diff --git a/version.h b/version.h index e94303f..24bddc6 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,7 @@ #include "StringFormatter.h" -#define VERSION "5.2.28" +#define VERSION "5.2.28-Railcom" // 5.2.28 - ESP32: Can all Wifi channels. // - ESP32: Only write Wifi password to display if it is a well known one // 5.2.27 - Bugfix: IOExpander memory allocation From eacf48380b25a0c704bd05f6d2bf9b56ecd93c9e Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 9 Feb 2024 10:15:23 +0000 Subject: [PATCH 05/28] typos in version comments --- version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.h b/version.h index 0252f8a..3b02cf1 100644 --- a/version.h +++ b/version.h @@ -4,8 +4,8 @@ #include "StringFormatter.h" #define VERSION "5.2.31" -// 5.2.31 - Exrail JMRI_SENSORS(vpin [,count]) creates types. -// 5.2.40 - Bugfix: WiThrottle sendIntro after initial N message as well +// 5.2.31 - Exrail JMRI_SENSOR(vpin [,count]) creates types. +// 5.2.30 - Bugfix: WiThrottle sendIntro after initial N message as well // 5.2.29 - Added IO_I2CDFPlayer.h to support DFPLayer over I2C connected to NXP SC16IS750/SC16IS752 (currently only single UART for SC16IS752) // - Added enhanced IO_I2CDFPLayer enum commands to EXRAIL2.h // - Added PLAYSOUND alias of ANOUT to EXRAILMacros.h From 4b97d63cf3ae4320b73696eae0d11f4a41dc3454 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 9 Feb 2024 10:37:51 +0000 Subject: [PATCH 06/28] Remove compiler warnings --- DCCTimerAVR.cpp | 1 + DCCTimerMEGAAVR.cpp | 1 + DCCTimerSAMD.cpp | 1 + DCCTimerSTM32.cpp | 1 + DCCTimerTEENSY.cpp | 1 + 5 files changed, 5 insertions(+) diff --git a/DCCTimerAVR.cpp b/DCCTimerAVR.cpp index 66b51ba..5310704 100644 --- a/DCCTimerAVR.cpp +++ b/DCCTimerAVR.cpp @@ -75,6 +75,7 @@ void DCCTimer::startRailcomTimer(byte brakePin) { (there will be 7 DCC timer1 ticks in which to do this.) */ + (void) brakePin; // Ignored... works on pin 9 only const int cutoutDuration = 430; // Desired interval in microseconds // Set up Timer2 for CTC mode (Clear Timer on Compare Match) diff --git a/DCCTimerMEGAAVR.cpp b/DCCTimerMEGAAVR.cpp index 19eb409..be9337b 100644 --- a/DCCTimerMEGAAVR.cpp +++ b/DCCTimerMEGAAVR.cpp @@ -82,6 +82,7 @@ extern char *__malloc_heap_start; void DCCTimer::startRailcomTimer(byte brakePin) { // TODO: for intended operation see DCCTimerAVR.cpp + (void) brakePin; } void DCCTimer::ackRailcomTimer() { diff --git a/DCCTimerSAMD.cpp b/DCCTimerSAMD.cpp index a0b4da5..d281371 100644 --- a/DCCTimerSAMD.cpp +++ b/DCCTimerSAMD.cpp @@ -78,6 +78,7 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) { void DCCTimer::startRailcomTimer(byte brakePin) { // TODO: for intended operation see DCCTimerAVR.cpp + (void) brakePin; } void DCCTimer::ackRailcomTimer() { diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index eb7e1ca..e22758f 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -203,6 +203,7 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) { void DCCTimer::startRailcomTimer(byte brakePin) { // TODO: for intended operation see DCCTimerAVR.cpp + (void) brakePin; } void DCCTimer::ackRailcomTimer() { diff --git a/DCCTimerTEENSY.cpp b/DCCTimerTEENSY.cpp index 1230180..d7bb29b 100644 --- a/DCCTimerTEENSY.cpp +++ b/DCCTimerTEENSY.cpp @@ -41,6 +41,7 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) { void DCCTimer::startRailcomTimer(byte brakePin) { // TODO: for intended operation see DCCTimerAVR.cpp + (void) brakePin; } void DCCTimer::ackRailcomTimer() { From c780b968564eae454d2001d51b0988f2245b0e02 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 9 Feb 2024 11:54:53 +0000 Subject: [PATCH 07/28] Exrail CONFIGURE_SERVO --- EXRAIL2MacroReset.h | 4 +++- EXRAILMacros.h | 3 +++ version.h | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index f52b636..309c325 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -42,6 +42,7 @@ #undef CLEAR_STASH #undef CLEAR_ALL_STASH #undef CLOSE +#undef CONFIGURE_SERVO #undef DCC_SIGNAL #undef DCC_TURNTABLE #undef DEACTIVATE @@ -194,7 +195,8 @@ #define CALL(route) #define CLEAR_STASH(id) #define CLEAR_ALL_STASH(id) -#define CLOSE(id) +#define CLOSE(id) +#define CONFIGURE_SERVO(vpin,pos1,pos2,profile) #define DCC_SIGNAL(id,add,subaddr) #define DCC_TURNTABLE(id,home,description) #define DEACTIVATE(addr,subaddr) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 5873e38..3b579a3 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -151,6 +151,8 @@ static_assert(!hasdup(compileTimeSequenceList[0],1),"Duplicate SEQUENCE/ROUTE/AU #define HAL_IGNORE_DEFAULTS ignore_defaults=true; #undef JMRI_SENSOR #define JMRI_SENSOR(vpin,count...) Sensor::createMultiple(vpin,##count); +#undef CONFIGURE_SERVO +#define CONFIGURE_SERVO(vpin,pos1,pos2,profile) IODevice::configureServo(vpin,pos1,pos2,PCA9685::profile); bool exrailHalSetup() { bool ignore_defaults=false; #include "myAutomation.h" @@ -441,6 +443,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define CLEAR_STASH(id) OPCODE_CLEAR_STASH,V(id), #define CLEAR_ALL_STASH OPCODE_CLEAR_ALL_STASH,V(0), #define CLOSE(id) OPCODE_CLOSE,V(id), +#define CONFIGURE_SERVO(vpin,pos1,pos2,profile) #ifndef IO_NO_HAL #define DCC_TURNTABLE(id,home,description...) OPCODE_DCCTURNTABLE,V(id),OPCODE_PAD,V(home), #endif diff --git a/version.h b/version.h index c24efed..84eeedf 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.32" +#define VERSION "5.2.33" +// 5.2.33 - Exrail CONFIGURE_SERVO(vpin,pos1,pos2,profile) // 5.2.32 - Railcom Cutout (Initial trial Mega2560 only) // 5.2.31 - Exrail JMRI_SENSOR(vpin [,count]) creates types. // 5.2.30 - Bugfix: WiThrottle sendIntro after initial N message as well From 784088b0df9363a7f0c9a956f8f19c0963d0030b Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 11 Feb 2024 23:19:51 +0100 Subject: [PATCH 08/28] get it into nano and uno again --- DCCEXParser.cpp | 2 ++ platformio.ini | 6 ++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 5b335d2..6deb34f 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1035,6 +1035,7 @@ bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) { DCC::setGlobalSpeedsteps(128); DIAG(F("128 Speedsteps")); return true; +#if defined(HAS_ENOUGH_MEMORY) && !defined(ARDUINO_ARCH_UNO) case "RAILCOM"_hk: { // if (params<2) return false; @@ -1059,6 +1060,7 @@ bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) { ,DCCWaveform::setRailcom(on,debug)?F("ON"):F("OFF")); return true; } +#endif #ifndef DISABLE_PROG case "ACK"_hk: // if (params >= 3) { diff --git a/platformio.ini b/platformio.ini index 2630e6d..a03ff61 100644 --- a/platformio.ini +++ b/platformio.ini @@ -148,10 +148,7 @@ build_flags = platform = atmelavr board = uno framework = arduino -lib_deps = - ${env.lib_deps} - arduino-libraries/Ethernet - SPI +lib_deps = ${env.lib_deps} monitor_speed = 115200 monitor_echo = yes build_flags = -mcall-prologues @@ -164,6 +161,7 @@ framework = arduino lib_deps = ${env.lib_deps} monitor_speed = 115200 monitor_echo = yes +build_flags = -mcall-prologues [env:ESP32] platform = espressif32 From 59b0e8383d0509f784e9ba1108cd11142fa04b68 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 15 Feb 2024 19:51:52 +0000 Subject: [PATCH 09/28] --- DCC.cpp | 16 ++++++++++++++++ DCC.h | 1 + DCCEXParser.cpp | 9 +++++++++ 3 files changed, 26 insertions(+) diff --git a/DCC.cpp b/DCC.cpp index 0c5148a..ed2a803 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -278,6 +278,22 @@ void DCC::setAccessory(int address, byte port, bool gate, byte onoff /*= 2*/) { } } +void DCC::setExtendedAccessory(int16_t address, int16_t value, byte repeats) { + + // format is 10AAAAAA, 0AAA0AA1, 000XXXXX + if (address != (address & 0x7F)) return; + if (value != (value & 0x1F)) return; + + byte b[3]; + b[0]= 0x80 | ((address & 0x7FF)>>5); + b[1]= 0x01 | ((address & 0x1c)<<2) | (address & 0x03)<<1; + b[2]=value & 0x1F; + DCCWaveform::mainTrack.schedulePacket(b, sizeof(b), repeats); // Repeat on packet three times +#if defined(EXRAIL_ACTIVE) + // TODO RMFT2::activateExtendedEvent(address,value); +#endif +} + // // writeCVByteMain: Write a byte with PoM on main. This writes // the 5 byte sized packet to implement this DCC function diff --git a/DCC.h b/DCC.h index 3bf0cf5..e1aeae6 100644 --- a/DCC.h +++ b/DCC.h @@ -71,6 +71,7 @@ public: static uint32_t getFunctionMap(int cab); static void updateGroupflags(byte &flags, int16_t functionNumber); static void setAccessory(int address, byte port, bool gate, byte onoff = 2); + static void setExtendedAccessory(int16_t address, int16_t value, byte repeats=3); static bool writeTextPacket(byte *b, int nBytes); // ACKable progtrack calls bitresults callback 0,0 or -1, cv returns value or -1 diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 6deb34f..a3aa1c4 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -384,6 +384,15 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) #endif } return; + + case 'A': // EXTENDED ACCESSORY + { + if (params!=2) break; + if (p[0] != (p[0] & 0x7F)) break; + if (p[1] != (p[1] & 0x1F)) break; + DCC::setExtendedAccessory(p[0],p[1],3); + } + return; case 'T': // TURNOUT if (parseT(stream, params, p)) From e4904e40802ca3f1a8e9a6b82d2f06cddd4f81e2 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 15 Feb 2024 20:05:27 +0000 Subject: [PATCH 10/28] Exrail ASPECT(addr,value) --- EXRAIL2.cpp | 5 +++++ EXRAIL2.h | 2 +- EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 4 ++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 8a2eadf..56aeeb2 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -800,6 +800,11 @@ void RMFT2::loop2() { DCC::setAccessory(addr,subaddr,active); break; } + case OPCODE_ASPECT: { + // operand is address<<5 | value + DCC::setExtendedAccessory(operand>>5, operand & 0x1F); + break; + } case OPCODE_FOLLOW: progCounter=routeLookup->find(operand); diff --git a/EXRAIL2.h b/EXRAIL2.h index ccfbbe6..e8235c6 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -54,7 +54,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_START,OPCODE_SETLOCO,OPCODE_SENDLOCO,OPCODE_FORGET, OPCODE_PAUSE, OPCODE_RESUME,OPCODE_POWEROFF,OPCODE_POWERON, OPCODE_ONCLOSE, OPCODE_ONTHROW, OPCODE_SERVOTURNOUT, OPCODE_PINTURNOUT, - OPCODE_PRINT,OPCODE_DCCACTIVATE, + OPCODE_PRINT,OPCODE_DCCACTIVATE,OPCODE_ASPECT, OPCODE_ONACTIVATE,OPCODE_ONDEACTIVATE, OPCODE_ROSTER,OPCODE_KILLALL, OPCODE_ROUTE,OPCODE_AUTOMATION,OPCODE_SEQUENCE, diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 309c325..633f4d5 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -31,6 +31,7 @@ #undef ALIAS #undef AMBER #undef ANOUT +#undef ASPECT #undef AT #undef ATGTE #undef ATLT @@ -186,6 +187,7 @@ #define AMBER(signal_id) #define ANOUT(vpin,value,param1,param2) #define AT(sensor_id) +#define ASPECT(address,value) #define ATGTE(sensor_id,value) #define ATLT(sensor_id,value) #define ATTIMEOUT(sensor_id,timeout_ms) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 3b579a3..8d870ef 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -117,6 +117,9 @@ static_assert(!hasdup(compileTimeSequenceList[0],1),"Duplicate SEQUENCE/ROUTE/AU // - check range on LATCH/UNLATCH // This pass generates no runtime data or code #include "EXRAIL2MacroReset.h" +#undef ASPECT +#define ASPECT(address,value) static_assert((address & 0x7ff)== address, "invalid Address"); \ + static_assert((value & 0x1F)== value, "Invalid value"); #undef CALL #define CALL(id) static_assert(hasseq(id),"Sequence not found"); #undef FOLLOW @@ -432,6 +435,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define ALIAS(name,value...) #define AMBER(signal_id) OPCODE_AMBER,V(signal_id), #define ANOUT(vpin,value,param1,param2) OPCODE_SERVO,V(vpin),OPCODE_PAD,V(value),OPCODE_PAD,V(param1),OPCODE_PAD,V(param2), +#define ASPECT(address,value) OPCODE_ASPECT,V((address<<5) | (value & 0x1F)), #define AT(sensor_id) OPCODE_AT,V(sensor_id), #define ATGTE(sensor_id,value) OPCODE_ATGTE,V(sensor_id),OPCODE_PAD,V(value), #define ATLT(sensor_id,value) OPCODE_ATLT,V(sensor_id),OPCODE_PAD,V(value), From 8705c8c33f0255b46a2925cbe9453d87a90c2c79 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 16 Feb 2024 11:49:02 +0000 Subject: [PATCH 11/28] DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect) --- EXRAIL2.cpp | 12 +++++++++++- EXRAIL2.h | 1 + EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 5 +++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 56aeeb2..6f0420c 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1066,7 +1066,7 @@ int16_t RMFT2::getSignalSlot(int16_t id) { if (diag) DIAG(F(" doSignal %d %x"),id,rag); // Schedule any event handler for this signal change. - // Thjis will work even without a signal definition. + // This will work even without a signal definition. if (rag==SIGNAL_RED) onRedLookup->handleEvent(F("RED"),id); else if (rag==SIGNAL_GREEN) onGreenLookup->handleEvent(F("GREEN"),id); else onAmberLookup->handleEvent(F("AMBER"),id); @@ -1103,6 +1103,16 @@ int16_t RMFT2::getSignalSlot(int16_t id) { return; } + if (sigtype== DCCX_SIGNAL_FLAG) { + // redpin,amberpin,greenpin are the 3 aspects + byte value=redpin; + if (rag==SIGNAL_AMBER) value=amberpin; + if (rag==SIGNAL_GREEN) value=greenpin; + DCC::setExtendedAccessory(sigid & SIGNAL_ID_MASK,value); + return; + } + + // LED or similar 3 pin signal, (all pins zero would be a virtual signal) // If amberpin is zero, synthesise amber from red+green const byte SIMAMBER=0x00; diff --git a/EXRAIL2.h b/EXRAIL2.h index e8235c6..eec2a06 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -158,6 +158,7 @@ class LookList { static const int16_t SERVO_SIGNAL_FLAG=0x4000; static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000; static const int16_t DCC_SIGNAL_FLAG=0x1000; + static const int16_t DCCX_SIGNAL_FLAG=0x3000; static const int16_t SIGNAL_ID_MASK=0x0FFF; // Throttle Info Access functions built by exrail macros static const byte rosterNameCount; diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 633f4d5..723bef2 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -45,6 +45,7 @@ #undef CLOSE #undef CONFIGURE_SERVO #undef DCC_SIGNAL +#undef DCCX_SIGNAL #undef DCC_TURNTABLE #undef DEACTIVATE #undef DEACTIVATEL @@ -200,6 +201,7 @@ #define CLOSE(id) #define CONFIGURE_SERVO(vpin,pos1,pos2,profile) #define DCC_SIGNAL(id,add,subaddr) +#define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect) #define DCC_TURNTABLE(id,home,description) #define DEACTIVATE(addr,subaddr) #define DEACTIVATEL(addr) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 8d870ef..698451e 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -172,6 +172,8 @@ bool exrailHalSetup() { #define SERVO_SIGNAL(vpin,redval,amberval,greenval) | FEATURE_SIGNAL #undef DCC_SIGNAL #define DCC_SIGNAL(id,addr,subaddr) | FEATURE_SIGNAL +#undef DCCX_SIGNAL +#define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect) | FEATURE_SIGNAL #undef VIRTUAL_SIGNAL #define VIRTUAL_SIGNAL(id) | FEATURE_SIGNAL @@ -401,6 +403,8 @@ const FSH * RMFT2::getRosterFunctions(int16_t id) { #define SERVO_SIGNAL(vpin,redval,amberval,greenval) vpin | RMFT2::SERVO_SIGNAL_FLAG,redval,amberval,greenval, #undef DCC_SIGNAL #define DCC_SIGNAL(id,addr,subaddr) id | RMFT2::DCC_SIGNAL_FLAG,addr,subaddr,0, +#undef DCCX_SIGNAL +#define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect) id | RMFT2::DCCX_SIGNAL_FLAG,redAspect,amberAspect,greenAspect, #undef VIRTUAL_SIGNAL #define VIRTUAL_SIGNAL(id) id,0,0,0, @@ -457,6 +461,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define DELAYMINS(mindelay) OPCODE_DELAYMINS,V(mindelay), #define DELAYRANDOM(mindelay,maxdelay) DELAY(mindelay) OPCODE_RANDWAIT,V((maxdelay-mindelay)/100L), #define DCC_SIGNAL(id,add,subaddr) +#define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect) #define DONE OPCODE_ENDTASK,0,0, #define DRIVE(analogpin) OPCODE_DRIVE,V(analogpin), #define ELSE OPCODE_ELSE,0,0, From 5742b71ec608e79feaedba1b14322187d960474c Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 16 Feb 2024 12:20:58 +0000 Subject: [PATCH 12/28] to EXRAIL DCCX_SIGNAL intercept --- DCCEXParser.cpp | 6 ++++++ EXRAIL2.cpp | 32 ++++++++++++++++++++++++++++++++ EXRAIL2.h | 3 ++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index a3aa1c4..bcd258a 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -390,6 +390,12 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) if (params!=2) break; if (p[0] != (p[0] & 0x7F)) break; if (p[1] != (p[1] & 0x1F)) break; +#ifdef EXRAIL_ACTIVE + // Ask exrail if this is just changing the aspect on a + // predefined DCCX_SIGNAL. Because this will handle all + // the IFRED and ONRED type issues at the same time. + if (RMFT2::signalAspectEvent(p[0],p[1])) return; +#endif DCC::setExtendedAccessory(p[0],p[1],3); } return; diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 6f0420c..928bc79 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1146,6 +1146,38 @@ int16_t RMFT2::getSignalSlot(int16_t id) { return (flags[sigslot] & SIGNAL_MASK) == rag; } + +// signalAspectEvent returns true if the aspect is destined +// for a defined DCCX_SIGNAL which will handle all the RAG flags +// and ON* handlers. +// Otherwise false so the parser should send the command directly +bool RMFT2::signalAspectEvent(int16_t address, byte aspect ) { + if (!(compileFeatures & FEATURE_SIGNAL)) return false; + int16_t sigslot=getSignalSlot(address); + if (sigslot<0) return false; // this is not a defined signal + int16_t sigpos=sigslot*8; + VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos); + VPIN sigtype=sigid & ~SIGNAL_ID_MASK; + if (sigtype!=DCCX_SIGNAL_FLAG) return false; // not a DCCX signal + // Turn an aspect change into a RED/AMBER/GREEN setting + if (aspect==GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+2)) { + doSignal(sigid,SIGNAL_RED); + return true; + } + + if (aspect==GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+4)) { + doSignal(sigid,SIGNAL_AMBER); + return true; + } + + if (aspect==GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+6)) { + doSignal(sigid,SIGNAL_GREEN); + return true; + } + + return false; // aspect is not a defined one +} + void RMFT2::turnoutEvent(int16_t turnoutId, bool closed) { // Hunt for an ONTHROW/ONCLOSE for this turnout if (closed) onCloseLookup->handleEvent(F("CLOSE"),turnoutId); diff --git a/EXRAIL2.h b/EXRAIL2.h index eec2a06..914c822 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -155,6 +155,7 @@ class LookList { static void clockEvent(int16_t clocktime, bool change); static void rotateEvent(int16_t id, bool change); static void powerEvent(int16_t track, bool overload); + static bool signalAspectEvent(int16_t address, byte aspect ); static const int16_t SERVO_SIGNAL_FLAG=0x4000; static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000; static const int16_t DCC_SIGNAL_FLAG=0x1000; @@ -173,7 +174,7 @@ class LookList { static const FSH * getTurntableDescription(int16_t id); static const FSH * getTurntablePositionDescription(int16_t turntableId, uint8_t positionId); static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc); - + private: static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); static bool parseSlash(Print * stream, byte & paramCount, int16_t p[]) ; From 7ee4188d88e899007658a197db6bea25ae47b492 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 16 Feb 2024 12:36:33 +0000 Subject: [PATCH 13/28] Tidy and version --- DCCEXParser.cpp | 11 ++++------- EXRAIL2.cpp | 5 ++++- EXRAIL2Parser.cpp | 8 ++++++++ version.h | 6 +++++- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index bcd258a..d66b51f 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -387,16 +387,13 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) case 'A': // EXTENDED ACCESSORY { + // Note: if this happens to match a defined EXRAIL + // DCCX_SIGNAL, then EXRAIL will have intercepted + // this command alrerady. if (params!=2) break; if (p[0] != (p[0] & 0x7F)) break; if (p[1] != (p[1] & 0x1F)) break; -#ifdef EXRAIL_ACTIVE - // Ask exrail if this is just changing the aspect on a - // predefined DCCX_SIGNAL. Because this will handle all - // the IFRED and ONRED type issues at the same time. - if (RMFT2::signalAspectEvent(p[0],p[1])) return; -#endif - DCC::setExtendedAccessory(p[0],p[1],3); + DCC::setExtendedAccessory(p[0],p[1]); } return; diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 928bc79..014e42b 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -802,7 +802,10 @@ void RMFT2::loop2() { } case OPCODE_ASPECT: { // operand is address<<5 | value - DCC::setExtendedAccessory(operand>>5, operand & 0x1F); + int16_t address=operand>>5; + byte aspect=operand & 0x1f; + if (!signalAspectEvent(address,aspect)) + DCC::setExtendedAccessory(address,aspect); break; } diff --git a/EXRAIL2Parser.cpp b/EXRAIL2Parser.cpp index 99f0c37..7969750 100644 --- a/EXRAIL2Parser.cpp +++ b/EXRAIL2Parser.cpp @@ -51,6 +51,14 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16 opcode=0; break; + case 'A': // + if (paramCount!=2) break; + // Ask exrail if this is just changing the aspect on a + // predefined DCCX_SIGNAL. Because this will handle all + // the IFRED and ONRED type issues at the same time. + if (signalAspectEvent(p[0],p[1])) opcode=0; // all done + break; + case 'L': // This entire code block is compiled out if LLC macros not used if (!(compileFeatures & FEATURE_LCC)) return; diff --git a/version.h b/version.h index 84eeedf..341c61e 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,11 @@ #include "StringFormatter.h" -#define VERSION "5.2.33" +#define VERSION "5.2.34" +// 5.2.34 - Command fopr DCC Extended Accessories +// - Exrail ASPECT(address,aspect) for above. +// - EXRAIL DCCX_SIGNAL(Address,redAspect,amberAspect,greenAspect) +// - Exrail intercept for DCC Signals. // 5.2.33 - Exrail CONFIGURE_SERVO(vpin,pos1,pos2,profile) // 5.2.32 - Railcom Cutout (Initial trial Mega2560 only) // 5.2.31 - Exrail JMRI_SENSOR(vpin [,count]) creates types. From 7e4093f03fd96b76fb6cdbceb2c0b91918a86b8a Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 16 Feb 2024 12:45:33 +0000 Subject: [PATCH 14/28] comment only --- DCCEXParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index d66b51f..af6c4ee 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -45,7 +45,7 @@ Once a new OPCODE is decided upon, update this list. 0, Track power off 1, Track power on a, DCC accessory control - A, + A, DCC extended accessory control b, Write CV bit on main B, Write CV bit c, Request current command From dcd332603c1cc97f0474f7e1266b76d1fa7af3ae Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 17 Feb 2024 11:09:03 +0000 Subject: [PATCH 15/28] accessory packet issues. --- DCC.cpp | 40 +++++++++++++++++++++++++++++----------- DCC.h | 2 +- DCCEXParser.cpp | 17 ++++++----------- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index ed2a803..8a6e8e0 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -278,21 +278,39 @@ void DCC::setAccessory(int address, byte port, bool gate, byte onoff /*= 2*/) { } } -void DCC::setExtendedAccessory(int16_t address, int16_t value, byte repeats) { +bool DCC::setExtendedAccessory(int16_t address, int16_t value, byte repeats) { + +/* From https://www.nmra.org/sites/default/files/s-9.2.1_2012_07.pdf + +The Extended Accessory Decoder Control Packet is included for the purpose of transmitting aspect control to signal +decoders or data bytes to more complex accessory decoders. Each signal head can display one aspect at a time. +{preamble} 0 10AAAAAA 0 0AAA0AA1 0 000XXXXX 0 EEEEEEEE 1 + +XXXXX is for a single head. A value of 00000 for XXXXX indicates the absolute stop aspect. All other aspects +represented by the values for XXXXX are determined by the signaling system used and the prototype being +modeled. +*/ +/* CAH Notes: +Thus in byte packet form the format is 10AAAAAA, 0AAA0AA1, 000XXXXX + +Note that the Basic accessory format mentions +"By convention these bits (bits 4-6 of the second data byte) are in ones complement" +but this note is absent from the advanced packet description. +The (~address) construct below applies this because this appears to be +required. + +*/ + if (address != (address & 0x7FF)) return false; // 11 bits + if (value != (value & 0x1F)) return false; // 5 bits - // format is 10AAAAAA, 0AAA0AA1, 000XXXXX - if (address != (address & 0x7F)) return; - if (value != (value & 0x1F)) return; byte b[3]; - b[0]= 0x80 | ((address & 0x7FF)>>5); - b[1]= 0x01 | ((address & 0x1c)<<2) | (address & 0x03)<<1; - b[2]=value & 0x1F; + b[0]= 0x80 | (address>>5); + b[1]= 0x01 | (((~address) & 0x1c)<<2) | ((address & 0x03)<<1); + b[2]=value; DCCWaveform::mainTrack.schedulePacket(b, sizeof(b), repeats); // Repeat on packet three times -#if defined(EXRAIL_ACTIVE) - // TODO RMFT2::activateExtendedEvent(address,value); -#endif -} + return true; + } // // writeCVByteMain: Write a byte with PoM on main. This writes diff --git a/DCC.h b/DCC.h index e1aeae6..aa93520 100644 --- a/DCC.h +++ b/DCC.h @@ -71,7 +71,7 @@ public: static uint32_t getFunctionMap(int cab); static void updateGroupflags(byte &flags, int16_t functionNumber); static void setAccessory(int address, byte port, bool gate, byte onoff = 2); - static void setExtendedAccessory(int16_t address, int16_t value, byte repeats=3); + static bool setExtendedAccessory(int16_t address, int16_t value, byte repeats=3); static bool writeTextPacket(byte *b, int nBytes); // ACKable progtrack calls bitresults callback 0,0 or -1, cv returns value or -1 diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index af6c4ee..db78d71 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -385,17 +385,12 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } return; - case 'A': // EXTENDED ACCESSORY - { - // Note: if this happens to match a defined EXRAIL - // DCCX_SIGNAL, then EXRAIL will have intercepted - // this command alrerady. - if (params!=2) break; - if (p[0] != (p[0] & 0x7F)) break; - if (p[1] != (p[1] & 0x1F)) break; - DCC::setExtendedAccessory(p[0],p[1]); - } - return; + case 'A': // EXTENDED ACCESSORY + // Note: if this happens to match a defined EXRAIL + // DCCX_SIGNAL, then EXRAIL will have intercepted + // this command alrerady. + if (params==2 && DCC::setExtendedAccessory(p[0],p[1])) return; + break; case 'T': // TURNOUT if (parseT(stream, params, p)) From fbbedc7577ab390e22235ffab111384179ed53be Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 17 Feb 2024 18:51:13 +0100 Subject: [PATCH 16/28] make ext acc packet format follow RCN-213 --- DCC.cpp | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 8a6e8e0..a435eeb 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -289,28 +289,45 @@ decoders or data bytes to more complex accessory decoders. Each signal head can XXXXX is for a single head. A value of 00000 for XXXXX indicates the absolute stop aspect. All other aspects represented by the values for XXXXX are determined by the signaling system used and the prototype being modeled. -*/ -/* CAH Notes: + +From https://normen.railcommunity.de/RCN-213.pdf: + +More information is in RCN-213 about how the address bits are organized. +preamble -0- 1 0 A7 A6 A5 A4 A3 A2 -0- 0 ^A10 ^A9 ^A8 0 A1 A0 1 -0- .... + Thus in byte packet form the format is 10AAAAAA, 0AAA0AA1, 000XXXXX -Note that the Basic accessory format mentions -"By convention these bits (bits 4-6 of the second data byte) are in ones complement" -but this note is absent from the advanced packet description. -The (~address) construct below applies this because this appears to be -required. +Die Adresse für den ersten erweiterten Zubehördecoder ist wie bei den einfachen +Zubehördecodern die Adresse 4 = 1000-0001 0111-0001 . Diese Adresse wird in +Anwenderdialogen als Adresse 1 dargestellt. +This means that the first address shown to the user as "1" is mapped +to internal address 4. + +Note that the Basic accessory format mentions "By convention these +bits (bits 4-6 of the second data byte) are in ones complement" but +this note is absent from the advanced packet description. The +english translation does not mention that the address format for +the advanced packet follows the one for the basic packet but +according to the RCN-213 this is the case. + +We allow for addresses from -3 to 2047-3 as that allows to address the +whole range of the 11 bits sent to track. */ - if (address != (address & 0x7FF)) return false; // 11 bits - if (value != (value & 0x1F)) return false; // 5 bits + if ((address > 2044) || (address < -3)) return false; // 2047-3, 11 bits but offset 3 + if (value != (value & 0x1F)) return false; // 5 bits - + address+=3; // +3 offset according to RCN-213 byte b[3]; - b[0]= 0x80 | (address>>5); - b[1]= 0x01 | (((~address) & 0x1c)<<2) | ((address & 0x03)<<1); + b[0]= 0x80 // bits always on + | ((address>>2) & 0x3F); // shift out 2, mask out used bits + b[1]= 0x01 // bits always on + | (((~(address>>8)) & 0x07)<<4) // shift out 8, invert, mask 3 bits, shift up 4 + | ((address & 0x03)<<1); // mask 2 bits, shift up 1 b[2]=value; - DCCWaveform::mainTrack.schedulePacket(b, sizeof(b), repeats); // Repeat on packet three times + DCCWaveform::mainTrack.schedulePacket(b, sizeof(b), repeats); return true; - } +} // // writeCVByteMain: Write a byte with PoM on main. This writes From fe9b1da8a3bcdced2465074dd882689d93e29b31 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 17 Feb 2024 18:52:48 +0100 Subject: [PATCH 17/28] version 5.2.35 --- 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 956a8d2..40e7ece 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202402050827Z" +#define GITHUB_SHA "devel-202402171752Z" diff --git a/version.h b/version.h index 341c61e..d1d6416 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.34" +#define VERSION "5.2.35" +// 5.2.35 - Bugfix: Make DCC Extended Accessories follow RCN-213 // 5.2.34 - Command fopr DCC Extended Accessories // - Exrail ASPECT(address,aspect) for above. // - EXRAIL DCCX_SIGNAL(Address,redAspect,amberAspect,greenAspect) From 821115caaddd513152d277a5e5e4426bad8f9b58 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 19 Feb 2024 20:02:01 +0000 Subject: [PATCH 18/28] Make ASSERT macro match 5.2.35 --- EXRAILMacros.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 698451e..f0cb150 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -118,8 +118,8 @@ static_assert(!hasdup(compileTimeSequenceList[0],1),"Duplicate SEQUENCE/ROUTE/AU // This pass generates no runtime data or code #include "EXRAIL2MacroReset.h" #undef ASPECT -#define ASPECT(address,value) static_assert((address & 0x7ff)== address, "invalid Address"); \ - static_assert((value & 0x1F)== value, "Invalid value"); +#define ASPECT(address,value) static_assert(address <=2044, "invalid Address"); \ + static_assert(address>=-3, "Invalid value"); #undef CALL #define CALL(id) static_assert(hasseq(id),"Sequence not found"); #undef FOLLOW From 423d1932ae42035412c4b6c970f2d2d889a91991 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 20 Feb 2024 15:08:41 +0100 Subject: [PATCH 19/28] merge artifact --- version.h | 1 - 1 file changed, 1 deletion(-) diff --git a/version.h b/version.h index c3ea916..dcc7c6e 100644 --- a/version.h +++ b/version.h @@ -33,7 +33,6 @@ // 5.2.21 - Add STARTUP_DELAY config option to delay CS bootup // 5.2.20 - Check return of Ethernet.begin() // 5.2.19 - ESP32: Determine if the RMT hardware can handle DCC ->>>>>>> devel // 5.2.18 - Display network IP fix // 5.2.17 - ESP32 simplify network logic // 5.2.16 - Bugfix to allow for devices using the EX-IOExpander protocol to have no analogue or no digital pins From 3fa2edb0dac4cbae560af447cc0982e481f6d67c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 20 Feb 2024 15:13:22 +0100 Subject: [PATCH 20/28] fix warning and indent proper --- Turntables.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/Turntables.cpp b/Turntables.cpp index ba143cb..f75005c 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -247,22 +247,23 @@ DCCTurntable::DCCTurntable(uint16_t id) : Turntable(id, TURNTABLE_DCC) {} StringFormatter::send(stream, F("\n"), _turntableData.id); } - // EX-Turntable specific code for moving to the specified position - bool DCCTurntable::setPositionInternal(uint8_t position, uint8_t activity) { +// EX-Turntable specific code for moving to the specified position +bool DCCTurntable::setPositionInternal(uint8_t position, uint8_t activity) { + (void) activity; #ifndef IO_NO_HAL - int16_t value = getPositionValue(position); - if (position == 0 || !value) return false; // Return false if it's not a valid position - // Set position via device driver - int16_t addr=value>>3; - int16_t subaddr=(value>>1) & 0x03; - bool active=value & 0x01; - _previousPosition = _turntableData.position; - _turntableData.position = position; - DCC::setAccessory(addr, subaddr, active); + int16_t value = getPositionValue(position); + if (position == 0 || !value) return false; // Return false if it's not a valid position + // Set position via device driver + int16_t addr=value>>3; + int16_t subaddr=(value>>1) & 0x03; + bool active=value & 0x01; + _previousPosition = _turntableData.position; + _turntableData.position = position; + DCC::setAccessory(addr, subaddr, active); #else - (void)position; + (void)position; #endif - return true; - } + return true; +} #endif From 1101cfd6377702fe4e3f03fd361d63cf48ec9bef Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 24 Feb 2024 17:24:55 +0100 Subject: [PATCH 21/28] surpress warnings --- IO_EXTurntable.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index aeb935b..0e134d4 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -83,6 +83,7 @@ void EXTurntable::_loop(unsigned long currentMicros) { // Read returns status as obtained in our loop. // Return false if our status value is invalid. int EXTurntable::_read(VPIN vpin) { + (void)vpin; // surpress warning if (_deviceState == DEVSTATE_FAILED) return 0; if (_stepperStatus > 1) { return false; @@ -127,6 +128,8 @@ void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_ vpin, value, activity, duration); DIAG(F("I2CManager write I2C Address:%d stepsMSB:%d stepsLSB:%d activity:%d"), _I2CAddress.toString(), stepsMSB, stepsLSB, activity); +#else + (void)duration; #endif if (activity < 4) _stepperStatus = 1; // Tell the device driver Turntable-EX is busy _previousStatus = _stepperStatus; From b29a01f436daab54978cd00b0d9af1830672def1 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 24 Feb 2024 20:56:06 +0100 Subject: [PATCH 22/28] ESP32: Use the BOOSTER_INPUT define --- TrackManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index a77108b..5aba978 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -219,7 +219,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr if (mode & TRACK_MODE_BOOST) { //DIAG(F("Track=%c mode boost pin %d"),trackToSet+'A', p.pin); pinMode(BOOSTER_INPUT, INPUT); - gpio_matrix_in(26, SIG_IN_FUNC228_IDX, false); //pads 224 to 228 available as loopback + gpio_matrix_in(BOOSTER_INPUT, SIG_IN_FUNC228_IDX, false); //pads 224 to 228 available as loopback gpio_matrix_out(p.pin, SIG_IN_FUNC228_IDX, false, false); if (p.invpin != UNUSED_PIN) { gpio_matrix_out(p.invpin, SIG_IN_FUNC228_IDX, true /*inverted*/, false); From 274affce45d1c0d5e5527fdd524f607ffa058af8 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 24 Feb 2024 20:57:38 +0100 Subject: [PATCH 23/28] version 5.2.37 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 9c706cf..699ead5 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202402201404Z" +#define GITHUB_SHA "devel-202402241957Z" diff --git a/version.h b/version.h index dcc7c6e..ee8c827 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.36" +#define VERSION "5.2.37" +// 5.2.37 - Bugfix ESP32: Use BOOSTER_INPUT define // 5.2.36 - Variable frequency for DC mode // 5.2.35 - Bugfix: Make DCC Extended Accessories follow RCN-213 // 5.2.34 - Command fopr DCC Extended Accessories From 7503421eb6bea5f7c3758cb0604c54829df826c3 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 26 Feb 2024 09:11:21 +0100 Subject: [PATCH 24/28] Compile time optimization for booster mode --- MotorDriver.h | 12 +++++++++--- TrackManager.cpp | 9 +++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/MotorDriver.h b/MotorDriver.h index b678a84..4491164 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -34,9 +34,15 @@ template inline T operator| (T a, T b) { return (T)((int)a | (int)b); } template inline T operator& (T a, T b) { return (T)((int)a & (int)b); } template inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); } enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4, - TRACK_MODE_DC = 8, TRACK_MODE_EXT = 16, TRACK_MODE_BOOST = 32, - TRACK_MODE_ALL = 62, // only to operate all tracks - TRACK_MODE_INV = 64, TRACK_MODE_DCX = 72 /*DC + INV*/, TRACK_MODE_AUTOINV = 128}; + TRACK_MODE_DC = 8, TRACK_MODE_EXT = 16, +#ifdef ARDUINO_ARCH_ESP32 + TRACK_MODE_BOOST = 32, +#else + TRACK_MODE_BOOST = 0, +#endif + TRACK_MODE_ALL = TRACK_MODE_MAIN|TRACK_MODE_PROG|TRACK_MODE_DC|TRACK_MODE_EXT|TRACK_MODE_BOOST, + TRACK_MODE_INV = 64, + TRACK_MODE_DCX = TRACK_MODE_DC|TRACK_MODE_INV, TRACK_MODE_AUTOINV = 128}; #define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH #define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW diff --git a/TrackManager.cpp b/TrackManager.cpp index 5aba978..e357e7f 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -362,7 +362,8 @@ bool TrackManager::parseEqualSign(Print *stream, int16_t params, int16_t p[]) if (params==2 && p[1]=="EXT"_hk) // <= id EXT> return setTrackMode(p[0],TRACK_MODE_EXT); #ifdef BOOSTER_INPUT - if (params==2 && p[1]=="BOOST"_hk) // <= id BOOST> + if (TRACK_MODE_BOOST != 0 && // compile time optimization + params==2 && p[1]=="BOOST"_hk) // <= id BOOST> return setTrackMode(p[0],TRACK_MODE_BOOST); #endif if (params==2 && p[1]=="AUTO"_hk) // <= id AUTO> @@ -401,11 +402,11 @@ const FSH* TrackManager::getModeName(TRACK_MODE tm) { modename=F("EXT"); else if(tm & TRACK_MODE_BOOST) { if(tm & TRACK_MODE_AUTOINV) - modename=F("B A"); + modename=F("BOOST A"); else if (tm & TRACK_MODE_INV) - modename=F("B I"); + modename=F("BOOST I"); else - modename=F("B"); + modename=F("BOOST"); } else if (tm & TRACK_MODE_DC) { if (tm & TRACK_MODE_INV) From 3d6f41398d5efac7fcd8e35a70510657649f4770 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 4 Mar 2024 15:07:03 +0100 Subject: [PATCH 25/28] compile time check WIFI_PASSWORD length for reasonable value --- CommandStation-EX.ino | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index 2cd9d33..3a0e5ca 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -65,6 +65,9 @@ #ifdef EXRAIL_WARNING #warning You have myAutomation.h but your hardware has not enough memory to do that, so EX-RAIL DISABLED #endif +// compile time check, passwords 1 to 7 chars do not work, so do not try to compile with them at all +// remember trailing '\0', sizeof("") == 1. +#define PASSWDCHECK(S) static_assert(sizeof(S) == 1 || sizeof(S) > 8, "Password shorter than 8 chars") void setup() { @@ -102,10 +105,12 @@ void setup() // Start Ethernet if it exists #ifndef ARDUINO_ARCH_ESP32 #if WIFI_ON + PASSWDCHECK(WIFI_PASSWORD); // compile time check WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP); #endif // WIFI_ON #else // ESP32 needs wifi on always + PASSWDCHECK(WIFI_PASSWORD); // compile time check WifiESP::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP); #endif // ARDUINO_ARCH_ESP32 From b75266689928b1040a06c90240a34970379301c9 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 4 Mar 2024 15:20:48 +0100 Subject: [PATCH 26/28] remove warning --- EXRAILMacros.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 825ce02..876bc23 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -350,6 +350,8 @@ const FSH * RMFT2::getTurntableDescription(int16_t turntableId) { #define TT_ADDPOSITION(turntable_id,position,value,home,description...) T_DESC(turntable_id,position,description) const FSH * RMFT2::getTurntablePositionDescription(int16_t turntableId, uint8_t positionId) { + (void)turntableId; + (void)positionId; #include "myAutomation.h" return NULL; } From 4b04a80e6fa5b4d2505f31eb53c284944d3cfbd6 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 5 Mar 2024 19:59:29 +0000 Subject: [PATCH 27/28] Remove unnecessary warning --- EXRAILMacros.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 876bc23..ca12e23 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -95,14 +95,14 @@ constexpr int16_t stuffSize=sizeof(compileTimeSequenceList)/sizeof(int16_t) - 1; // Compile time function to check for sequence nos. -constexpr bool hasseq(const int16_t value, const uint16_t pos=0 ) { +constexpr bool hasseq(const int16_t value, const int16_t pos=0 ) { return pos>=stuffSize? false : compileTimeSequenceList[pos]==value || hasseq(value,pos+1); } // Compile time function to check for duplicate sequence nos. -constexpr bool hasdup(const int16_t value, const uint16_t pos ) { +constexpr bool hasdup(const int16_t value, const int16_t pos ) { return pos>=stuffSize? false : compileTimeSequenceList[pos]==value || hasseq(value,pos+1) From be218d3032967a64326a1daddf954cf0baa8b35c Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 8 Mar 2024 20:33:11 +0000 Subject: [PATCH 28/28] EXRAIL MESSAGE() --- CommandDistributor.cpp | 5 +++++ CommandDistributor.h | 1 + EXRAIL2.cpp | 4 ++++ EXRAIL2.h | 2 +- EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 4 ++++ version.h | 4 +++- 7 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index f838fd2..67bcc54 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -312,6 +312,11 @@ void CommandDistributor::broadcastRaw(clientType type, char * msg) { broadcastReply(type, F("%s"),msg); } +void CommandDistributor::broadcastMessage(char * message) { + broadcastReply(COMMAND_TYPE, F("\n"),message); + broadcastReply(WITHROTTLE_TYPE, F("Hm%s\n"),message); +} + void CommandDistributor::broadcastTrackState(const FSH* format, byte trackLetter, const FSH *modename, int16_t dcAddr) { broadcastReply(COMMAND_TYPE, format, trackLetter, modename, dcAddr); } diff --git a/CommandDistributor.h b/CommandDistributor.h index e4dff5d..f68a5e2 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -60,6 +60,7 @@ public : static void forget(byte clientId); static void broadcastRouteState(uint16_t routeId,byte state); static void broadcastRouteCaption(uint16_t routeId,const FSH * caption); + static void broadcastMessage(char * message); // Handling code for virtual LCD receiver. static Print * getVirtualLCDSerial(byte screen, byte row); diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 9f1075c..c9c6716 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1334,6 +1334,7 @@ void RMFT2::thrungeString(uint32_t strfar, thrunger mode, byte id) { break; case thrunge_parse: case thrunge_broadcast: + case thrunge_message: case thrunge_lcd: default: // thrunge_lcd+1, ... if (!buffer) buffer=new StringBuffer(); @@ -1371,6 +1372,9 @@ void RMFT2::thrungeString(uint32_t strfar, thrunger mode, byte id) { case thrunge_withrottle: CommandDistributor::broadcastRaw(CommandDistributor::WITHROTTLE_TYPE,buffer->getString()); break; + case thrunge_message: + CommandDistributor::broadcastMessage(buffer->getString()); + break; case thrunge_lcd: LCD(id,F("%s"),buffer->getString()); break; diff --git a/EXRAIL2.h b/EXRAIL2.h index b4a06da..f4cf320 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -94,7 +94,7 @@ enum thrunger: byte { thrunge_serial,thrunge_parse, thrunge_serial1, thrunge_serial2, thrunge_serial3, thrunge_serial4, thrunge_serial5, thrunge_serial6, - thrunge_lcn, + thrunge_lcn,thrunge_message, thrunge_lcd, // Must be last!! }; diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 2428a09..e94c657 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -97,6 +97,7 @@ #undef LCCX #undef LCN #undef MOVETT +#undef MESSAGE #undef ONACTIVATE #undef ONACTIVATEL #undef ONAMBER @@ -253,6 +254,7 @@ #define LCD(row,msg) #define SCREEN(display,row,msg) #define LCN(msg) +#define MESSAGE(msg) #define MOVETT(id,steps,activity) #define ONACTIVATE(addr,subaddr) #define ONACTIVATEL(linear) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index ca12e23..5588811 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -253,6 +253,9 @@ const int StringMacroTracker1=__COUNTER__; #define PRINT(msg) THRUNGE(msg,thrunge_print) #undef LCN #define LCN(msg) THRUNGE(msg,thrunge_lcn) +#undef MESSAGE +#define MESSAGE(msg) THRUNGE(msg,thrunge_message) + #undef ROUTE_CAPTION #define ROUTE_CAPTION(id,caption) \ case (__COUNTER__ - StringMacroTracker1) : {\ @@ -516,6 +519,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define SCREEN(display,id,msg) PRINT(msg) #define STEALTH(code...) PRINT(dummy) #define LCN(msg) PRINT(msg) +#define MESSAGE(msg) PRINT(msg) #define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0), #define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr), #define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3), diff --git a/version.h b/version.h index ee8c827..97b0a70 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.37" +#define VERSION "5.2.38" +// 5.2.38 - Exrail MESSAGE("text") to send a user message to all +// connected throttles (uses and withrottle Hmtext. // 5.2.37 - Bugfix ESP32: Use BOOSTER_INPUT define // 5.2.36 - Variable frequency for DC mode // 5.2.35 - Bugfix: Make DCC Extended Accessories follow RCN-213