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.
dc84f560e6
...
f031add7a0
@ -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;
|
||||||
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
150
DCCWaveform.cpp
150
DCCWaveform.cpp
@ -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,7 +171,13 @@ 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#pragma GCC pop_options
|
#pragma GCC pop_options
|
||||||
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
|
103
EXRAIL2.cpp
103
EXRAIL2.cpp
@ -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;
|
||||||
break;
|
driveLoco(operand);
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
20
EXRAIL2.h
20
EXRAIL2.h
@ -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];
|
||||||
|
@ -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
|
||||||
|
@ -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,12 +283,12 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -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;
|
||||||
|
|
||||||
|
@ -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__;
|
||||||
|
@ -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...) {
|
||||||
|
@ -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;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user