1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-04-21 20:41:19 +02:00

Compare commits

..

No commits in common. "dc84f560e65a928b24d5b612b25a711d01e83231" and "f031add7a01c8e606fce9ea5b84f04a8f2eff4d6" have entirely different histories.

14 changed files with 222 additions and 353 deletions

View File

@ -64,8 +64,8 @@ Once a new OPCODE is decided upon, update this list.
I, Turntable object command, control, and broadcast I, Turntable object command, control, and broadcast
j, Throttle responses j, Throttle responses
J, Throttle queries J, Throttle queries
k, Block exit (Railcom) k, Reserved for future use - Potentially Railcom
K, Block enter (Railcom) K, Reserved for future use - Potentially Railcom
l, Loco speedbyte/function map broadcast l, Loco speedbyte/function map broadcast
L, Reserved for LCC interface (implemented in EXRAIL) L, Reserved for LCC interface (implemented in EXRAIL)
m, message to throttles (broadcast output) m, message to throttles (broadcast output)
@ -1223,10 +1223,6 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
return true; return true;
#ifdef HAS_ENOUGH_MEMORY #ifdef HAS_ENOUGH_MEMORY
case "RAILCOM"_hk: // <D RAILCOM ON/OFF>
Diag::RAILCOM = onOff;
return true;
case "WIFI"_hk: // <D WIFI ON/OFF> case "WIFI"_hk: // <D WIFI ON/OFF>
Diag::WIFI = onOff; Diag::WIFI = onOff;
return true; return true;

View File

