mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-23 16:16:13 +01:00
Merge pull request #399 from DCC-EX/devel-Ash
Update recent changes from devel branch
This commit is contained in:
commit
ab1a13775e
|
@ -312,6 +312,11 @@ void CommandDistributor::broadcastRaw(clientType type, char * msg) {
|
||||||
broadcastReply(type, F("%s"),msg);
|
broadcastReply(type, F("%s"),msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandDistributor::broadcastMessage(char * message) {
|
||||||
|
broadcastReply(COMMAND_TYPE, F("<m \"%s\">\n"),message);
|
||||||
|
broadcastReply(WITHROTTLE_TYPE, F("Hm%s\n"),message);
|
||||||
|
}
|
||||||
|
|
||||||
void CommandDistributor::broadcastTrackState(const FSH* format, byte trackLetter, const FSH *modename, int16_t dcAddr) {
|
void CommandDistributor::broadcastTrackState(const FSH* format, byte trackLetter, const FSH *modename, int16_t dcAddr) {
|
||||||
broadcastReply(COMMAND_TYPE, format, trackLetter, modename, dcAddr);
|
broadcastReply(COMMAND_TYPE, format, trackLetter, modename, dcAddr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ public :
|
||||||
static void forget(byte clientId);
|
static void forget(byte clientId);
|
||||||
static void broadcastRouteState(uint16_t routeId,byte state);
|
static void broadcastRouteState(uint16_t routeId,byte state);
|
||||||
static void broadcastRouteCaption(uint16_t routeId,const FSH * caption);
|
static void broadcastRouteCaption(uint16_t routeId,const FSH * caption);
|
||||||
|
static void broadcastMessage(char * message);
|
||||||
|
|
||||||
// Handling code for virtual LCD receiver.
|
// Handling code for virtual LCD receiver.
|
||||||
static Print * getVirtualLCDSerial(byte screen, byte row);
|
static Print * getVirtualLCDSerial(byte screen, byte row);
|
||||||
|
|
|
@ -65,6 +65,9 @@
|
||||||
#ifdef EXRAIL_WARNING
|
#ifdef EXRAIL_WARNING
|
||||||
#warning You have myAutomation.h but your hardware has not enough memory to do that, so EX-RAIL DISABLED
|
#warning You have myAutomation.h but your hardware has not enough memory to do that, so EX-RAIL DISABLED
|
||||||
#endif
|
#endif
|
||||||
|
// compile time check, passwords 1 to 7 chars do not work, so do not try to compile with them at all
|
||||||
|
// remember trailing '\0', sizeof("") == 1.
|
||||||
|
#define PASSWDCHECK(S) static_assert(sizeof(S) == 1 || sizeof(S) > 8, "Password shorter than 8 chars")
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
|
@ -102,10 +105,12 @@ void setup()
|
||||||
// Start Ethernet if it exists
|
// Start Ethernet if it exists
|
||||||
#ifndef ARDUINO_ARCH_ESP32
|
#ifndef ARDUINO_ARCH_ESP32
|
||||||
#if WIFI_ON
|
#if WIFI_ON
|
||||||
|
PASSWDCHECK(WIFI_PASSWORD); // compile time check
|
||||||
WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
|
WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
|
||||||
#endif // WIFI_ON
|
#endif // WIFI_ON
|
||||||
#else
|
#else
|
||||||
// ESP32 needs wifi on always
|
// ESP32 needs wifi on always
|
||||||
|
PASSWDCHECK(WIFI_PASSWORD); // compile time check
|
||||||
WifiESP::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
|
WifiESP::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
|
||||||
#endif // ARDUINO_ARCH_ESP32
|
#endif // ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
|
|
51
DCC.cpp
51
DCC.cpp
|
@ -305,6 +305,57 @@ void DCC::setAccessory(int address, byte port, bool gate, byte onoff /*= 2*/) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DCC::setExtendedAccessory(int16_t address, int16_t value, byte repeats) {
|
||||||
|
|
||||||
|
/* From https://www.nmra.org/sites/default/files/s-9.2.1_2012_07.pdf
|
||||||
|
|
||||||
|
The Extended Accessory Decoder Control Packet is included for the purpose of transmitting aspect control to signal
|
||||||
|
decoders or data bytes to more complex accessory decoders. Each signal head can display one aspect at a time.
|
||||||
|
{preamble} 0 10AAAAAA 0 0AAA0AA1 0 000XXXXX 0 EEEEEEEE 1
|
||||||
|
|
||||||
|
XXXXX is for a single head. A value of 00000 for XXXXX indicates the absolute stop aspect. All other aspects
|
||||||
|
represented by the values for XXXXX are determined by the signaling system used and the prototype being
|
||||||
|
modeled.
|
||||||
|
|
||||||
|
From https://normen.railcommunity.de/RCN-213.pdf:
|
||||||
|
|
||||||
|
More information is in RCN-213 about how the address bits are organized.
|
||||||
|
preamble -0- 1 0 A7 A6 A5 A4 A3 A2 -0- 0 ^A10 ^A9 ^A8 0 A1 A0 1 -0- ....
|
||||||
|
|
||||||
|
Thus in byte packet form the format is 10AAAAAA, 0AAA0AA1, 000XXXXX
|
||||||
|
|
||||||
|
Die Adresse für den ersten erweiterten Zubehördecoder ist wie bei den einfachen
|
||||||
|
Zubehördecodern die Adresse 4 = 1000-0001 0111-0001 . Diese Adresse wird in
|
||||||
|
Anwenderdialogen als Adresse 1 dargestellt.
|
||||||
|
|
||||||
|
This means that the first address shown to the user as "1" is mapped
|
||||||
|
to internal address 4.
|
||||||
|
|
||||||
|
Note that the Basic accessory format mentions "By convention these
|
||||||
|
bits (bits 4-6 of the second data byte) are in ones complement" but
|
||||||
|
this note is absent from the advanced packet description. The
|
||||||
|
english translation does not mention that the address format for
|
||||||
|
the advanced packet follows the one for the basic packet but
|
||||||
|
according to the RCN-213 this is the case.
|
||||||
|
|
||||||
|
We allow for addresses from -3 to 2047-3 as that allows to address the
|
||||||
|
whole range of the 11 bits sent to track.
|
||||||
|
*/
|
||||||
|
if ((address > 2044) || (address < -3)) return false; // 2047-3, 11 bits but offset 3
|
||||||
|
if (value != (value & 0x1F)) return false; // 5 bits
|
||||||
|
|
||||||
|
address+=3; // +3 offset according to RCN-213
|
||||||
|
byte b[3];
|
||||||
|
b[0]= 0x80 // bits always on
|
||||||
|
| ((address>>2) & 0x3F); // shift out 2, mask out used bits
|
||||||
|
b[1]= 0x01 // bits always on
|
||||||
|
| (((~(address>>8)) & 0x07)<<4) // shift out 8, invert, mask 3 bits, shift up 4
|
||||||
|
| ((address & 0x03)<<1); // mask 2 bits, shift up 1
|
||||||
|
b[2]=value;
|
||||||
|
DCCWaveform::mainTrack.schedulePacket(b, sizeof(b), repeats);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// writeCVByteMain: Write a byte with PoM on main. This writes
|
// writeCVByteMain: Write a byte with PoM on main. This writes
|
||||||
// the 5 byte sized packet to implement this DCC function
|
// the 5 byte sized packet to implement this DCC function
|
||||||
|
|
1
DCC.h
1
DCC.h
|
@ -72,6 +72,7 @@ public:
|
||||||
static uint32_t getFunctionMap(int cab);
|
static uint32_t getFunctionMap(int cab);
|
||||||
static void updateGroupflags(byte &flags, int16_t functionNumber);
|
static void updateGroupflags(byte &flags, int16_t functionNumber);
|
||||||
static void setAccessory(int address, byte port, bool gate, byte onoff = 2);
|
static void setAccessory(int address, byte port, bool gate, byte onoff = 2);
|
||||||
|
static bool setExtendedAccessory(int16_t address, int16_t value, byte repeats=3);
|
||||||
static bool writeTextPacket(byte *b, int nBytes);
|
static bool writeTextPacket(byte *b, int nBytes);
|
||||||
|
|
||||||
// ACKable progtrack calls bitresults callback 0,0 or -1, cv returns value or -1
|
// ACKable progtrack calls bitresults callback 0,0 or -1, cv returns value or -1
|
||||||
|
|
|
@ -45,7 +45,7 @@ Once a new OPCODE is decided upon, update this list.
|
||||||
0, Track power off
|
0, Track power off
|
||||||
1, Track power on
|
1, Track power on
|
||||||
a, DCC accessory control
|
a, DCC accessory control
|
||||||
A,
|
A, DCC extended accessory control
|
||||||
b, Write CV bit on main
|
b, Write CV bit on main
|
||||||
B, Write CV bit
|
B, Write CV bit
|
||||||
c, Request current command
|
c, Request current command
|
||||||
|
@ -385,6 +385,13 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case 'A': // EXTENDED ACCESSORY <A address value>
|
||||||
|
// Note: if this happens to match a defined EXRAIL
|
||||||
|
// DCCX_SIGNAL, then EXRAIL will have intercepted
|
||||||
|
// this command alrerady.
|
||||||
|
if (params==2 && DCC::setExtendedAccessory(p[0],p[1])) return;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'T': // TURNOUT <T ...>
|
case 'T': // TURNOUT <T ...>
|
||||||
if (parseT(stream, params, p))
|
if (parseT(stream, params, p))
|
||||||
return;
|
return;
|
||||||
|
@ -1036,7 +1043,32 @@ bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) {
|
||||||
DCC::setGlobalSpeedsteps(128);
|
DCC::setGlobalSpeedsteps(128);
|
||||||
DIAG(F("128 Speedsteps"));
|
DIAG(F("128 Speedsteps"));
|
||||||
return true;
|
return true;
|
||||||
|
#if defined(HAS_ENOUGH_MEMORY) && !defined(ARDUINO_ARCH_UNO)
|
||||||
|
case "RAILCOM"_hk:
|
||||||
|
{ // <C RAILCOM ON|OFF|DEBUG >
|
||||||
|
if (params<2) return false;
|
||||||
|
bool on=false;
|
||||||
|
bool debug=false;
|
||||||
|
switch (p[1]) {
|
||||||
|
case "ON"_hk:
|
||||||
|
case 1:
|
||||||
|
on=true;
|
||||||
|
break;
|
||||||
|
case "DEBUG"_hk:
|
||||||
|
on=true;
|
||||||
|
debug=true;
|
||||||
|
break;
|
||||||
|
case "OFF"_hk:
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DIAG(F("Railcom %S")
|
||||||
|
,DCCWaveform::setRailcom(on,debug)?F("ON"):F("OFF"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifndef DISABLE_PROG
|
#ifndef DISABLE_PROG
|
||||||
case "ACK"_hk: // <D ACK ON/OFF> <D ACK [LIMIT|MIN|MAX|RETRY] Value>
|
case "ACK"_hk: // <D ACK ON/OFF> <D ACK [LIMIT|MIN|MAX|RETRY] Value>
|
||||||
if (params >= 3) {
|
if (params >= 3) {
|
||||||
|
|
|
@ -62,6 +62,8 @@ class DCCTimer {
|
||||||
static bool isPWMPin(byte pin);
|
static bool isPWMPin(byte pin);
|
||||||
static void setPWM(byte pin, bool high);
|
static void setPWM(byte pin, bool high);
|
||||||
static void clearPWM();
|
static void clearPWM();
|
||||||
|
static void startRailcomTimer(byte brakePin);
|
||||||
|
static void ackRailcomTimer();
|
||||||
static void DCCEXanalogWriteFrequency(uint8_t pin, uint32_t frequency);
|
static void DCCEXanalogWriteFrequency(uint8_t pin, uint32_t frequency);
|
||||||
static void DCCEXanalogWrite(uint8_t pin, int value);
|
static void DCCEXanalogWrite(uint8_t pin, int value);
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,9 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
||||||
#define TIMER1_A_PIN 11
|
#define TIMER1_A_PIN 11
|
||||||
#define TIMER1_B_PIN 12
|
#define TIMER1_B_PIN 12
|
||||||
#define TIMER1_C_PIN 13
|
#define TIMER1_C_PIN 13
|
||||||
|
#define TIMER2_A_PIN 10
|
||||||
|
#define TIMER2_B_PIN 9
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define TIMER1_A_PIN 9
|
#define TIMER1_A_PIN 9
|
||||||
#define TIMER1_B_PIN 10
|
#define TIMER1_B_PIN 10
|
||||||
|
@ -56,6 +59,67 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
||||||
interrupts();
|
interrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DCCTimer::startRailcomTimer(byte brakePin) {
|
||||||
|
/* The Railcom timer is started in such a way that it
|
||||||
|
- First triggers 28uS after the last TIMER1 tick.
|
||||||
|
This provides an accurate offset (in High Accuracy mode)
|
||||||
|
for the start of the Railcom cutout.
|
||||||
|
- Sets the Railcom pin high at first tick,
|
||||||
|
because its been setup with 100% PWM duty cycle.
|
||||||
|
|
||||||
|
- Cycles at 436uS so the second tick is the
|
||||||
|
correct distance from the cutout.
|
||||||
|
|
||||||
|
- Waveform code is responsible for altering the PWM
|
||||||
|
duty cycle to 0% any time between the first and last tick.
|
||||||
|
(there will be 7 DCC timer1 ticks in which to do this.)
|
||||||
|
|
||||||
|
*/
|
||||||
|
(void) brakePin; // Ignored... works on pin 9 only
|
||||||
|
const int cutoutDuration = 430; // Desired interval in microseconds
|
||||||
|
|
||||||
|
// Set up Timer2 for CTC mode (Clear Timer on Compare Match)
|
||||||
|
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)
|
||||||
|
|
||||||
|
// Enable Timer2 output on pin 9 (OC2B)
|
||||||
|
DDRB |= (1 << DDB1);
|
||||||
|
// TODO Fudge TCNT2 to sync with last tcnt1 tick + 28uS
|
||||||
|
|
||||||
|
// Previous TIMER1 Tick was at rising end-of-packet bit
|
||||||
|
// Cutout starts half way through first preamble
|
||||||
|
// that is 2.5 * 58uS later.
|
||||||
|
// TCNT1 ticks 8 times / microsecond
|
||||||
|
// auto microsendsToFirstRailcomTick=(58+58+29)-(TCNT1/8);
|
||||||
|
// set the railcom timer counter allowing for phase-correct
|
||||||
|
|
||||||
|
// CHris's NOTE:
|
||||||
|
// I dont kniow quite how this calculation works out but
|
||||||
|
// it does seems to get a good answer.
|
||||||
|
|
||||||
|
TCNT2=193 + (ICR1 - TCNT1)/8;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCTimer::ackRailcomTimer() {
|
||||||
|
OCR2B= 0x00; // brake pin pwm duty cycle 0 at next tick
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ISR called by timer interrupt every 58uS
|
// ISR called by timer interrupt every 58uS
|
||||||
ISR(TIMER1_OVF_vect){ interruptHandler(); }
|
ISR(TIMER1_OVF_vect){ interruptHandler(); }
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,15 @@ extern char *__malloc_heap_start;
|
||||||
interruptHandler();
|
interruptHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DCCTimer::startRailcomTimer(byte brakePin) {
|
||||||
|
// TODO: for intended operation see DCCTimerAVR.cpp
|
||||||
|
(void) brakePin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCTimer::ackRailcomTimer() {
|
||||||
|
// TODO: for intended operation see DCCTimerAVR.cpp
|
||||||
|
}
|
||||||
|
|
||||||
bool DCCTimer::isPWMPin(byte pin) {
|
bool DCCTimer::isPWMPin(byte pin) {
|
||||||
(void) pin;
|
(void) pin;
|
||||||
return false; // TODO what are the relevant pins?
|
return false; // TODO what are the relevant pins?
|
||||||
|
|
|
@ -76,6 +76,15 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
||||||
interrupts();
|
interrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DCCTimer::startRailcomTimer(byte brakePin) {
|
||||||
|
// TODO: for intended operation see DCCTimerAVR.cpp
|
||||||
|
(void) brakePin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCTimer::ackRailcomTimer() {
|
||||||
|
// TODO: for intended operation see DCCTimerAVR.cpp
|
||||||
|
}
|
||||||
|
|
||||||
// Timer IRQ handlers replace the dummy handlers (in cortex_handlers)
|
// Timer IRQ handlers replace the dummy handlers (in cortex_handlers)
|
||||||
// copied from rf24 branch
|
// copied from rf24 branch
|
||||||
void TCC0_Handler() {
|
void TCC0_Handler() {
|
||||||
|
|
|
@ -201,6 +201,15 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
||||||
interrupts();
|
interrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DCCTimer::startRailcomTimer(byte brakePin) {
|
||||||
|
// TODO: for intended operation see DCCTimerAVR.cpp
|
||||||
|
(void) brakePin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCTimer::ackRailcomTimer() {
|
||||||
|
// TODO: for intended operation see DCCTimerAVR.cpp
|
||||||
|
}
|
||||||
|
|
||||||
bool DCCTimer::isPWMPin(byte pin) {
|
bool DCCTimer::isPWMPin(byte pin) {
|
||||||
//TODO: STM32 whilst this call to digitalPinHasPWM will reveal which pins can do PWM,
|
//TODO: STM32 whilst this call to digitalPinHasPWM will reveal which pins can do PWM,
|
||||||
// there's no support yet for High Accuracy, so for now return false
|
// there's no support yet for High Accuracy, so for now return false
|
||||||
|
|
|
@ -39,6 +39,15 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
||||||
myDCCTimer.begin(interruptHandler, DCC_SIGNAL_TIME);
|
myDCCTimer.begin(interruptHandler, DCC_SIGNAL_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DCCTimer::startRailcomTimer(byte brakePin) {
|
||||||
|
// TODO: for intended operation see DCCTimerAVR.cpp
|
||||||
|
(void) brakePin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCTimer::ackRailcomTimer() {
|
||||||
|
// TODO: for intended operation see DCCTimerAVR.cpp
|
||||||
|
}
|
||||||
|
|
||||||
bool DCCTimer::isPWMPin(byte pin) {
|
bool DCCTimer::isPWMPin(byte pin) {
|
||||||
//Teensy: digitalPinHasPWM, todo
|
//Teensy: digitalPinHasPWM, todo
|
||||||
(void) pin;
|
(void) pin;
|
||||||
|
|
|
@ -116,7 +116,21 @@ DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) {
|
||||||
bits_sent = 0;
|
bits_sent = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
volatile bool DCCWaveform::railcomActive=false; // switched on by user
|
||||||
|
volatile bool DCCWaveform::railcomDebug=false; // switched on by user
|
||||||
|
|
||||||
|
bool DCCWaveform::setRailcom(bool on, bool debug) {
|
||||||
|
if (on) {
|
||||||
|
// TODO check possible
|
||||||
|
railcomActive=true;
|
||||||
|
railcomDebug=debug;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
railcomActive=false;
|
||||||
|
railcomDebug=false;
|
||||||
|
}
|
||||||
|
return railcomActive;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma GCC push_options
|
#pragma GCC push_options
|
||||||
#pragma GCC optimize ("-O3")
|
#pragma GCC optimize ("-O3")
|
||||||
|
@ -124,7 +138,6 @@ void DCCWaveform::interrupt2() {
|
||||||
// calculate the next bit to be sent:
|
// calculate the next bit to be sent:
|
||||||
// set state WAVE_MID_1 for a 1=bit
|
// set state WAVE_MID_1 for a 1=bit
|
||||||
// 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--;
|
||||||
|
@ -134,6 +147,7 @@ void DCCWaveform::interrupt2() {
|
||||||
// that the reminder doesn't block a more urgent packet.
|
// that the reminder doesn't block a more urgent packet.
|
||||||
reminderWindowOpen=transmitRepeats==0 && remainingPreambles<4 && remainingPreambles>1;
|
reminderWindowOpen=transmitRepeats==0 && remainingPreambles<4 && remainingPreambles>1;
|
||||||
if (remainingPreambles==1) promotePendingPacket();
|
if (remainingPreambles==1) promotePendingPacket();
|
||||||
|
else if (remainingPreambles==10 && isMainTrack && railcomActive) DCCTimer::ackRailcomTimer();
|
||||||
// Update free memory diagnostic as we don't have anything else to do this time.
|
// 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);
|
||||||
|
@ -157,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,7 +228,11 @@ void DCCWaveform::promotePendingPacket() {
|
||||||
|
|
||||||
// nothing to do, just send idles or resets
|
// nothing to do, just send idles or resets
|
||||||
// Fortunately reset and idle packets are the same length
|
// Fortunately reset and idle packets are the same length
|
||||||
memcpy( transmitPacket, isMainTrack ? idlePacket : resetPacket, sizeof(idlePacket));
|
// Note: If railcomDebug is on, then we send resets to the main
|
||||||
|
// track instead of idles. This means that all data will be zeros
|
||||||
|
// and only the porersets will be ones, making it much
|
||||||
|
// easier to read on a logic analyser.
|
||||||
|
memcpy( transmitPacket, (isMainTrack && (!railcomDebug)) ? idlePacket : resetPacket, sizeof(idlePacket));
|
||||||
transmitLength = sizeof(idlePacket);
|
transmitLength = sizeof(idlePacket);
|
||||||
transmitRepeats = 0;
|
transmitRepeats = 0;
|
||||||
if (getResets() < 250) sentResetsSincePacket++; // only place to increment (private!)
|
if (getResets() < 250) sentResetsSincePacket++; // only place to increment (private!)
|
||||||
|
@ -297,4 +321,10 @@ bool DCCWaveform::isReminderWindowOpen() {
|
||||||
void IRAM_ATTR DCCWaveform::loop() {
|
void IRAM_ATTR DCCWaveform::loop() {
|
||||||
DCCACK::checkAck(progTrack.getResets());
|
DCCACK::checkAck(progTrack.getResets());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DCCWaveform::setRailcom(bool on, bool debug) {
|
||||||
|
// TODO... ESP32 railcom waveform
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -40,7 +40,14 @@ const byte MAX_PACKET_SIZE = 5; // NMRA standard extended packets, payload s
|
||||||
|
|
||||||
// The WAVE_STATE enum is deliberately numbered because a change of order would be catastrophic
|
// The WAVE_STATE enum is deliberately numbered because a change of order would be catastrophic
|
||||||
// to the transform array.
|
// to the transform array.
|
||||||
enum WAVE_STATE : byte {WAVE_START=0,WAVE_MID_1=1,WAVE_HIGH_0=2,WAVE_MID_0=3,WAVE_LOW_0=4,WAVE_PENDING=5};
|
enum WAVE_STATE : byte {
|
||||||
|
WAVE_START=0, // wave going high at start of bit
|
||||||
|
WAVE_MID_1=1, // middle of 1 bit
|
||||||
|
WAVE_HIGH_0=2, // first part of 0 bit high
|
||||||
|
WAVE_MID_0=3, // middle of 0 bit
|
||||||
|
WAVE_LOW_0=4, // first part of 0 bit low
|
||||||
|
WAVE_PENDING=5 // next bit not yet known
|
||||||
|
};
|
||||||
|
|
||||||
// NOTE: static functions are used for the overall controller, then
|
// NOTE: static functions are used for the overall controller, then
|
||||||
// one instance is created for each track.
|
// one instance is created for each track.
|
||||||
|
@ -78,6 +85,8 @@ class DCCWaveform {
|
||||||
void schedulePacket(const byte buffer[], byte byteCount, byte repeats);
|
void schedulePacket(const byte buffer[], byte byteCount, byte repeats);
|
||||||
bool isReminderWindowOpen();
|
bool isReminderWindowOpen();
|
||||||
void promotePendingPacket();
|
void promotePendingPacket();
|
||||||
|
static bool setRailcom(bool on, bool debug);
|
||||||
|
static bool isRailcom() {return railcomActive;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifndef ARDUINO_ARCH_ESP32
|
#ifndef ARDUINO_ARCH_ESP32
|
||||||
|
@ -103,6 +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 volatile bool railcomActive; // switched on by user
|
||||||
|
static volatile bool railcomDebug; // switched on by user
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
static RMTChannel *rmtMainChannel;
|
static RMTChannel *rmtMainChannel;
|
||||||
static RMTChannel *rmtProgChannel;
|
static RMTChannel *rmtProgChannel;
|
||||||
|
|
56
EXRAIL2.cpp
56
EXRAIL2.cpp
|
@ -839,6 +839,14 @@ void RMFT2::loop2() {
|
||||||
DCC::setAccessory(addr,subaddr,active);
|
DCC::setAccessory(addr,subaddr,active);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OPCODE_ASPECT: {
|
||||||
|
// operand is address<<5 | value
|
||||||
|
int16_t address=operand>>5;
|
||||||
|
byte aspect=operand & 0x1f;
|
||||||
|
if (!signalAspectEvent(address,aspect))
|
||||||
|
DCC::setExtendedAccessory(address,aspect);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case OPCODE_FOLLOW:
|
case OPCODE_FOLLOW:
|
||||||
progCounter=routeLookup->find(operand);
|
progCounter=routeLookup->find(operand);
|
||||||
|
@ -1100,7 +1108,7 @@ int16_t RMFT2::getSignalSlot(int16_t id) {
|
||||||
if (diag) DIAG(F(" doSignal %d %x"),id,rag);
|
if (diag) DIAG(F(" doSignal %d %x"),id,rag);
|
||||||
|
|
||||||
// Schedule any event handler for this signal change.
|
// Schedule any event handler for this signal change.
|
||||||
// Thjis will work even without a signal definition.
|
// This will work even without a signal definition.
|
||||||
if (rag==SIGNAL_RED) onRedLookup->handleEvent(F("RED"),id);
|
if (rag==SIGNAL_RED) onRedLookup->handleEvent(F("RED"),id);
|
||||||
else if (rag==SIGNAL_GREEN) onGreenLookup->handleEvent(F("GREEN"),id);
|
else if (rag==SIGNAL_GREEN) onGreenLookup->handleEvent(F("GREEN"),id);
|
||||||
else onAmberLookup->handleEvent(F("AMBER"),id);
|
else onAmberLookup->handleEvent(F("AMBER"),id);
|
||||||
|
@ -1137,6 +1145,16 @@ int16_t RMFT2::getSignalSlot(int16_t id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sigtype== DCCX_SIGNAL_FLAG) {
|
||||||
|
// redpin,amberpin,greenpin are the 3 aspects
|
||||||
|
byte value=redpin;
|
||||||
|
if (rag==SIGNAL_AMBER) value=amberpin;
|
||||||
|
if (rag==SIGNAL_GREEN) value=greenpin;
|
||||||
|
DCC::setExtendedAccessory(sigid & SIGNAL_ID_MASK,value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// LED or similar 3 pin signal, (all pins zero would be a virtual signal)
|
// LED or similar 3 pin signal, (all pins zero would be a virtual signal)
|
||||||
// If amberpin is zero, synthesise amber from red+green
|
// If amberpin is zero, synthesise amber from red+green
|
||||||
const byte SIMAMBER=0x00;
|
const byte SIMAMBER=0x00;
|
||||||
|
@ -1170,6 +1188,38 @@ int16_t RMFT2::getSignalSlot(int16_t id) {
|
||||||
return (flags[sigslot] & SIGNAL_MASK) == rag;
|
return (flags[sigslot] & SIGNAL_MASK) == rag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// signalAspectEvent returns true if the aspect is destined
|
||||||
|
// for a defined DCCX_SIGNAL which will handle all the RAG flags
|
||||||
|
// and ON* handlers.
|
||||||
|
// Otherwise false so the parser should send the command directly
|
||||||
|
bool RMFT2::signalAspectEvent(int16_t address, byte aspect ) {
|
||||||
|
if (!(compileFeatures & FEATURE_SIGNAL)) return false;
|
||||||
|
int16_t sigslot=getSignalSlot(address);
|
||||||
|
if (sigslot<0) return false; // this is not a defined signal
|
||||||
|
int16_t sigpos=sigslot*8;
|
||||||
|
VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos);
|
||||||
|
VPIN sigtype=sigid & ~SIGNAL_ID_MASK;
|
||||||
|
if (sigtype!=DCCX_SIGNAL_FLAG) return false; // not a DCCX signal
|
||||||
|
// Turn an aspect change into a RED/AMBER/GREEN setting
|
||||||
|
if (aspect==GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+2)) {
|
||||||
|
doSignal(sigid,SIGNAL_RED);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aspect==GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+4)) {
|
||||||
|
doSignal(sigid,SIGNAL_AMBER);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aspect==GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+6)) {
|
||||||
|
doSignal(sigid,SIGNAL_GREEN);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // aspect is not a defined one
|
||||||
|
}
|
||||||
|
|
||||||
void RMFT2::turnoutEvent(int16_t turnoutId, bool closed) {
|
void RMFT2::turnoutEvent(int16_t turnoutId, bool closed) {
|
||||||
// Hunt for an ONTHROW/ONCLOSE for this turnout
|
// Hunt for an ONTHROW/ONCLOSE for this turnout
|
||||||
if (closed) onCloseLookup->handleEvent(F("CLOSE"),turnoutId);
|
if (closed) onCloseLookup->handleEvent(F("CLOSE"),turnoutId);
|
||||||
|
@ -1284,6 +1334,7 @@ void RMFT2::thrungeString(uint32_t strfar, thrunger mode, byte id) {
|
||||||
break;
|
break;
|
||||||
case thrunge_parse:
|
case thrunge_parse:
|
||||||
case thrunge_broadcast:
|
case thrunge_broadcast:
|
||||||
|
case thrunge_message:
|
||||||
case thrunge_lcd:
|
case thrunge_lcd:
|
||||||
default: // thrunge_lcd+1, ...
|
default: // thrunge_lcd+1, ...
|
||||||
if (!buffer) buffer=new StringBuffer();
|
if (!buffer) buffer=new StringBuffer();
|
||||||
|
@ -1321,6 +1372,9 @@ void RMFT2::thrungeString(uint32_t strfar, thrunger mode, byte id) {
|
||||||
case thrunge_withrottle:
|
case thrunge_withrottle:
|
||||||
CommandDistributor::broadcastRaw(CommandDistributor::WITHROTTLE_TYPE,buffer->getString());
|
CommandDistributor::broadcastRaw(CommandDistributor::WITHROTTLE_TYPE,buffer->getString());
|
||||||
break;
|
break;
|
||||||
|
case thrunge_message:
|
||||||
|
CommandDistributor::broadcastMessage(buffer->getString());
|
||||||
|
break;
|
||||||
case thrunge_lcd:
|
case thrunge_lcd:
|
||||||
LCD(id,F("%s"),buffer->getString());
|
LCD(id,F("%s"),buffer->getString());
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -54,7 +54,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
|
||||||
OPCODE_START,OPCODE_SETLOCO,OPCODE_SETFREQ,OPCODE_SENDLOCO,OPCODE_FORGET,
|
OPCODE_START,OPCODE_SETLOCO,OPCODE_SETFREQ,OPCODE_SENDLOCO,OPCODE_FORGET,
|
||||||
OPCODE_PAUSE, OPCODE_RESUME,OPCODE_POWEROFF,OPCODE_POWERON,
|
OPCODE_PAUSE, OPCODE_RESUME,OPCODE_POWEROFF,OPCODE_POWERON,
|
||||||
OPCODE_ONCLOSE, OPCODE_ONTHROW, OPCODE_SERVOTURNOUT, OPCODE_PINTURNOUT,
|
OPCODE_ONCLOSE, OPCODE_ONTHROW, OPCODE_SERVOTURNOUT, OPCODE_PINTURNOUT,
|
||||||
OPCODE_PRINT,OPCODE_DCCACTIVATE,
|
OPCODE_PRINT,OPCODE_DCCACTIVATE,OPCODE_ASPECT,
|
||||||
OPCODE_ONACTIVATE,OPCODE_ONDEACTIVATE,
|
OPCODE_ONACTIVATE,OPCODE_ONDEACTIVATE,
|
||||||
OPCODE_ROSTER,OPCODE_KILLALL,
|
OPCODE_ROSTER,OPCODE_KILLALL,
|
||||||
OPCODE_ROUTE,OPCODE_AUTOMATION,OPCODE_SEQUENCE,
|
OPCODE_ROUTE,OPCODE_AUTOMATION,OPCODE_SEQUENCE,
|
||||||
|
@ -94,7 +94,7 @@ enum thrunger: byte {
|
||||||
thrunge_serial,thrunge_parse,
|
thrunge_serial,thrunge_parse,
|
||||||
thrunge_serial1, thrunge_serial2, thrunge_serial3,
|
thrunge_serial1, thrunge_serial2, thrunge_serial3,
|
||||||
thrunge_serial4, thrunge_serial5, thrunge_serial6,
|
thrunge_serial4, thrunge_serial5, thrunge_serial6,
|
||||||
thrunge_lcn,
|
thrunge_lcn,thrunge_message,
|
||||||
thrunge_lcd, // Must be last!!
|
thrunge_lcd, // Must be last!!
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -155,9 +155,11 @@ 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 bool signalAspectEvent(int16_t address, byte aspect );
|
||||||
static const int16_t SERVO_SIGNAL_FLAG=0x4000;
|
static const int16_t SERVO_SIGNAL_FLAG=0x4000;
|
||||||
static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000;
|
static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000;
|
||||||
static const int16_t DCC_SIGNAL_FLAG=0x1000;
|
static const int16_t DCC_SIGNAL_FLAG=0x1000;
|
||||||
|
static const int16_t DCCX_SIGNAL_FLAG=0x3000;
|
||||||
static const int16_t SIGNAL_ID_MASK=0x0FFF;
|
static const int16_t SIGNAL_ID_MASK=0x0FFF;
|
||||||
// Throttle Info Access functions built by exrail macros
|
// Throttle Info Access functions built by exrail macros
|
||||||
static const byte rosterNameCount;
|
static const byte rosterNameCount;
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#undef ALIAS
|
#undef ALIAS
|
||||||
#undef AMBER
|
#undef AMBER
|
||||||
#undef ANOUT
|
#undef ANOUT
|
||||||
|
#undef ASPECT
|
||||||
#undef AT
|
#undef AT
|
||||||
#undef ATGTE
|
#undef ATGTE
|
||||||
#undef ATLT
|
#undef ATLT
|
||||||
|
@ -42,7 +43,9 @@
|
||||||
#undef CLEAR_STASH
|
#undef CLEAR_STASH
|
||||||
#undef CLEAR_ALL_STASH
|
#undef CLEAR_ALL_STASH
|
||||||
#undef CLOSE
|
#undef CLOSE
|
||||||
|
#undef CONFIGURE_SERVO
|
||||||
#undef DCC_SIGNAL
|
#undef DCC_SIGNAL
|
||||||
|
#undef DCCX_SIGNAL
|
||||||
#undef DCC_TURNTABLE
|
#undef DCC_TURNTABLE
|
||||||
#undef DEACTIVATE
|
#undef DEACTIVATE
|
||||||
#undef DEACTIVATEL
|
#undef DEACTIVATEL
|
||||||
|
@ -94,6 +97,7 @@
|
||||||
#undef LCCX
|
#undef LCCX
|
||||||
#undef LCN
|
#undef LCN
|
||||||
#undef MOVETT
|
#undef MOVETT
|
||||||
|
#undef MESSAGE
|
||||||
#undef ONACTIVATE
|
#undef ONACTIVATE
|
||||||
#undef ONACTIVATEL
|
#undef ONACTIVATEL
|
||||||
#undef ONAMBER
|
#undef ONAMBER
|
||||||
|
@ -186,6 +190,7 @@
|
||||||
#define AMBER(signal_id)
|
#define AMBER(signal_id)
|
||||||
#define ANOUT(vpin,value,param1,param2)
|
#define ANOUT(vpin,value,param1,param2)
|
||||||
#define AT(sensor_id)
|
#define AT(sensor_id)
|
||||||
|
#define ASPECT(address,value)
|
||||||
#define ATGTE(sensor_id,value)
|
#define ATGTE(sensor_id,value)
|
||||||
#define ATLT(sensor_id,value)
|
#define ATLT(sensor_id,value)
|
||||||
#define ATTIMEOUT(sensor_id,timeout_ms)
|
#define ATTIMEOUT(sensor_id,timeout_ms)
|
||||||
|
@ -196,7 +201,9 @@
|
||||||
#define CLEAR_STASH(id)
|
#define CLEAR_STASH(id)
|
||||||
#define CLEAR_ALL_STASH(id)
|
#define CLEAR_ALL_STASH(id)
|
||||||
#define CLOSE(id)
|
#define CLOSE(id)
|
||||||
|
#define CONFIGURE_SERVO(vpin,pos1,pos2,profile)
|
||||||
#define DCC_SIGNAL(id,add,subaddr)
|
#define DCC_SIGNAL(id,add,subaddr)
|
||||||
|
#define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect)
|
||||||
#define DCC_TURNTABLE(id,home,description)
|
#define DCC_TURNTABLE(id,home,description)
|
||||||
#define DEACTIVATE(addr,subaddr)
|
#define DEACTIVATE(addr,subaddr)
|
||||||
#define DEACTIVATEL(addr)
|
#define DEACTIVATEL(addr)
|
||||||
|
@ -247,6 +254,7 @@
|
||||||
#define LCD(row,msg)
|
#define LCD(row,msg)
|
||||||
#define SCREEN(display,row,msg)
|
#define SCREEN(display,row,msg)
|
||||||
#define LCN(msg)
|
#define LCN(msg)
|
||||||
|
#define MESSAGE(msg)
|
||||||
#define MOVETT(id,steps,activity)
|
#define MOVETT(id,steps,activity)
|
||||||
#define ONACTIVATE(addr,subaddr)
|
#define ONACTIVATE(addr,subaddr)
|
||||||
#define ONACTIVATEL(linear)
|
#define ONACTIVATEL(linear)
|
||||||
|
|
|
@ -51,6 +51,14 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16
|
||||||
opcode=0;
|
opcode=0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'A': // <A address aspect>
|
||||||
|
if (paramCount!=2) break;
|
||||||
|
// Ask exrail if this is just changing the aspect on a
|
||||||
|
// predefined DCCX_SIGNAL. Because this will handle all
|
||||||
|
// the IFRED and ONRED type issues at the same time.
|
||||||
|
if (signalAspectEvent(p[0],p[1])) opcode=0; // all done
|
||||||
|
break;
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
// This entire code block is compiled out if LLC macros not used
|
// This entire code block is compiled out if LLC macros not used
|
||||||
if (!(compileFeatures & FEATURE_LCC)) return;
|
if (!(compileFeatures & FEATURE_LCC)) return;
|
||||||
|
|
|
@ -95,14 +95,14 @@ constexpr int16_t stuffSize=sizeof(compileTimeSequenceList)/sizeof(int16_t) - 1;
|
||||||
|
|
||||||
|
|
||||||
// Compile time function to check for sequence nos.
|
// Compile time function to check for sequence nos.
|
||||||
constexpr bool hasseq(const int16_t value, const uint16_t pos=0 ) {
|
constexpr bool hasseq(const int16_t value, const int16_t pos=0 ) {
|
||||||
return pos>=stuffSize? false :
|
return pos>=stuffSize? false :
|
||||||
compileTimeSequenceList[pos]==value
|
compileTimeSequenceList[pos]==value
|
||||||
|| hasseq(value,pos+1);
|
|| hasseq(value,pos+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile time function to check for duplicate sequence nos.
|
// Compile time function to check for duplicate sequence nos.
|
||||||
constexpr bool hasdup(const int16_t value, const uint16_t pos ) {
|
constexpr bool hasdup(const int16_t value, const int16_t pos ) {
|
||||||
return pos>=stuffSize? false :
|
return pos>=stuffSize? false :
|
||||||
compileTimeSequenceList[pos]==value
|
compileTimeSequenceList[pos]==value
|
||||||
|| hasseq(value,pos+1)
|
|| hasseq(value,pos+1)
|
||||||
|
@ -117,6 +117,9 @@ static_assert(!hasdup(compileTimeSequenceList[0],1),"Duplicate SEQUENCE/ROUTE/AU
|
||||||
// - check range on LATCH/UNLATCH
|
// - check range on LATCH/UNLATCH
|
||||||
// This pass generates no runtime data or code
|
// This pass generates no runtime data or code
|
||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
|
#undef ASPECT
|
||||||
|
#define ASPECT(address,value) static_assert(address <=2044, "invalid Address"); \
|
||||||
|
static_assert(address>=-3, "Invalid value");
|
||||||
#undef CALL
|
#undef CALL
|
||||||
#define CALL(id) static_assert(hasseq(id),"Sequence not found");
|
#define CALL(id) static_assert(hasseq(id),"Sequence not found");
|
||||||
#undef FOLLOW
|
#undef FOLLOW
|
||||||
|
@ -151,6 +154,8 @@ static_assert(!hasdup(compileTimeSequenceList[0],1),"Duplicate SEQUENCE/ROUTE/AU
|
||||||
#define HAL_IGNORE_DEFAULTS ignore_defaults=true;
|
#define HAL_IGNORE_DEFAULTS ignore_defaults=true;
|
||||||
#undef JMRI_SENSOR
|
#undef JMRI_SENSOR
|
||||||
#define JMRI_SENSOR(vpin,count...) Sensor::createMultiple(vpin,##count);
|
#define JMRI_SENSOR(vpin,count...) Sensor::createMultiple(vpin,##count);
|
||||||
|
#undef CONFIGURE_SERVO
|
||||||
|
#define CONFIGURE_SERVO(vpin,pos1,pos2,profile) IODevice::configureServo(vpin,pos1,pos2,PCA9685::profile);
|
||||||
bool exrailHalSetup() {
|
bool exrailHalSetup() {
|
||||||
bool ignore_defaults=false;
|
bool ignore_defaults=false;
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
|
@ -167,6 +172,8 @@ bool exrailHalSetup() {
|
||||||
#define SERVO_SIGNAL(vpin,redval,amberval,greenval) | FEATURE_SIGNAL
|
#define SERVO_SIGNAL(vpin,redval,amberval,greenval) | FEATURE_SIGNAL
|
||||||
#undef DCC_SIGNAL
|
#undef DCC_SIGNAL
|
||||||
#define DCC_SIGNAL(id,addr,subaddr) | FEATURE_SIGNAL
|
#define DCC_SIGNAL(id,addr,subaddr) | FEATURE_SIGNAL
|
||||||
|
#undef DCCX_SIGNAL
|
||||||
|
#define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect) | FEATURE_SIGNAL
|
||||||
#undef VIRTUAL_SIGNAL
|
#undef VIRTUAL_SIGNAL
|
||||||
#define VIRTUAL_SIGNAL(id) | FEATURE_SIGNAL
|
#define VIRTUAL_SIGNAL(id) | FEATURE_SIGNAL
|
||||||
|
|
||||||
|
@ -246,6 +253,9 @@ const int StringMacroTracker1=__COUNTER__;
|
||||||
#define PRINT(msg) THRUNGE(msg,thrunge_print)
|
#define PRINT(msg) THRUNGE(msg,thrunge_print)
|
||||||
#undef LCN
|
#undef LCN
|
||||||
#define LCN(msg) THRUNGE(msg,thrunge_lcn)
|
#define LCN(msg) THRUNGE(msg,thrunge_lcn)
|
||||||
|
#undef MESSAGE
|
||||||
|
#define MESSAGE(msg) THRUNGE(msg,thrunge_message)
|
||||||
|
|
||||||
#undef ROUTE_CAPTION
|
#undef ROUTE_CAPTION
|
||||||
#define ROUTE_CAPTION(id,caption) \
|
#define ROUTE_CAPTION(id,caption) \
|
||||||
case (__COUNTER__ - StringMacroTracker1) : {\
|
case (__COUNTER__ - StringMacroTracker1) : {\
|
||||||
|
@ -343,6 +353,8 @@ const FSH * RMFT2::getTurntableDescription(int16_t turntableId) {
|
||||||
#define TT_ADDPOSITION(turntable_id,position,value,home,description...) T_DESC(turntable_id,position,description)
|
#define TT_ADDPOSITION(turntable_id,position,value,home,description...) T_DESC(turntable_id,position,description)
|
||||||
|
|
||||||
const FSH * RMFT2::getTurntablePositionDescription(int16_t turntableId, uint8_t positionId) {
|
const FSH * RMFT2::getTurntablePositionDescription(int16_t turntableId, uint8_t positionId) {
|
||||||
|
(void)turntableId;
|
||||||
|
(void)positionId;
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -396,6 +408,8 @@ const FSH * RMFT2::getRosterFunctions(int16_t id) {
|
||||||
#define SERVO_SIGNAL(vpin,redval,amberval,greenval) vpin | RMFT2::SERVO_SIGNAL_FLAG,redval,amberval,greenval,
|
#define SERVO_SIGNAL(vpin,redval,amberval,greenval) vpin | RMFT2::SERVO_SIGNAL_FLAG,redval,amberval,greenval,
|
||||||
#undef DCC_SIGNAL
|
#undef DCC_SIGNAL
|
||||||
#define DCC_SIGNAL(id,addr,subaddr) id | RMFT2::DCC_SIGNAL_FLAG,addr,subaddr,0,
|
#define DCC_SIGNAL(id,addr,subaddr) id | RMFT2::DCC_SIGNAL_FLAG,addr,subaddr,0,
|
||||||
|
#undef DCCX_SIGNAL
|
||||||
|
#define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect) id | RMFT2::DCCX_SIGNAL_FLAG,redAspect,amberAspect,greenAspect,
|
||||||
#undef VIRTUAL_SIGNAL
|
#undef VIRTUAL_SIGNAL
|
||||||
#define VIRTUAL_SIGNAL(id) id,0,0,0,
|
#define VIRTUAL_SIGNAL(id) id,0,0,0,
|
||||||
|
|
||||||
|
@ -430,6 +444,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
|
||||||
#define ALIAS(name,value...)
|
#define ALIAS(name,value...)
|
||||||
#define AMBER(signal_id) OPCODE_AMBER,V(signal_id),
|
#define AMBER(signal_id) OPCODE_AMBER,V(signal_id),
|
||||||
#define ANOUT(vpin,value,param1,param2) OPCODE_SERVO,V(vpin),OPCODE_PAD,V(value),OPCODE_PAD,V(param1),OPCODE_PAD,V(param2),
|
#define ANOUT(vpin,value,param1,param2) OPCODE_SERVO,V(vpin),OPCODE_PAD,V(value),OPCODE_PAD,V(param1),OPCODE_PAD,V(param2),
|
||||||
|
#define ASPECT(address,value) OPCODE_ASPECT,V((address<<5) | (value & 0x1F)),
|
||||||
#define AT(sensor_id) OPCODE_AT,V(sensor_id),
|
#define AT(sensor_id) OPCODE_AT,V(sensor_id),
|
||||||
#define ATGTE(sensor_id,value) OPCODE_ATGTE,V(sensor_id),OPCODE_PAD,V(value),
|
#define ATGTE(sensor_id,value) OPCODE_ATGTE,V(sensor_id),OPCODE_PAD,V(value),
|
||||||
#define ATLT(sensor_id,value) OPCODE_ATLT,V(sensor_id),OPCODE_PAD,V(value),
|
#define ATLT(sensor_id,value) OPCODE_ATLT,V(sensor_id),OPCODE_PAD,V(value),
|
||||||
|
@ -441,6 +456,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
|
||||||
#define CLEAR_STASH(id) OPCODE_CLEAR_STASH,V(id),
|
#define CLEAR_STASH(id) OPCODE_CLEAR_STASH,V(id),
|
||||||
#define CLEAR_ALL_STASH OPCODE_CLEAR_ALL_STASH,V(0),
|
#define CLEAR_ALL_STASH OPCODE_CLEAR_ALL_STASH,V(0),
|
||||||
#define CLOSE(id) OPCODE_CLOSE,V(id),
|
#define CLOSE(id) OPCODE_CLOSE,V(id),
|
||||||
|
#define CONFIGURE_SERVO(vpin,pos1,pos2,profile)
|
||||||
#ifndef IO_NO_HAL
|
#ifndef IO_NO_HAL
|
||||||
#define DCC_TURNTABLE(id,home,description...) OPCODE_DCCTURNTABLE,V(id),OPCODE_PAD,V(home),
|
#define DCC_TURNTABLE(id,home,description...) OPCODE_DCCTURNTABLE,V(id),OPCODE_PAD,V(home),
|
||||||
#endif
|
#endif
|
||||||
|
@ -450,6 +466,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
|
||||||
#define DELAYMINS(mindelay) OPCODE_DELAYMINS,V(mindelay),
|
#define DELAYMINS(mindelay) OPCODE_DELAYMINS,V(mindelay),
|
||||||
#define DELAYRANDOM(mindelay,maxdelay) DELAY(mindelay) OPCODE_RANDWAIT,V((maxdelay-mindelay)/100L),
|
#define DELAYRANDOM(mindelay,maxdelay) DELAY(mindelay) OPCODE_RANDWAIT,V((maxdelay-mindelay)/100L),
|
||||||
#define DCC_SIGNAL(id,add,subaddr)
|
#define DCC_SIGNAL(id,add,subaddr)
|
||||||
|
#define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect)
|
||||||
#define DONE OPCODE_ENDTASK,0,0,
|
#define DONE OPCODE_ENDTASK,0,0,
|
||||||
#define DRIVE(analogpin) OPCODE_DRIVE,V(analogpin),
|
#define DRIVE(analogpin) OPCODE_DRIVE,V(analogpin),
|
||||||
#define ELSE OPCODE_ELSE,0,0,
|
#define ELSE OPCODE_ELSE,0,0,
|
||||||
|
@ -502,6 +519,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
|
||||||
#define SCREEN(display,id,msg) PRINT(msg)
|
#define SCREEN(display,id,msg) PRINT(msg)
|
||||||
#define STEALTH(code...) PRINT(dummy)
|
#define STEALTH(code...) PRINT(dummy)
|
||||||
#define LCN(msg) PRINT(msg)
|
#define LCN(msg) PRINT(msg)
|
||||||
|
#define MESSAGE(msg) PRINT(msg)
|
||||||
#define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0),
|
#define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0),
|
||||||
#define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr),
|
#define 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),
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
#define GITHUB_SHA "devel-stm32EC 202402072340Z"
|
#define GITHUB_SHA "devel-stm32ECa-202403082308Z"
|
||||||
|
|
|
@ -83,6 +83,7 @@ void EXTurntable::_loop(unsigned long currentMicros) {
|
||||||
// Read returns status as obtained in our loop.
|
// Read returns status as obtained in our loop.
|
||||||
// Return false if our status value is invalid.
|
// Return false if our status value is invalid.
|
||||||
int EXTurntable::_read(VPIN vpin) {
|
int EXTurntable::_read(VPIN vpin) {
|
||||||
|
(void)vpin; // surpress warning
|
||||||
if (_deviceState == DEVSTATE_FAILED) return 0;
|
if (_deviceState == DEVSTATE_FAILED) return 0;
|
||||||
if (_stepperStatus > 1) {
|
if (_stepperStatus > 1) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -127,6 +128,8 @@ void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_
|
||||||
vpin, value, activity, duration);
|
vpin, value, activity, duration);
|
||||||
DIAG(F("I2CManager write I2C Address:%d stepsMSB:%d stepsLSB:%d activity:%d"),
|
DIAG(F("I2CManager write I2C Address:%d stepsMSB:%d stepsLSB:%d activity:%d"),
|
||||||
_I2CAddress.toString(), stepsMSB, stepsLSB, activity);
|
_I2CAddress.toString(), stepsMSB, stepsLSB, activity);
|
||||||
|
#else
|
||||||
|
(void)duration;
|
||||||
#endif
|
#endif
|
||||||
if (activity < 4) _stepperStatus = 1; // Tell the device driver Turntable-EX is busy
|
if (activity < 4) _stepperStatus = 1; // Tell the device driver Turntable-EX is busy
|
||||||
_previousStatus = _stepperStatus;
|
_previousStatus = _stepperStatus;
|
||||||
|
|
|
@ -34,9 +34,15 @@ template<class T> inline T operator| (T a, T b) { return (T)((int)a | (int)b); }
|
||||||
template<class T> inline T operator& (T a, T b) { return (T)((int)a & (int)b); }
|
template<class T> inline T operator& (T a, T b) { return (T)((int)a & (int)b); }
|
||||||
template<class T> inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); }
|
template<class T> inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); }
|
||||||
enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4,
|
enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4,
|
||||||
TRACK_MODE_DC = 8, TRACK_MODE_EXT = 16, TRACK_MODE_BOOST = 32,
|
TRACK_MODE_DC = 8, TRACK_MODE_EXT = 16,
|
||||||
TRACK_MODE_ALL = 62, // only to operate all tracks
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
TRACK_MODE_INV = 64, TRACK_MODE_DCX = 72 /*DC + INV*/, TRACK_MODE_AUTOINV = 128};
|
TRACK_MODE_BOOST = 32,
|
||||||
|
#else
|
||||||
|
TRACK_MODE_BOOST = 0,
|
||||||
|
#endif
|
||||||
|
TRACK_MODE_ALL = TRACK_MODE_MAIN|TRACK_MODE_PROG|TRACK_MODE_DC|TRACK_MODE_EXT|TRACK_MODE_BOOST,
|
||||||
|
TRACK_MODE_INV = 64,
|
||||||
|
TRACK_MODE_DCX = TRACK_MODE_DC|TRACK_MODE_INV, TRACK_MODE_AUTOINV = 128};
|
||||||
|
|
||||||
#define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH
|
#define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH
|
||||||
#define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW
|
#define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW
|
||||||
|
|
|
@ -158,12 +158,6 @@ void TrackManager::setDCCSignal( bool on) {
|
||||||
HAVE_PORTF(PORTF=shadowPORTF);
|
HAVE_PORTF(PORTF=shadowPORTF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrackManager::setCutout( bool on) {
|
|
||||||
(void) on;
|
|
||||||
// TODO Cutout needs fake ports as well
|
|
||||||
// TODO APPLY_BY_MODE(TRACK_MODE_MAIN,setCutout(on));
|
|
||||||
}
|
|
||||||
|
|
||||||
// setPROGSignal(), called from interrupt context
|
// setPROGSignal(), called from interrupt context
|
||||||
// does assume ports are shadowed if they can be
|
// does assume ports are shadowed if they can be
|
||||||
void TrackManager::setPROGSignal( bool on) {
|
void TrackManager::setPROGSignal( bool on) {
|
||||||
|
@ -225,7 +219,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
|
||||||
if (mode & TRACK_MODE_BOOST) {
|
if (mode & TRACK_MODE_BOOST) {
|
||||||
//DIAG(F("Track=%c mode boost pin %d"),trackToSet+'A', p.pin);
|
//DIAG(F("Track=%c mode boost pin %d"),trackToSet+'A', p.pin);
|
||||||
pinMode(BOOSTER_INPUT, INPUT);
|
pinMode(BOOSTER_INPUT, INPUT);
|
||||||
gpio_matrix_in(26, SIG_IN_FUNC228_IDX, false); //pads 224 to 228 available as loopback
|
gpio_matrix_in(BOOSTER_INPUT, SIG_IN_FUNC228_IDX, false); //pads 224 to 228 available as loopback
|
||||||
gpio_matrix_out(p.pin, SIG_IN_FUNC228_IDX, false, false);
|
gpio_matrix_out(p.pin, SIG_IN_FUNC228_IDX, false, false);
|
||||||
if (p.invpin != UNUSED_PIN) {
|
if (p.invpin != UNUSED_PIN) {
|
||||||
gpio_matrix_out(p.invpin, SIG_IN_FUNC228_IDX, true /*inverted*/, false);
|
gpio_matrix_out(p.invpin, SIG_IN_FUNC228_IDX, true /*inverted*/, false);
|
||||||
|
@ -368,7 +362,8 @@ bool TrackManager::parseEqualSign(Print *stream, int16_t params, int16_t p[])
|
||||||
if (params==2 && p[1]=="EXT"_hk) // <= id EXT>
|
if (params==2 && p[1]=="EXT"_hk) // <= id EXT>
|
||||||
return setTrackMode(p[0],TRACK_MODE_EXT);
|
return setTrackMode(p[0],TRACK_MODE_EXT);
|
||||||
#ifdef BOOSTER_INPUT
|
#ifdef BOOSTER_INPUT
|
||||||
if (params==2 && p[1]=="BOOST"_hk) // <= id BOOST>
|
if (TRACK_MODE_BOOST != 0 && // compile time optimization
|
||||||
|
params==2 && p[1]=="BOOST"_hk) // <= id BOOST>
|
||||||
return setTrackMode(p[0],TRACK_MODE_BOOST);
|
return setTrackMode(p[0],TRACK_MODE_BOOST);
|
||||||
#endif
|
#endif
|
||||||
if (params==2 && p[1]=="AUTO"_hk) // <= id AUTO>
|
if (params==2 && p[1]=="AUTO"_hk) // <= id AUTO>
|
||||||
|
@ -407,11 +402,11 @@ const FSH* TrackManager::getModeName(TRACK_MODE tm) {
|
||||||
modename=F("EXT");
|
modename=F("EXT");
|
||||||
else if(tm & TRACK_MODE_BOOST) {
|
else if(tm & TRACK_MODE_BOOST) {
|
||||||
if(tm & TRACK_MODE_AUTOINV)
|
if(tm & TRACK_MODE_AUTOINV)
|
||||||
modename=F("B A");
|
modename=F("BOOST A");
|
||||||
else if (tm & TRACK_MODE_INV)
|
else if (tm & TRACK_MODE_INV)
|
||||||
modename=F("B I");
|
modename=F("BOOST I");
|
||||||
else
|
else
|
||||||
modename=F("B");
|
modename=F("BOOST");
|
||||||
}
|
}
|
||||||
else if (tm & TRACK_MODE_DC) {
|
else if (tm & TRACK_MODE_DC) {
|
||||||
if (tm & TRACK_MODE_INV)
|
if (tm & TRACK_MODE_INV)
|
||||||
|
|
|
@ -57,7 +57,6 @@ class TrackManager {
|
||||||
);
|
);
|
||||||
|
|
||||||
static void setDCCSignal( bool on);
|
static void setDCCSignal( bool on);
|
||||||
static void setCutout( bool on);
|
|
||||||
static void setPROGSignal( bool on);
|
static void setPROGSignal( bool on);
|
||||||
static void setDCSignal(int16_t cab, byte speedbyte);
|
static void setDCSignal(int16_t cab, byte speedbyte);
|
||||||
static MotorDriver * getProgDriver();
|
static MotorDriver * getProgDriver();
|
||||||
|
|
|
@ -247,8 +247,9 @@ DCCTurntable::DCCTurntable(uint16_t id) : Turntable(id, TURNTABLE_DCC) {}
|
||||||
StringFormatter::send(stream, F("<i %d DCCTURNTABLE>\n"), _turntableData.id);
|
StringFormatter::send(stream, F("<i %d DCCTURNTABLE>\n"), _turntableData.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// EX-Turntable specific code for moving to the specified position
|
// EX-Turntable specific code for moving to the specified position
|
||||||
bool DCCTurntable::setPositionInternal(uint8_t position, uint8_t activity) {
|
bool DCCTurntable::setPositionInternal(uint8_t position, uint8_t activity) {
|
||||||
|
(void) activity;
|
||||||
#ifndef IO_NO_HAL
|
#ifndef IO_NO_HAL
|
||||||
int16_t value = getPositionValue(position);
|
int16_t value = getPositionValue(position);
|
||||||
if (position == 0 || !value) return false; // Return false if it's not a valid position
|
if (position == 0 || !value) return false; // Return false if it's not a valid position
|
||||||
|
@ -263,6 +264,6 @@ DCCTurntable::DCCTurntable(uint16_t id) : Turntable(id, TURNTABLE_DCC) {}
|
||||||
(void)position;
|
(void)position;
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -148,10 +148,7 @@ build_flags =
|
||||||
platform = atmelavr
|
platform = atmelavr
|
||||||
board = uno
|
board = uno
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps = ${env.lib_deps}
|
||||||
${env.lib_deps}
|
|
||||||
arduino-libraries/Ethernet
|
|
||||||
SPI
|
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_echo = yes
|
monitor_echo = yes
|
||||||
build_flags = -mcall-prologues
|
build_flags = -mcall-prologues
|
||||||
|
@ -164,6 +161,7 @@ framework = arduino
|
||||||
lib_deps = ${env.lib_deps}
|
lib_deps = ${env.lib_deps}
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_echo = yes
|
monitor_echo = yes
|
||||||
|
build_flags = -mcall-prologues
|
||||||
|
|
||||||
[env:ESP32]
|
[env:ESP32]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
|
|
18
version.h
18
version.h
|
@ -3,7 +3,22 @@
|
||||||
|
|
||||||
#include "StringFormatter.h"
|
#include "StringFormatter.h"
|
||||||
|
|
||||||
#define VERSION "5.3.5e"
|
#define VERSION "5.3.6"
|
||||||
|
// 5.2.38 - Exrail MESSAGE("text") to send a user message to all
|
||||||
|
// connected throttles (uses <m "text"> and withrottle Hmtext.
|
||||||
|
// 5.2.37 - Bugfix ESP32: Use BOOSTER_INPUT define
|
||||||
|
// 5.2.36 - Variable frequency for DC mode
|
||||||
|
// 5.2.35 - Bugfix: Make DCC Extended Accessories follow RCN-213
|
||||||
|
// 5.2.34 - <A address aspect> Command fopr DCC Extended Accessories
|
||||||
|
// - Exrail ASPECT(address,aspect) for above.
|
||||||
|
// - EXRAIL DCCX_SIGNAL(Address,redAspect,amberAspect,greenAspect)
|
||||||
|
// - Exrail intercept <A ...> for DCC Signals.
|
||||||
|
// 5.2.33 - Exrail CONFIGURE_SERVO(vpin,pos1,pos2,profile)
|
||||||
|
// 5.2.32 - Railcom Cutout (Initial trial Mega2560 only)
|
||||||
|
// 5.2.31 - Exrail JMRI_SENSOR(vpin [,count]) creates <S> types.
|
||||||
|
// 5.2.30 - Bugfix: WiThrottle sendIntro after initial N message as well
|
||||||
|
//
|
||||||
|
//
|
||||||
// 5.3.5e - Fixes to ethernet cable handling, and STM32 related handling as well
|
// 5.3.5e - Fixes to ethernet cable handling, and STM32 related handling as well
|
||||||
// 5.3.5 - Exrail JMRI_SENSORS(vpin [,count]) creates <S> types.
|
// 5.3.5 - Exrail JMRI_SENSORS(vpin [,count]) creates <S> types.
|
||||||
// 5.3.4 - Bugfix: WiThrottle sendIntro after initial N message as well
|
// 5.3.4 - Bugfix: WiThrottle sendIntro after initial N message as well
|
||||||
|
@ -12,6 +27,7 @@
|
||||||
// 5.3.1 - Variable frequency for DC mode
|
// 5.3.1 - Variable frequency for DC mode
|
||||||
// 5.2.40 - Bugfix: WiThrottle sendIntro after initial N message as well
|
// 5.2.40 - Bugfix: WiThrottle sendIntro after initial N message as well
|
||||||
// 5.2.31 - included in stm32EC as 5.3.5
|
// 5.2.31 - included in stm32EC as 5.3.5
|
||||||
|
// -- some duplicates in above list
|
||||||
// 5.2.29 - Added IO_I2CDFPlayer.h to support DFPLayer over I2C connected to NXP SC16IS750/SC16IS752 (currently only single UART for SC16IS752)
|
// 5.2.29 - Added IO_I2CDFPlayer.h to support DFPLayer over I2C connected to NXP SC16IS750/SC16IS752 (currently only single UART for SC16IS752)
|
||||||
// - Added enhanced IO_I2CDFPLayer enum commands to EXRAIL2.h
|
// - Added enhanced IO_I2CDFPLayer enum commands to EXRAIL2.h
|
||||||
// - Added PLAYSOUND alias of ANOUT to EXRAILMacros.h
|
// - Added PLAYSOUND alias of ANOUT to EXRAILMacros.h
|
||||||
|
|
Loading…
Reference in New Issue
Block a user