@ -2,7 +2,7 @@
* © 2021 Mike S * © 2021 Mike S
* © 2021-2023 Harald Barth * © 2021-2023 Harald Barth
* © 2021 Fred Decker * © 2021 Fred Decker
* © 2021-2025 Chris Harlow * © 2021 Chris Harlow
* © 2021 David Cutting * © 2021 David Cutting
* All rights reserved. * All rights reserved.
* *
@ -57,59 +57,66 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
TCCR1B = _BV(WGM13) | _BV(CS10); // Mode 8, clock select 1 TCCR1B = _BV(WGM13) | _BV(CS10); // Mode 8, clock select 1
TIMSK1 = _BV(TOIE1); // Enable Software interrupt TIMSK1 = _BV(TOIE1); // Enable Software interrupt
interrupts(); interrupts();
//diagnostic pinMode(4,OUTPUT);
} }
void DCCTimer::startRailcomTimer(byte brakePin) { void DCCTimer::startRailcomTimer(byte brakePin) {
(void) brakePin; // Ignored... works on pin 9 only
// diagnostic digitalWrite(4,HIGH);
/* The Railcom timer is started in such a way that it /* The Railcom timer is started in such a way that it
- First triggers 58+29 uS after the previous TIMER1 tick. - First triggers 28uS after the last TIMER1 tick.
This provides an accurate offset (in High Accuracy mode) This provides an accurate offset (in High Accuracy mode)
for the start of the Railcom cutout. for the start of the Railcom cutout.
- Sets the Railcom pin high at first tick and subsequent ticks - Sets the Railcom pin high at first tick,
until its reset to setting pin 9 low at next tick. because its been setup with 100% PWM duty cycle.
- Cycles at 436uS so the second tick is the - Cycles at 436uS so the second tick is the
correct distance from the cutout. correct distance from the cutout.
- Waveform code is responsible for resetting - Waveform code is responsible for altering the PWM
any time between the first and second tick. duty cycle to 0% any time between the first and last tick.
(there will be 7 DCC timer1 ticks in which to do this.) (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 const int cutoutDuration = 430; // Desired interval in microseconds
const int cycle=cutoutDuration/2;
const byte RailcomFudge0=58+58+29; // 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); // enable pwm on pin 9
TCCR2A |= (1 << WGM20);
// 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 / 64 - 1;
// Calculate the compare match value for desired duty cycle
OCR2B = OCR2A+1; // set duty cycle to 100%= OCR2A)
// Set Timer2 to CTC mode with set on compare match
TCCR2A = (1 << WGM21) | (1 << COM2B0) | (1 << COM2B1);
// Prescaler of 32
TCCR2B = (1 << CS21) | (1 << CS20);
OCR2A = cycle-1; // Compare match value for 430 uS
// Enable Timer2 output on pin 9 (OC2B) // Enable Timer2 output on pin 9 (OC2B)
DDRB |= (1 << DDB1); DDRB |= (1 << DDB1);
// TODO Fudge TCNT2 to sync with last tcnt1 tick + 28uS
// RailcomFudge2 is the expected time from idealised
// setup call (at previous DCC timer interrupt) to the cutout.
// This value should be reduced to reflect the Timer1 value
// measuring the time since the previous hardware interrupt
byte tcfudge=TCNT1/16;
TCNT2=cycle-RailcomFudge0/2+tcfudge/2;
// Previous TIMER1 Tick was at rising end-of-packet bit // Previous TIMER1 Tick was at rising end-of-packet bit
// Cutout starts half way through first preamble // Cutout starts half way through first preamble
// that is 2.5 * 58uS later. // 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() { void DCCTimer::ackRailcomTimer() {
// Change Timer2 to CTC mode with RESET pin 9 on next compare match OCR2B= 0x00; // brake pin pwm duty cycle 0 at next tick
TCCR2A = (1 << WGM21) | (1 << COM2B1);
// diagnostic digitalWrite(4,LOW);
} }

View File

@ -24,13 +24,14 @@
#ifndef ARDUINO_ARCH_ESP32 #ifndef ARDUINO_ARCH_ESP32
// This code is replaced entirely on an ESP32 // This code is replaced entirely on an ESP32
#include <Arduino.h> #include <Arduino.h>
#include "DCCWaveform.h" #include "DCCWaveform.h"
#include "TrackManager.h" #include "TrackManager.h"
#include "DCCTimer.h" #include "DCCTimer.h"
#include "DCCACK.h" #include "DCCACK.h"
#include "DIAG.h" #include "DIAG.h"
bool DCCWaveform::cutoutNextTime=false;
DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true); DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true);
DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false); DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
@ -70,18 +71,9 @@ void DCCWaveform::loop() {
#pragma GCC push_options #pragma GCC push_options
#pragma GCC optimize ("-O3") #pragma GCC optimize ("-O3")
void DCCWaveform::interruptHandler() { void DCCWaveform::interruptHandler() {
// call the timer edge sensitive actions for progtrack and maintrack // call the timer edge sensitive actions for progtrack and maintrack
// member functions would be cleaner but have more overhead // member functions would be cleaner but have more overhead
#if defined(HAS_ENOUGH_MEMORY)
if (cutoutNextTime) {
cutoutNextTime=false;
railcomSampleWindow=false; // about to cutout, stop reading railcom data.
railcomCutoutCounter++;
DCCTimer::startRailcomTimer(9);
}
#endif
byte sigMain=signalTransform[mainTrack.state]; byte sigMain=signalTransform[mainTrack.state];
byte sigProg=TrackManager::progTrackSyncMain? sigMain : signalTransform[progTrack.state]; byte sigProg=TrackManager::progTrackSyncMain? sigMain : signalTransform[progTrack.state];
@ -124,23 +116,18 @@ DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) {
bits_sent = 0; bits_sent = 0;
} }
bool DCCWaveform::railcomPossible=false; // High accuracy only
volatile bool DCCWaveform::railcomActive=false; // switched on by user volatile bool DCCWaveform::railcomActive=false; // switched on by user
volatile bool DCCWaveform::railcomDebug=false; // switched on by user volatile bool DCCWaveform::railcomDebug=false; // switched on by user
volatile bool DCCWaveform::railcomSampleWindow=false; // true during packet transmit
volatile byte DCCWaveform::railcomCutoutCounter=0; // cyclic cutout
volatile byte DCCWaveform::railcomLastAddressHigh=0;
volatile byte DCCWaveform::railcomLastAddressLow=0;
bool DCCWaveform::setRailcom(bool on, bool debug) { bool DCCWaveform::setRailcom(bool on, bool debug) {
if (on && railcomPossible) { if (on) {
// TODO check possible
railcomActive=true; railcomActive=true;
railcomDebug=debug; railcomDebug=debug;
} }
else { else {
railcomActive=false; railcomActive=false;
railcomDebug=false; railcomDebug=false;
railcomSampleWindow=false;
} }
return railcomActive; return railcomActive;
} }
@ -153,37 +140,14 @@ void DCCWaveform::interrupt2() {
// or WAVE_HIGH_0 for a 0 bit. // or WAVE_HIGH_0 for a 0 bit.
if (remainingPreambles > 0 ) { if (remainingPreambles > 0 ) {
state=WAVE_MID_1; // switch state to trigger LOW on next interrupt state=WAVE_MID_1; // switch state to trigger LOW on next interrupt
remainingPreambles--; remainingPreambles--;
// As we get to the end of the preambles, open the reminder window. // As we get to the end of the preambles, open the reminder window.
// This delays any reminder insertion until the last moment so // This delays any reminder insertion until the last moment so
// that the reminder doesn't block a more urgent packet. // that the reminder doesn't block a more urgent packet.
reminderWindowOpen=transmitRepeats==0 && remainingPreambles<10 && remainingPreambles>1; reminderWindowOpen=transmitRepeats==0 && remainingPreambles<4 && remainingPreambles>1;
if (remainingPreambles==1) if (remainingPreambles==1) promotePendingPacket();
promotePendingPacket(); else if (remainingPreambles==10 && isMainTrack && railcomActive) DCCTimer::ackRailcomTimer();
#if defined(HAS_ENOUGH_MEMORY)
else if (isMainTrack && railcomActive) {
if (remainingPreambles==(requiredPreambles-1)) {
// First look if we need to start a railcom cutout on next interrupt
cutoutNextTime= true;
} else if (remainingPreambles==(requiredPreambles-12)) {
// cutout has ended so its now possible to poll the railcom detectors
// requiredPreambles is one higher that preamble length so
// if preamble length is 16 then this evaluates to 5
// Remember address bytes of last sent packet so that Railcom can
// work out where the channel2 data came from.
railcomLastAddressHigh=transmitPacket[0];
railcomLastAddressLow =transmitPacket[1];
railcomSampleWindow=true;
} else if (remainingPreambles==(requiredPreambles-3)) {
// cutout can be ended when read
// see above for requiredPreambles
DCCTimer::ackRailcomTimer();
}
}
#endif
// Update free memory diagnostic as we don't have anything else to do this time. // 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. // Allow for checkAck and its called functions using 22 bytes more.
else DCCTimer::updateMinimumFreeMemoryISR(22); else DCCTimer::updateMinimumFreeMemoryISR(22);
@ -207,6 +171,12 @@ void DCCWaveform::interrupt2() {
bytes_sent = 0; bytes_sent = 0;
// preamble for next packet will start... // preamble for next packet will start...
remainingPreambles = requiredPreambles; 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);
} }
} }
} }
@ -260,7 +230,7 @@ void DCCWaveform::promotePendingPacket() {
// Fortunately reset and idle packets are the same length // Fortunately reset and idle packets are the same length
// Note: If railcomDebug is on, then we send resets to the main // Note: If railcomDebug is on, then we send resets to the main
// track instead of idles. This means that all data will be zeros // track instead of idles. This means that all data will be zeros
// and only the presets will be ones, making it much // and only the porersets will be ones, making it much
// easier to read on a logic analyser. // easier to read on a logic analyser.
memcpy( transmitPacket, (isMainTrack && (!railcomDebug)) ? idlePacket : resetPacket, sizeof(idlePacket)); memcpy( transmitPacket, (isMainTrack && (!railcomDebug)) ? idlePacket : resetPacket, sizeof(idlePacket));
transmitLength = sizeof(idlePacket); transmitLength = sizeof(idlePacket);
@ -268,3 +238,93 @@ void DCCWaveform::promotePendingPacket() {
if (getResets() < 250) sentResetsSincePacket++; // only place to increment (private!) if (getResets() < 250) sentResetsSincePacket++; // only place to increment (private!)
} }
#endif #endif
#ifdef ARDUINO_ARCH_ESP32
#include "DCCWaveform.h"
#include "DCCACK.h"
DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true);
DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
RMTChannel *DCCWaveform::rmtMainChannel = NULL;
RMTChannel *DCCWaveform::rmtProgChannel = NULL;
DCCWaveform::DCCWaveform(byte preambleBits, bool isMain) {
isMainTrack = isMain;
requiredPreambles = preambleBits;
}
void DCCWaveform::begin() {
for(const auto& md: TrackManager::getMainDrivers()) {
pinpair p = md->getSignalPin();
if(rmtMainChannel) {
//DIAG(F("added pins %d %d to MAIN channel"), p.pin, p.invpin);
rmtMainChannel->addPin(p); // add pin to existing main channel
} else {
//DIAG(F("new MAIN channel with pins %d %d"), p.pin, p.invpin);
rmtMainChannel = new RMTChannel(p, true); /* create new main channel */
}
}
MotorDriver *md = TrackManager::getProgDriver();
if (md) {
pinpair p = md->getSignalPin();
if (rmtProgChannel) {
//DIAG(F("added pins %d %d to PROG channel"), p.pin, p.invpin);
rmtProgChannel->addPin(p); // add pin to existing prog channel
} else {
//DIAG(F("new PROGchannel with pins %d %d"), p.pin, p.invpin);
rmtProgChannel = new RMTChannel(p, false);
}
}
}
void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) {
if (byteCount > MAX_PACKET_SIZE) return; // allow for chksum
byte checksum = 0;
for (byte b = 0; b < byteCount; b++) {
checksum ^= buffer[b];
pendingPacket[b] = buffer[b];
}
// buffer is MAX_PACKET_SIZE but pendingPacket is one bigger
pendingPacket[byteCount] = checksum;
pendingLength = byteCount + 1;
pendingRepeats = repeats;
// DIAG repeated commands (accesories)
// if (pendingRepeats > 0)
// DIAG(F("Repeats=%d on %s track"), pendingRepeats, isMainTrack ? "MAIN" : "PROG");
// The resets will be zero not only now but as well repeats packets into the future
clearResets(repeats+1);
{
int ret = 0;
do {
if(isMainTrack) {
if (rmtMainChannel != NULL)
ret = rmtMainChannel->RMTfillData(pendingPacket, pendingLength, pendingRepeats);
} else {
if (rmtProgChannel != NULL)
ret = rmtProgChannel->RMTfillData(pendingPacket, pendingLength, pendingRepeats);
}
} while(ret > 0);
}
}
bool DCCWaveform::isReminderWindowOpen() {
if(isMainTrack) {
if (rmtMainChannel == NULL)
return false;
return !rmtMainChannel->busy();
} else {
if (rmtProgChannel == NULL)
return false;
return !rmtProgChannel->busy();
}
}
void IRAM_ATTR DCCWaveform::loop() {
DCCACK::checkAck(progTrack.getResets());
}
bool DCCWaveform::setRailcom(bool on, bool debug) {
// TODO... ESP32 railcom waveform
return false;
}
#endif

View File

@ -3,7 +3,7 @@
* © 2021 Mike S * © 2021 Mike S
* © 2021 Fred Decker * © 2021 Fred Decker
* © 2020-2024 Harald Barth * © 2020-2024 Harald Barth
* © 2020-2025 Chris Harlow * © 2020-2021 Chris Harlow
* All rights reserved. * All rights reserved.
* *
* This file is part of CommandStation-EX * This file is part of CommandStation-EX
@ -23,8 +23,11 @@
*/ */
#ifndef DCCWaveform_h #ifndef DCCWaveform_h
#define DCCWaveform_h #define DCCWaveform_h
#include "MotorDriver.h"
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#include "DCCRMT.h" #include "DCCRMT.h"
#include "TrackManager.h"
#endif #endif
@ -83,29 +86,7 @@ class DCCWaveform {
bool isReminderWindowOpen(); bool isReminderWindowOpen();
void promotePendingPacket(); void promotePendingPacket();
static bool setRailcom(bool on, bool debug); static bool setRailcom(bool on, bool debug);
inline static bool isRailcom() { static bool isRailcom() {return railcomActive;}
return railcomActive;
};
inline static byte getRailcomCutoutCounter() {
return railcomCutoutCounter;
};
inline static bool isRailcomSampleWindow() {
return railcomSampleWindow;
};
inline static bool isRailcomPossible() {
return railcomPossible;
};
inline static void setRailcomPossible(bool yes) {
railcomPossible=yes;
if (!yes) setRailcom(false,false);
};
inline static uint16_t getRailcomLastLocoAddress() {
// first 2 bits 00=short loco, 11=long loco , 01/10 = accessory
byte addressType=railcomLastAddressHigh & 0xC0;
if (addressType==0xC0) return ((railcomLastAddressHigh & 0x3f)<<8) | railcomLastAddressLow;
if (addressType==0x00) return railcomLastAddressHigh & 0x3F;
return 0;
}
private: private:
#ifndef ARDUINO_ARCH_ESP32 #ifndef ARDUINO_ARCH_ESP32
@ -131,13 +112,9 @@ class DCCWaveform {
byte pendingPacket[MAX_PACKET_SIZE+1]; // +1 for checksum byte pendingPacket[MAX_PACKET_SIZE+1]; // +1 for checksum
byte pendingLength; byte pendingLength;
byte pendingRepeats; byte pendingRepeats;
static bool railcomPossible; // High accuracy mode only
static volatile bool railcomActive; // switched on by user static volatile bool railcomActive; // switched on by user
static volatile bool railcomDebug; // switched on by user static volatile bool railcomDebug; // switched on by user
static volatile bool railcomSampleWindow; // when safe to sample
static volatile byte railcomCutoutCounter; // incremented for each cutout
static volatile byte railcomLastAddressHigh,railcomLastAddressLow;
static bool cutoutNextTime; // railcom
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
static RMTChannel *rmtMainChannel; static RMTChannel *rmtMainChannel;
static RMTChannel *rmtProgChannel; static RMTChannel *rmtProgChannel;

View File

@ -1,114 +0,0 @@
/*
* © 2021 Neil McKechnie
* © 2021 Mike S
* © 2021 Fred Decker
* © 2020-2022 Harald Barth
* © 2020-2021 Chris Harlow
* All rights reserved.
*
* This file is part of CommandStation-EX
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/
// This code is ESP32 ONLY.
#ifdef ARDUINO_ARCH_ESP32
#include "DCCWaveform.h"
#include "DCCACK.h"
#include "TrackManager.h"
DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true);
DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
RMTChannel *DCCWaveform::rmtMainChannel = NULL;
RMTChannel *DCCWaveform::rmtProgChannel = NULL;
DCCWaveform::DCCWaveform(byte preambleBits, bool isMain) {
isMainTrack = isMain;
requiredPreambles = preambleBits;
}
void DCCWaveform::begin() {
for(const auto& md: TrackManager::getMainDrivers()) {
pinpair p = md->getSignalPin();
if(rmtMainChannel) {
//DIAG(F("added pins %d %d to MAIN channel"), p.pin, p.invpin);
rmtMainChannel->addPin(p); // add pin to existing main channel
} else {
//DIAG(F("new MAIN channel with pins %d %d"), p.pin, p.invpin);
rmtMainChannel = new RMTChannel(p, true); /* create new main channel */
}
}
MotorDriver *md = TrackManager::getProgDriver();
if (md) {
pinpair p = md->getSignalPin();
if (rmtProgChannel) {
//DIAG(F("added pins %d %d to PROG channel"), p.pin, p.invpin);
rmtProgChannel->addPin(p); // add pin to existing prog channel
} else {
//DIAG(F("new PROGchannel with pins %d %d"), p.pin, p.invpin);
rmtProgChannel = new RMTChannel(p, false);
}
}
}
void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) {
if (byteCount > MAX_PACKET_SIZE) return; // allow for chksum
byte checksum = 0;
for (byte b = 0; b < byteCount; b++) {
checksum ^= buffer[b];
pendingPacket[b] = buffer[b];
}
// buffer is MAX_PACKET_SIZE but pendingPacket is one bigger
pendingPacket[byteCount] = checksum;
pendingLength = byteCount + 1;
pendingRepeats = repeats;
// DIAG repeated commands (accesories)
// if (pendingRepeats > 0)
// DIAG(F("Repeats=%d on %s track"), pendingRepeats, isMainTrack ? "MAIN" : "PROG");
// The resets will be zero not only now but as well repeats packets into the future
clearResets(repeats+1);
{
int ret = 0;
do {
if(isMainTrack) {
if (rmtMainChannel != NULL)
ret = rmtMainChannel->RMTfillData(pendingPacket, pendingLength, pendingRepeats);
} else {
if (rmtProgChannel != NULL)
ret = rmtProgChannel->RMTfillData(pendingPacket, pendingLength, pendingRepeats);
}
} while(ret > 0);
}
}
bool DCCWaveform::isReminderWindowOpen() {
if(isMainTrack) {
if (rmtMainChannel == NULL)
return false;
return !rmtMainChannel->busy();
} else {
if (rmtProgChannel == NULL)
return false;
return !rmtProgChannel->busy();
}
}
void IRAM_ATTR DCCWaveform::loop() {
DCCACK::checkAck(progTrack.getResets());
}
bool DCCWaveform::setRailcom(bool on, bool debug) {
// TODO... ESP32 railcom waveform
return false;
}
#endif

View File

@ -88,8 +88,6 @@ LookList * RMFT2::onClockLookup=NULL;
LookList * RMFT2::onRotateLookup=NULL; LookList * RMFT2::onRotateLookup=NULL;
#endif #endif
LookList * RMFT2::onOverloadLookup=NULL; LookList * RMFT2::onOverloadLookup=NULL;
LookList * RMFT2::onBlockEnterLookup=NULL;
LookList * RMFT2::onBlockExitLookup=NULL;
byte * RMFT2::routeStateArray=nullptr; byte * RMFT2::routeStateArray=nullptr;
const FSH * * RMFT2::routeCaptionArray=nullptr; const FSH * * RMFT2::routeCaptionArray=nullptr;
int16_t * RMFT2::stashArray=nullptr; int16_t * RMFT2::stashArray=nullptr;
@ -134,11 +132,11 @@ int16_t LookList::find(int16_t value) {
void LookList::chain(LookList * chain) { void LookList::chain(LookList * chain) {
m_chain=chain; m_chain=chain;
} }
void LookList::handleEvent(const FSH* reason,int16_t id, int16_t loco) { void LookList::handleEvent(const FSH* reason,int16_t id) {
// New feature... create multiple ONhandlers // New feature... create multiple ONhandlers
for (int i=0;i<m_size;i++) for (int i=0;i<m_size;i++)
if (m_lookupArray[i]==id) if (m_lookupArray[i]==id)
RMFT2::startNonRecursiveTask(reason,id,m_resultArray[i],loco); RMFT2::startNonRecursiveTask(reason,id,m_resultArray[i]);
} }
@ -206,12 +204,6 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
onRotateLookup=LookListLoader(OPCODE_ONROTATE); onRotateLookup=LookListLoader(OPCODE_ONROTATE);
#endif #endif
onOverloadLookup=LookListLoader(OPCODE_ONOVERLOAD); onOverloadLookup=LookListLoader(OPCODE_ONOVERLOAD);
if (compileFeatures & FEATURE_BLOCK) {
onBlockEnterLookup=LookListLoader(OPCODE_ONBLOCKENTER);
onBlockExitLookup=LookListLoader(OPCODE_ONBLOCKEXIT);
}
// onLCCLookup is not the same so not loaded here. // onLCCLookup is not the same so not loaded here.
// Second pass startup, define any turnouts or servos, set signals red // Second pass startup, define any turnouts or servos, set signals red
@ -388,7 +380,7 @@ char RMFT2::getRouteType(int16_t id) {
} }
RMFT2::RMFT2(int progCtr, int16_t _loco) { RMFT2::RMFT2(int progCtr) {
progCounter=progCtr; progCounter=progCtr;
// get an unused task id from the flags table // get an unused task id from the flags table
@ -401,7 +393,9 @@ RMFT2::RMFT2(int progCtr, int16_t _loco) {
} }
} }
delayTime=0; delayTime=0;
loco=_loco; loco=0;
speedo=0;
forward=true;
invert=false; invert=false;
blinkState=not_blink_task; blinkState=not_blink_task;
stackDepth=0; stackDepth=0;
@ -419,10 +413,7 @@ RMFT2::RMFT2(int progCtr, int16_t _loco) {
RMFT2::~RMFT2() { RMFT2::~RMFT2() {
// estop my loco if this is not an ONevent driveLoco(1); // ESTOP my loco if any
// (prevents DONE stopping loco at the end of an
// ONBLOCKENTER or ONBLOCKEXIT )
if (loco>0 && this->onEventStartPosition==-1) DCC::setThrottle(loco,1,DCC::getThrottleDirection(loco));
setFlag(taskId,0,TASK_FLAG); // we are no longer using this id setFlag(taskId,0,TASK_FLAG); // we are no longer using this id
if (next==this) if (next==this)
loopTask=NULL; loopTask=NULL;
@ -438,9 +429,23 @@ RMFT2::~RMFT2() {
void RMFT2::createNewTask(int route, uint16_t cab) { void RMFT2::createNewTask(int route, uint16_t cab) {
int pc=routeLookup->find(route); int pc=routeLookup->find(route);
if (pc<0) return; if (pc<0) return;
new RMFT2(pc,cab); RMFT2* task=new RMFT2(pc);
task->loco=cab;
} }
void RMFT2::driveLoco(byte speed) {
if (loco<=0) return; // Prevent broadcast!
//if (diag) DIAG(F("EXRAIL drive %d %d %d"),loco,speed,forward^invert);
/* TODO.....
power on appropriate track if DC or main if dcc
if (TrackManager::getMainPowerMode()==POWERMODE::OFF) {
TrackManager::setMainPower(POWERMODE::ON);
}
**********/
DCC::setThrottle(loco,speed, forward^invert);
speedo=speed;
}
bool RMFT2::readSensor(uint16_t sensorId) { bool RMFT2::readSensor(uint16_t sensorId) {
// Exrail operands are unsigned but we need the signed version as inserted by the macros. // Exrail operands are unsigned but we need the signed version as inserted by the macros.
@ -495,7 +500,7 @@ bool RMFT2::skipIfBlock() {
if (cv & LONG_ADDR_MARKER) { // maker bit indicates long addr if (cv & LONG_ADDR_MARKER) { // maker bit indicates long addr
progtrackLocoId = cv ^ LONG_ADDR_MARKER; // remove marker bit to get real 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 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; progtrackLocoId = -1;
} }
} else { } else {
@ -503,15 +508,6 @@ bool RMFT2::skipIfBlock() {
} }
} }
void RMFT2::pause() {
if (loco)
pauseSpeed=DCC::getThrottleSpeedByte(loco);
}
void RMFT2::resume() {
if (loco)
DCC::setThrottle(loco,pauseSpeed & 0x7f, pauseSpeed & 0x80);
}
void RMFT2::loop() { void RMFT2::loop() {
if (compileFeatures & FEATURE_SENSOR) if (compileFeatures & FEATURE_SENSOR)
EXRAILSensor::checkAll(); EXRAILSensor::checkAll();
@ -576,25 +572,24 @@ void RMFT2::loop2() {
#endif #endif
case OPCODE_REV: case OPCODE_REV:
if (loco) DCC::setThrottle(loco,operand,invert); forward = false;
driveLoco(operand);
break; break;
case OPCODE_FWD: case OPCODE_FWD:
if (loco) DCC::setThrottle(loco,operand,!invert); forward = true;
driveLoco(operand);
break; break;
case OPCODE_SPEED: case OPCODE_SPEED:
if (loco) DCC::setThrottle(loco,operand,DCC::getThrottleDirection(loco)); forward=DCC::getThrottleDirection(loco)^invert;
driveLoco(operand);
break; break;
case OPCODE_MOMENTUM: case OPCODE_MOMENTUM:
DCC::setMomentum(loco,operand,getOperand(1)); DCC::setMomentum(loco,operand,getOperand(1));
break; break;
case OPCODE_ESTOPALL:
DCC::setThrottle(0,1,1); // all locos speed=1
break;
case OPCODE_FORGET: case OPCODE_FORGET:
if (loco!=0) { if (loco!=0) {
DCC::forgetLoco(loco); DCC::forgetLoco(loco);
@ -604,11 +599,12 @@ void RMFT2::loop2() {
case OPCODE_INVERT_DIRECTION: case OPCODE_INVERT_DIRECTION:
invert= !invert; invert= !invert;
driveLoco(speedo);
break; break;
case OPCODE_RESERVE: case OPCODE_RESERVE:
if (getFlag(operand,SECTION_FLAG)) { if (getFlag(operand,SECTION_FLAG)) {
if (loco) DCC::setThrottle(loco,1,DCC::getThrottleDirection(loco)); driveLoco(0);
delayMe(500); delayMe(500);
return; return;
} }
@ -715,10 +711,6 @@ void RMFT2::loop2() {
if (loco) DCC::writeCVByteMain(loco, operand, getOperand(1)); if (loco) DCC::writeCVByteMain(loco, operand, getOperand(1));
break; break;
case OPCODE_XPOM:
DCC::writeCVByteMain(operand, getOperand(1), getOperand(2));
break;
case OPCODE_POWEROFF: case OPCODE_POWEROFF:
TrackManager::setPower(POWERMODE::OFF); TrackManager::setPower(POWERMODE::OFF);
TrackManager::setJoin(false); TrackManager::setJoin(false);
@ -755,8 +747,8 @@ void RMFT2::loop2() {
case OPCODE_RESUME: case OPCODE_RESUME:
pausingTask=NULL; pausingTask=NULL;
resume(); driveLoco(speedo);
for (RMFT2 * t=next; t!=this;t=t->next) t->resume(); for (RMFT2 * t=next; t!=this;t=t->next) if (t->loco >0) t->driveLoco(t->speedo);
break; break;
case OPCODE_IF: // do next operand if sensor set case OPCODE_IF: // do next operand if sensor set
@ -867,7 +859,8 @@ void RMFT2::loop2() {
case OPCODE_DRIVE: case OPCODE_DRIVE:
{ {
// Non functional but reserved byte analogSpeed=IODevice::readAnalogue(operand) *127 / 1024;
if (speedo!=analogSpeed) driveLoco(analogSpeed);
break; break;
} }
@ -965,6 +958,8 @@ void RMFT2::loop2() {
// which is intended so it can be checked // which is intended so it can be checked
// from within EXRAIL // from within EXRAIL
loco=progtrackLocoId; loco=progtrackLocoId;
speedo=0;
forward=true;
invert=false; invert=false;
break; break;
#endif #endif
@ -986,13 +981,16 @@ void RMFT2::loop2() {
{ {
int newPc=routeLookup->find(getOperand(1)); int newPc=routeLookup->find(getOperand(1));
if (newPc<0) break; if (newPc<0) break;
new RMFT2(newPc,operand); // create new task RMFT2* newtask=new RMFT2(newPc); // create new task
newtask->loco=operand;
} }
break; break;
case OPCODE_SETLOCO: case OPCODE_SETLOCO:
{ {
loco=operand; loco=operand;
speedo=0;
forward=true;
invert=false; invert=false;
} }
break; break;
@ -1126,8 +1124,7 @@ void RMFT2::loop2() {
case OPCODE_ONROTATE: case OPCODE_ONROTATE:
#endif #endif
case OPCODE_ONOVERLOAD: case OPCODE_ONOVERLOAD:
case OPCODE_ONBLOCKENTER:
case OPCODE_ONBLOCKEXIT:
break; break;
default: default:
@ -1319,14 +1316,6 @@ void RMFT2::activateEvent(int16_t addr, bool activate) {
else onDeactivateLookup->handleEvent(F("DEACTIVATE"),addr); else onDeactivateLookup->handleEvent(F("DEACTIVATE"),addr);
} }
void RMFT2::blockEvent(int16_t block, int16_t loco, bool entering) {
if (compileFeatures & FEATURE_BLOCK) {
// Hunt for an ONBLOCKENTER/ONBLOCKEXIT for this accessory
if (entering) onBlockEnterLookup->handleEvent(F("BLOCKENTER"),block,loco);
else onBlockExitLookup->handleEvent(F("BLOCKEXIT"),block,loco);
}
}
void RMFT2::changeEvent(int16_t vpin, bool change) { void RMFT2::changeEvent(int16_t vpin, bool change) {
// Hunt for an ONCHANGE for this sensor // Hunt for an ONCHANGE for this sensor
if (change) onChangeLookup->handleEvent(F("CHANGE"),vpin); if (change) onChangeLookup->handleEvent(F("CHANGE"),vpin);
@ -1382,11 +1371,11 @@ void RMFT2::killBlinkOnVpin(VPIN pin, uint16_t count) {
} }
} }
void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc, uint16_t loco) { void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc) {
// Check we dont already have a task running this handler // Check we dont already have a task running this handler
RMFT2 * task=loopTask; RMFT2 * task=loopTask;
while(task) { while(task) {
if (task->onEventStartPosition==pc && task->loco==loco) { if (task->onEventStartPosition==pc) {
DIAG(F("Recursive ON%S(%d)"),reason, id); DIAG(F("Recursive ON%S(%d)"),reason, id);
return; return;
} }
@ -1394,7 +1383,7 @@ void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc, uint16_t
if (task==loopTask) break; if (task==loopTask) break;
} }
task=new RMFT2(pc,loco); // new task starts at this instruction task=new RMFT2(pc); // new task starts at this instruction
task->onEventStartPosition=pc; // flag for recursion detector task->onEventStartPosition=pc; // flag for recursion detector
} }

View File

@ -79,8 +79,6 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,OPCODE_TOGGLE_TURNOUT,
OPCODE_STASH,OPCODE_CLEAR_STASH,OPCODE_CLEAR_ALL_STASH,OPCODE_PICKUP_STASH, OPCODE_STASH,OPCODE_CLEAR_STASH,OPCODE_CLEAR_ALL_STASH,OPCODE_PICKUP_STASH,
OPCODE_ONBUTTON,OPCODE_ONSENSOR, OPCODE_ONBUTTON,OPCODE_ONSENSOR,
OPCODE_NEOPIXEL, OPCODE_NEOPIXEL,
OPCODE_ONBLOCKENTER,OPCODE_ONBLOCKEXIT,
OPCODE_ESTOPALL,OPCODE_XPOM,
// OPcodes below this point are skip-nesting IF operations // OPcodes below this point are skip-nesting IF operations
// placed here so that they may be skipped as a group // placed here so that they may be skipped as a group
// see skipIfBlock() // see skipIfBlock()
@ -140,7 +138,6 @@ enum SignalType {
static const byte FEATURE_STASH = 0x08; static const byte FEATURE_STASH = 0x08;
static const byte FEATURE_BLINK = 0x04; static const byte FEATURE_BLINK = 0x04;
static const byte FEATURE_SENSOR = 0x02; static const byte FEATURE_SENSOR = 0x02;
static const byte FEATURE_BLOCK = 0x01;
// Flag bits for status of hardware and TPL // Flag bits for status of hardware and TPL
@ -167,7 +164,7 @@ class LookList {
int16_t findPosition(int16_t value); // finds index int16_t findPosition(int16_t value); // finds index
int16_t size(); int16_t size();
void stream(Print * _stream); void stream(Print * _stream);
void handleEvent(const FSH* reason,int16_t id, int16_t loco=0); void handleEvent(const FSH* reason,int16_t id);
private: private:
int16_t m_size; int16_t m_size;
@ -181,7 +178,8 @@ class LookList {
public: public:
static void begin(); static void begin();
static void loop(); static void loop();
RMFT2(int progCounter, int16_t cab=0); RMFT2(int progCounter);
RMFT2(int route, uint16_t cab);
~RMFT2(); ~RMFT2();
static void readLocoCallback(int16_t cv); static void readLocoCallback(int16_t cv);
static void createNewTask(int route, uint16_t cab); static void createNewTask(int route, uint16_t cab);
@ -191,7 +189,6 @@ class LookList {
static void clockEvent(int16_t clocktime, bool change); static void clockEvent(int16_t clocktime, bool change);
static void rotateEvent(int16_t id, bool change); static void rotateEvent(int16_t id, bool change);
static void powerEvent(int16_t track, bool overload); static void powerEvent(int16_t track, bool overload);
static void blockEvent(int16_t block, int16_t loco, bool entering);
static bool signalAspectEvent(int16_t address, byte aspect ); static bool signalAspectEvent(int16_t address, byte aspect );
// Throttle Info Access functions built by exrail macros // Throttle Info Access functions built by exrail macros
static const byte rosterNameCount; static const byte rosterNameCount;
@ -205,7 +202,7 @@ class LookList {
static const FSH * getRosterFunctions(int16_t id); static const FSH * getRosterFunctions(int16_t id);
static const FSH * getTurntableDescription(int16_t id); static const FSH * getTurntableDescription(int16_t id);
static const FSH * getTurntablePositionDescription(int16_t turntableId, uint8_t positionId); static const FSH * getTurntablePositionDescription(int16_t turntableId, uint8_t positionId);
static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc, uint16_t loco=0); static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc);
static bool readSensor(uint16_t sensorId); static bool readSensor(uint16_t sensorId);
static bool isSignal(int16_t id,char rag); static bool isSignal(int16_t id,char rag);
static SIGNAL_DEFINITION getSignalSlot(int16_t slotno); static SIGNAL_DEFINITION getSignalSlot(int16_t slotno);
@ -229,6 +226,7 @@ private:
static RMFT2 * loopTask; static RMFT2 * loopTask;
static RMFT2 * pausingTask; static RMFT2 * pausingTask;
void delayMe(long millisecs); void delayMe(long millisecs);
void driveLoco(byte speedo);
bool skipIfBlock(); bool skipIfBlock();
bool readLoco(); bool readLoco();
void loop2(); void loop2();
@ -237,8 +235,6 @@ private:
void printMessage2(const FSH * msg); void printMessage2(const FSH * msg);
void thrungeString(uint32_t strfar, thrunger mode, byte id=0); void thrungeString(uint32_t strfar, thrunger mode, byte id=0);
uint16_t getOperand(byte n); uint16_t getOperand(byte n);
void pause();
void resume();
static bool diag; static bool diag;
static const HIGHFLASH3 byte RouteCode[]; static const HIGHFLASH3 byte RouteCode[];
@ -260,9 +256,6 @@ private:
static LookList * onRotateLookup; static LookList * onRotateLookup;
#endif #endif
static LookList * onOverloadLookup; static LookList * onOverloadLookup;
static LookList * onBlockEnterLookup;
static LookList * onBlockExitLookup;
static const int countLCCLookup; static const int countLCCLookup;
static int onLCCLookup[]; static int onLCCLookup[];
@ -287,8 +280,9 @@ private:
byte taskId; byte taskId;
BlinkState blinkState; // includes AT_TIMEOUT flag. BlinkState blinkState; // includes AT_TIMEOUT flag.
uint16_t loco; uint16_t loco;
bool forward;
bool invert; bool invert;
byte pauseSpeed; byte speedo;
int onEventStartPosition; int onEventStartPosition;
byte stackDepth; byte stackDepth;
int callStack[MAX_STACK_DEPTH]; int callStack[MAX_STACK_DEPTH];

View File

@ -61,7 +61,6 @@
#undef ENDIF #undef ENDIF
#undef ENDTASK #undef ENDTASK
#undef ESTOP #undef ESTOP
#undef ESTOPALL
#undef EXRAIL #undef EXRAIL
#undef EXTT_TURNTABLE #undef EXTT_TURNTABLE
#undef FADE #undef FADE
@ -113,8 +112,6 @@
#undef ONACTIVATE #undef ONACTIVATE
#undef ONACTIVATEL #undef ONACTIVATEL
#undef ONAMBER #undef ONAMBER
#undef ONBLOCKENTER
#undef ONBLOCKEXIT
#undef ONDEACTIVATE #undef ONDEACTIVATE
#undef ONDEACTIVATEL #undef ONDEACTIVATEL
#undef ONCLOSE #undef ONCLOSE
@ -199,7 +196,6 @@
#undef XFOFF #undef XFOFF
#undef XFON #undef XFON
#undef XFTOGGLE #undef XFTOGGLE
#undef XPOM
#undef XREV #undef XREV
#undef XFWD #undef XFWD
@ -240,7 +236,6 @@
#define ENDIF #define ENDIF
#define ENDTASK #define ENDTASK
#define ESTOP #define ESTOP
#define ESTOPALL
#define EXRAIL #define EXRAIL
#define EXTT_TURNTABLE(id,vpin,home,description...) #define EXTT_TURNTABLE(id,vpin,home,description...)
#define FADE(pin,value,ms) #define FADE(pin,value,ms)
@ -291,8 +286,6 @@
#define ONACTIVATE(addr,subaddr) #define ONACTIVATE(addr,subaddr)
#define ONACTIVATEL(linear) #define ONACTIVATEL(linear)
#define ONAMBER(signal_id) #define ONAMBER(signal_id)
#define ONBLOCKENTER(blockid)
#define ONBLOCKEXIT(blockid)
#define ONTIME(value) #define ONTIME(value)
#define ONCLOCKTIME(hours,mins) #define ONCLOCKTIME(hours,mins)
#define ONCLOCKMINS(mins) #define ONCLOCKMINS(mins)
@ -379,5 +372,5 @@
#define XFTOGGLE(cab,func) #define XFTOGGLE(cab,func)
#define XFWD(cab,speed) #define XFWD(cab,speed)
#define XREV(cab,speed) #define XREV(cab,speed)
#define XPOM(cab,cv,value)
#endif #endif

View File

@ -210,15 +210,6 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16
default: default:
break; break;
} }
break;
case 'K': // <K blockid loco> Block enter
case 'k': // <k blockid loco> Block exit
if (paramCount!=2) break;
blockEvent(p[0],p[1],opcode=='K');
opcode=0;
break;
default: // other commands pass through default: // other commands pass through
break; break;
} }
@ -237,9 +228,11 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
); );
} }
else { else {
StringFormatter::send(stream,F("\nID=%d,PC=%d,LOCO=%d %c"), StringFormatter::send(stream,F("\nID=%d,PC=%d,LOCO=%d%c,SPEED=%d%c"),
(int)(task->taskId),task->progCounter,task->loco, (int)(task->taskId),task->progCounter,task->loco,
task->invert?'I':' ' task->invert?'I':' ',
task->speedo,
task->forward?'F':'R'
); );
} }
task=task->next; task=task->next;
@ -283,14 +276,6 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
switch (p[0]) { switch (p[0]) {
case "PAUSE"_hk: // </ PAUSE> case "PAUSE"_hk: // </ PAUSE>
if (paramCount!=1) return false; if (paramCount!=1) return false;
{ // pause all tasks
RMFT2 * task=loopTask;
while(task) {
task->pause();
task=task->next;
if (task==loopTask) break;
}
}
DCC::estopAll(); // pause all locos on the track DCC::estopAll(); // pause all locos on the track
pausingTask=(RMFT2 *)1; // Impossible task address pausingTask=(RMFT2 *)1; // Impossible task address
return true; return true;
@ -298,10 +283,10 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
case "RESUME"_hk: // </ RESUME> case "RESUME"_hk: // </ RESUME>
if (paramCount!=1) return false; if (paramCount!=1) return false;
pausingTask=NULL; pausingTask=NULL;
{ // resume all tasks {
RMFT2 * task=loopTask; RMFT2 * task=loopTask;
while(task) { while(task) {
task->resume(); if (task->loco) task->driveLoco(task->speedo);
task=task->next; task=task->next;
if (task==loopTask) break; if (task==loopTask) break;
} }
@ -316,7 +301,8 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
uint16_t cab=(paramCount==2)? 0 : p[1]; uint16_t cab=(paramCount==2)? 0 : p[1];
int pc=routeLookup->find(route); int pc=routeLookup->find(route);
if (pc<0) return false; if (pc<0) return false;
new RMFT2(pc,cab); RMFT2* task=new RMFT2(pc);
task->loco=cab;
} }
return true; return true;

View File

@ -231,10 +231,6 @@ bool exrailHalSetup() {
#define ONBUTTON(vpin) | FEATURE_SENSOR #define ONBUTTON(vpin) | FEATURE_SENSOR
#undef ONSENSOR #undef ONSENSOR
#define ONSENSOR(vpin) | FEATURE_SENSOR #define ONSENSOR(vpin) | FEATURE_SENSOR
#undef ONBLOCKENTER
#define ONBLOCKENTER(blockid) | FEATURE_BLOCK
#undef ONBLOCKEXIT
#define ONBLOCKEXIT(blockid) | FEATURE_BLOCK
const byte RMFT2::compileFeatures = 0 const byte RMFT2::compileFeatures = 0
#include "myAutomation.h" #include "myAutomation.h"
@ -517,7 +513,6 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define ENDIF OPCODE_ENDIF,0,0, #define ENDIF OPCODE_ENDIF,0,0,
#define ENDTASK OPCODE_ENDTASK,0,0, #define ENDTASK OPCODE_ENDTASK,0,0,
#define ESTOP OPCODE_SPEED,V(1), #define ESTOP OPCODE_SPEED,V(1),
#define ESTOPALL OPCODE_ESTOPALL,0,0,
#define EXRAIL #define EXRAIL
#ifndef IO_NO_HAL #ifndef IO_NO_HAL
#define EXTT_TURNTABLE(id,vpin,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(home), #define EXTT_TURNTABLE(id,vpin,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(home),
@ -581,8 +576,6 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr), #define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr),
#define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3), #define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3),
#define ONAMBER(signal_id) OPCODE_ONAMBER,V(signal_id), #define ONAMBER(signal_id) OPCODE_ONAMBER,V(signal_id),
#define ONBLOCKENTER(block_id) OPCODE_ONBLOCKENTER,V(block_id),
#define ONBLOCKEXIT(block_id) OPCODE_ONBLOCKEXIT,V(block_id),
#define ONCLOSE(turnout_id) OPCODE_ONCLOSE,V(turnout_id), #define ONCLOSE(turnout_id) OPCODE_ONCLOSE,V(turnout_id),
#define ONLCC(sender,event) OPCODE_ONLCC,V(event),\ #define ONLCC(sender,event) OPCODE_ONLCC,V(event),\
OPCODE_PAD,V((((uint64_t)sender)>>32)&0xFFFF),\ OPCODE_PAD,V((((uint64_t)sender)>>32)&0xFFFF),\
@ -676,7 +669,6 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define XFTOGGLE(cab,func) OPCODE_XFTOGGLE,V(cab),OPCODE_PAD,V(func), #define XFTOGGLE(cab,func) OPCODE_XFTOGGLE,V(cab),OPCODE_PAD,V(func),
#define XFWD(cab,speed) OPCODE_XFWD,V(cab),OPCODE_PAD,V(speed), #define XFWD(cab,speed) OPCODE_XFWD,V(cab),OPCODE_PAD,V(speed),
#define XREV(cab,speed) OPCODE_XREV,V(cab),OPCODE_PAD,V(speed), #define XREV(cab,speed) OPCODE_XREV,V(cab),OPCODE_PAD,V(speed),
#define XPOM(cab,cv,value) OPCODE_XPOM,V(cab),OPCODE_PAD,V(cv),OPCODE_PAD,V(value),
// Build RouteCode // Build RouteCode
const int StringMacroTracker2=__COUNTER__; const int StringMacroTracker2=__COUNTER__;

View File

@ -1,5 +1,5 @@
/* /*
* © 2020=2025, Chris Harlow. All rights reserved. * © 2020, Chris Harlow. All rights reserved.
* *
* This file is part of Asbelos DCC API * This file is part of Asbelos DCC API
* *
@ -27,8 +27,6 @@ bool Diag::WIFI=false;
bool Diag::WITHROTTLE=false; bool Diag::WITHROTTLE=false;
bool Diag::ETHERNET=false; bool Diag::ETHERNET=false;
bool Diag::LCN=false; bool Diag::LCN=false;
bool Diag::RAILCOM=false;
void StringFormatter::diag( const FSH* input...) { void StringFormatter::diag( const FSH* input...) {

View File

@ -1,5 +1,5 @@
/* /*
* © 2020-2025, Chris Harlow. All rights reserved. * © 2020, Chris Harlow. All rights reserved.
* *
* This file is part of Asbelos DCC API * This file is part of Asbelos DCC API
* *
@ -30,7 +30,6 @@ class Diag {
static bool WITHROTTLE; static bool WITHROTTLE;
static bool ETHERNET; static bool ETHERNET;
static bool LCN; static bool LCN;
static bool RAILCOM;
}; };

View File

@ -332,8 +332,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
canDo &= track[t]->trackPWM; canDo &= track[t]->trackPWM;
} }
} }
if (canDo) DIAG(F("HA mode")); if (!canDo) {
else {
// if we discover that HA mode was globally impossible // if we discover that HA mode was globally impossible
// we must adjust the trackPWM capabilities // we must adjust the trackPWM capabilities
FOR_EACH_TRACK(t) { FOR_EACH_TRACK(t) {
@ -342,7 +341,6 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
} }
DCCTimer::clearPWM(); // has to be AFTER trackPWM changes because if trackPWM==true this is undone for that track DCCTimer::clearPWM(); // has to be AFTER trackPWM changes because if trackPWM==true this is undone for that track
} }
DCCWaveform::setRailcomPossible(canDo);
#else #else
// For ESP32 we just reinitialize the DCC Waveform // For ESP32 we just reinitialize the DCC Waveform
DCCWaveform::begin(); DCCWaveform::begin();

View File

@ -3,13 +3,7 @@
#include "StringFormatter.h" #include "StringFormatter.h"
#define VERSION "5.5.4" #define VERSION "5.5.2"
// 5.5.4 - Split ESP32 from DCCWaveform to DCCWaveformRMT
// - Railcom Cutout control (DCCTimerAVR Mega only so far)
// 5.5.3 - EXRAIL ESTOPALL,XPOM,
// - Bugfix RESERVE to cause ESTOP.(was STOP)
// - Correct direction sync after manual throttle change.
// - plus ONBLOCKENTER/EXIT in preparation for Railcom
// 5.5.2 - DS1307 Real Time clock // 5.5.2 - DS1307 Real Time clock
// 5.5.1 - Momentum // 5.5.1 - Momentum
// 5.5.0 - New version on devel // 5.5.0 - New version on devel