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

Compare commits

...

64 Commits

Author SHA1 Message Date
Asbelos
4125e73318 Fix CLEAR_ALL_STASH 2025-03-29 10:33:36 +00:00
Harald Barth
911bbd63be version 5.4.6 2025-02-25 00:02:06 +01:00
Harald Barth
393b0bbd16 Bugfix: Do not drop further commands in same packet 2025-02-24 23:57:56 +01:00
Harald Barth
d9bd1e75f2 sort power output different 2025-02-21 00:09:35 +01:00
Harald Barth
d1daf41f12 version 5.4.5 2025-02-20 21:21:41 +01:00
Harald Barth
6bfa7028c4 Merge branch 'master' of https://github.com/DCC-EX/CommandStation-EX 2025-02-20 21:20:44 +01:00
Harald Barth
a5d1d04882 remove unneccessary warning about prog track as it can happen normally 2025-02-20 21:18:10 +01:00
Harald Barth
bd6e426499 track power is always turned on after setJoin() not by setJoin() 2025-02-20 21:16:50 +01:00
Harald Barth
09bae44cc0 ESP32: Better detection of wrong IDF version 2025-02-20 21:15:49 +01:00
Fred
9f3354c687
Move xls release notes to release notes folder (#440) 2025-02-18 09:23:30 -05:00
Fred
fb495985f4
Add XLS searchable version of 5.4 release notes (#439) 2025-02-18 08:30:28 -05:00
Harald Barth
f868604ca9 version 5.4.4 2025-01-31 11:22:55 +01:00
Harald Barth
41168a9dd8 Bugfix: trailing > in command was not replaced with \0 which did break <+> commands 2025-01-31 11:19:22 +01:00
Harald Barth
0154e7fd78 Bugfix: serial COMMAND_BUFFER_SIZE could be silently overrun 2025-01-31 11:17:59 +01:00
Asbelos
9054d8d9f5 Merge branch 'master-fn31' 2025-01-21 09:35:58 +00:00
Harald Barth
865f75dda4 version 5.4.2 2025-01-20 22:41:47 +01:00
Harald Barth
b40fa779a6 revert part of commit 3c725a which did fix bug but reverse direction 2025-01-20 22:40:43 +01:00
Asbelos
2115ada2a1 5.4.2 bugfix fn31 flip 2025-01-20 20:03:21 +00:00
Harald Barth
830de850a9 version 5.4.1 2025-01-17 19:14:32 +01:00
Harald Barth
c28965c58d ESP32 bugfix packet buffer race 2025-01-17 19:12:11 +01:00
Harald Barth
0476b9c1d8 Merge branch 'master' of https://github.com/DCC-EX/CommandStation-EX 2025-01-10 20:16:37 +01:00
Harald Barth
ba9ca1ccad sha 2025-01-10 20:15:20 +01:00
Harald Barth
c389fe9d3b tag 2025-01-09 21:42:01 +01:00
Harald Barth
79c30ec516 For 5.4.0: reduce number of compile targets 2025-01-09 21:35:52 +01:00
Harald Barth
147fe15e04 version 5.2.96 2025-01-09 20:41:46 +01:00
Harald Barth
b5491f9b52 EXRAIL additions XFWD() and XREV() 2025-01-09 20:40:07 +01:00
Harald Barth
6f1c7a9e98 Documentation improvements in config.example.h 2025-01-05 20:18:44 +01:00
Harald Barth
42986c3b2d 5.2.95 release candidate for 5.4 2025-01-02 19:49:22 +01:00
Harald Barth
c1046ddcc0 Merge branch 'master-merge' into devel-merge 2025-01-02 19:10:12 +01:00
Harald Barth
818240b349 version tag 2024-12-28 15:46:32 +01:00
Harald Barth
3c725afab4 Less confusion and simpler code around the RCN213 defines 2024-12-28 15:45:27 +01:00
Harald Barth
13488e1e93 version tag 2024-12-22 23:22:24 +01:00
Harald Barth
6cc3b4c6bf Merge branch 'devel-esp32-progfix' into devel 2024-12-22 23:14:12 +01:00
Harald Barth
43fe772661 remove diag 2024-12-22 23:13:53 +01:00
Harald Barth
cafd53a0e5 clear progTrackSyncMain (join flag) when prog track is removed 2nd fix 2024-12-22 23:12:45 +01:00
Harald Barth
d4a99b5db5 version 5.2.93 2024-12-22 13:59:21 +01:00
Harald Barth
3ead534c81 Merge branch 'devel-esp32-progfix' into devel 2024-12-22 13:57:28 +01:00
Harald Barth
84bc098157 seperate out the templates that make it possible to use bitwise operations on enums 2024-12-21 16:08:57 +01:00
Harald Barth
8329fd83ce clear progTrackSyncMain (join flag) when prog track is removed 2024-12-21 15:42:15 +01:00
Harald Barth
4f16091670 take whole if clause out when DISABLE_PROG is active 2024-12-21 15:19:23 +01:00
Asbelos
377f10e1c5 5.2.92 2024-12-19 13:19:34 +00:00
Asbelos
016a20259a FADE fix and Track id diag. 2024-12-19 12:06:03 +00:00
Asbelos
14724aeb2a Nweopixel overlap check 2024-11-24 14:47:19 +00:00
pmantoine
8f48e2ed94 Bugfix EXRAIL EXTT_TURNTABLE description now optional 2024-11-11 09:06:56 +08:00
Harald Barth
6710c47f03 Merge branch 'devel' of https://github.com/DCC-EX/CommandStation-EX into devel 2024-11-09 13:02:07 +01:00
Asbelos
953b8054f5 5.2.89 2024-11-06 01:11:18 +00:00
Asbelos
8081bfdf1e warnings cleared 2024-11-06 01:05:35 +00:00
Asbelos
03bd1e897a SET/RESET range 2024-11-05 12:23:06 +00:00
Harald Barth
ece2ac3ccf revert last 3 commits 2024-10-06 08:00:07 +02:00
Barry Daniel
ea2e5ab8e9
Delete CamParser.cpp 2024-10-06 15:07:52 +10:00
Barry Daniel
480eb1bfde
Delete myHal.cpp 2024-10-06 15:07:15 +10:00
Barry Daniel
21dca05257
Add files via upload 2024-10-06 14:54:37 +10:00
Harald Barth
4e491a1e56 Typo 2024-06-02 21:17:30 +02:00
Harald Barth
430161ef60 ESP32: Refuse IDF5 2024-06-02 21:14:46 +02:00
Peter Akers
28d60d4984 Update README.md 2024-02-16 18:02:40 +10:00
peteGSX
3b162996ad EX-IO fixes in version 2024-01-21 07:13:53 +10:00
Harald Barth
fb414a7a50 Bugfix: allocate enough bytes for digital pins. Add more sanity checks when allocating memory 2024-01-20 21:45:09 +01:00
Harald Barth
818e05b425 version 5.0.8 2024-01-10 08:37:54 +01:00
Harald Barth
c5168f030f Do not crash on turnouts without description 2024-01-10 08:25:34 +01:00
Harald Barth
387ea019bd version 5.0.7 2023-11-06 22:11:56 +01:00
Harald Barth
a981f83bb9 Only flag 2.2.0.0-dev as broken, not 2.2.0.0 2023-11-06 22:11:31 +01:00
Asbelos
749a859db5 Bugfix TURNOUTL 2023-11-01 20:13:05 +00:00
Harald Barth
659c58b307 version 5.0.5 2023-10-28 19:20:33 +02:00
Harald Barth
0b9ec7460b Bugfix version detection logic and better message 2023-10-28 19:18:59 +02:00
28 changed files with 250 additions and 184 deletions

View File

@ -280,6 +280,9 @@ void CommandDistributor::broadcastPower() {
state = '1'; state = '1';
} }
if (state != '2')
broadcastReply(COMMAND_TYPE, F("<p%c>\n"),state);
// additional info about MAIN, PROG and JOIN // additional info about MAIN, PROG and JOIN
bool main=TrackManager::getMainPower()==POWERMODE::ON; bool main=TrackManager::getMainPower()==POWERMODE::ON;
bool prog=TrackManager::getProgPower()==POWERMODE::ON; bool prog=TrackManager::getProgPower()==POWERMODE::ON;
@ -288,7 +291,7 @@ void CommandDistributor::broadcastPower() {
const FSH * reason=F(""); const FSH * reason=F("");
if (join) { if (join) {
reason = F(" JOIN"); // with space at start so we can append without space reason = F(" JOIN"); // with space at start so we can append without space
broadcastReply(COMMAND_TYPE, F("<p1 %S>\n"),reason); broadcastReply(COMMAND_TYPE, F("<p1%S>\n"),reason);
} else { } else {
if (main) { if (main) {
//reason = F("MAIN"); //reason = F("MAIN");
@ -299,9 +302,6 @@ void CommandDistributor::broadcastPower() {
broadcastReply(COMMAND_TYPE, F("<p1 PROG>\n")); broadcastReply(COMMAND_TYPE, F("<p1 PROG>\n"));
} }
} }
if (state != '2')
broadcastReply(COMMAND_TYPE, F("<p%c>\n"),state);
#ifdef CD_HANDLE_RING #ifdef CD_HANDLE_RING
// send '1' if all main are on, otherwise global state (which in that case is '0' or '2') // send '1' if all main are on, otherwise global state (which in that case is '0' or '2')
broadcastReply(WITHROTTLE_TYPE, F("PPA%c\n"), main?'1': state); broadcastReply(WITHROTTLE_TYPE, F("PPA%c\n"), main?'1': state);

12
DCC.cpp
View File

@ -229,15 +229,9 @@ bool DCC::setFn( int cab, int16_t functionNumber, bool on) {
// Flip function state (used from withrottle protocol) // Flip function state (used from withrottle protocol)
void DCC::changeFn( int cab, int16_t functionNumber) { void DCC::changeFn( int cab, int16_t functionNumber) {
if (cab<=0 || functionNumber>31) return; auto currentValue=getFn(cab,functionNumber);
int reg = lookupSpeedTable(cab); if (currentValue<0) return; // function not valid for change
if (reg<0) return; setFn(cab,functionNumber, currentValue?false:true);
unsigned long funcmask = (1UL<<functionNumber);
speedTable[reg].functions ^= funcmask;
if (functionNumber <= 28) {
updateGroupflags(speedTable[reg].groupFlags, functionNumber);
}
CommandDistributor::broadcastLoco(reg);
} }
// Report function state (used from withrottle protocol) // Report function state (used from withrottle protocol)

View File

@ -67,16 +67,24 @@ CALLBACK_STATE DCCACK::callbackState=READY;
ACK_CALLBACK DCCACK::ackManagerCallback; ACK_CALLBACK DCCACK::ackManagerCallback;
void DCCACK::Setup(int cv, byte byteValueOrBitnum, ackOp const program[], ACK_CALLBACK callback) { void DCCACK::Setup(int cv, byte byteValueOrBitnum, ackOp const program[], ACK_CALLBACK callback) {
// On ESP32 the joined track is hidden from sight (it has type MAIN)
// and because of that we need first check if track was joined and
// then unjoin if necessary. This requires that the joined flag is
// cleared when the prog track is removed.
ackManagerRejoin=TrackManager::isJoined(); ackManagerRejoin=TrackManager::isJoined();
//DIAG(F("Joined is %d"), ackManagerRejoin);
if (ackManagerRejoin) { if (ackManagerRejoin) {
// Change from JOIN must zero resets packet. // Change from JOIN must zero resets packet.
TrackManager::setJoin(false); TrackManager::setJoin(false);
DCCWaveform::progTrack.clearResets(); DCCWaveform::progTrack.clearResets();
} }
progDriver=TrackManager::getProgDriver(); progDriver=TrackManager::getProgDriver();
//DIAG(F("Progdriver is %d"), progDriver);
if (progDriver==NULL) { if (progDriver==NULL) {
TrackManager::setJoin(ackManagerRejoin); if (ackManagerRejoin) {
DIAG(F("Joined but no Prog track"));
TrackManager::setJoin(false);
}
callback(-3); // we dont have a prog track! callback(-3); // we dont have a prog track!
return; return;
} }

View File

@ -167,8 +167,10 @@ int16_t DCCEXParser::splitValues(int16_t result[MAX_COMMAND_PARAMS], byte *cmd,
break; break;
if (hot == '\0') if (hot == '\0')
return -1; return -1;
if (hot == '>') if (hot == '>') {
*remainingCmd = '\0'; // terminate the cmd string with 0 instead of '>'
return parameterCount; return parameterCount;
}
state = 2; state = 2;
continue; continue;
@ -265,17 +267,22 @@ void DCCEXParser::parse(const FSH * cmd) {
// See documentation on DCC class for info on this section // See documentation on DCC class for info on this section
void DCCEXParser::parse(Print *stream, byte *com, RingStream *ringStream) { void DCCEXParser::parse(Print *stream, byte *com, RingStream *ringStream) {
// This function can get stings of the form "<C OMM AND>" or "C OMM AND" // This function can get stings of the form "<C OMM AND>" or "C OMM AND>"
// found is true first after the leading "<" has been passed // found is true first after the leading "<" has been passed which results
// in parseOne() getting c="C OMM AND>"
byte *cForLater = NULL;
bool found = (com[0] != '<'); bool found = (com[0] != '<');
for (byte *c=com; c[0] != '\0'; c++) { for (byte *c=com; c[0] != '\0'; c++) {
if (found) { if (found) {
parseOne(stream, c, ringStream); cForLater = c;
found=false; found=false;
} }
if (c[0] == '<') if (c[0] == '<') {
if (cForLater) parseOne(stream, cForLater, ringStream);
found = true; found = true;
}
} }
if (cForLater) parseOne(stream, cForLater, ringStream);
} }
void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
@ -402,7 +409,8 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|| (p[activep] > 1) || (p[activep] < 0) // invalid activate 0|1 || (p[activep] > 1) || (p[activep] < 0) // invalid activate 0|1
) break; ) break;
// Honour the configuration option (config.h) which allows the <a> command to be reversed // Honour the configuration option (config.h) which allows the <a> command to be reversed
#ifdef DCC_ACCESSORY_COMMAND_REVERSE // Because of earlier confusion we need to do the same thing under both defines
#if defined(DCC_ACCESSORY_COMMAND_REVERSE)
DCC::setAccessory(address, subaddress,p[activep]==0,onoff); DCC::setAccessory(address, subaddress,p[activep]==0,onoff);
#else #else
DCC::setAccessory(address, subaddress,p[activep]==1,onoff); DCC::setAccessory(address, subaddress,p[activep]==1,onoff);

View File

@ -44,6 +44,12 @@ class RMTChannel {
return true; return true;
return dataReady; return dataReady;
}; };
inline void waitForDataCopy() {
while(1) { // do nothing and wait for interrupt clearing dataReady to happen
if (dataReady == false)
break;
}
};
inline uint32_t packetCount() { return packetCounter; }; inline uint32_t packetCount() { return packetCounter; };
private: private:

View File

@ -78,11 +78,17 @@ int DCCTimer::freeMemory() {
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#if __has_include("esp_idf_version.h")
#include "esp_idf_version.h" #include "esp_idf_version.h"
#if ESP_IDF_VERSION_MAJOR > 4 #endif
#error "DCC-EX does not support compiling with IDF version 5.0 or later. Downgrade your ESP32 library to a version that contains IDE version 4. Arduino ESP32 library 3.0.0 is too new. Downgrade to one of 2.0.9 to 2.0.17" #if ESP_IDF_VERSION_MAJOR == 4
// all well correct IDF version
#else
#error "DCC-EX does not support compiling with IDF version 5.0 or later. Downgrade your ESP32 library to a version that contains IDF version 4. Arduino ESP32 library 3.0.0 is too new. Downgrade to one of 2.0.9 to 2.0.17"
#endif #endif
// protect all the rest of the code from IDF version 5
#if ESP_IDF_VERSION_MAJOR == 4
#include "DIAG.h" #include "DIAG.h"
#include <driver/adc.h> #include <driver/adc.h>
#include <soc/sens_reg.h> #include <soc/sens_reg.h>
@ -322,5 +328,5 @@ void ADCee::scan() {
void ADCee::begin() { void ADCee::begin() {
} }
#endif //IDF v4
#endif //ESP32 #endif //ESP32

View File

@ -278,7 +278,11 @@ void DCCWaveform::begin() {
void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) { void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) {
if (byteCount > MAX_PACKET_SIZE) return; // allow for chksum if (byteCount > MAX_PACKET_SIZE) return; // allow for chksum
RMTChannel *rmtchannel = (isMainTrack ? rmtMainChannel : rmtProgChannel);
if (rmtchannel == NULL)
return; // no idea to prepare packet if we can not send it anyway
rmtchannel->waitForDataCopy(); // blocking wait so we can write into buffer
byte checksum = 0; byte checksum = 0;
for (byte b = 0; b < byteCount; b++) { for (byte b = 0; b < byteCount; b++) {
checksum ^= buffer[b]; checksum ^= buffer[b];
@ -296,13 +300,7 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea
{ {
int ret = 0; int ret = 0;
do { do {
if(isMainTrack) { ret = rmtchannel->RMTfillData(pendingPacket, pendingLength, pendingRepeats);
if (rmtMainChannel != NULL)
ret = rmtMainChannel->RMTfillData(pendingPacket, pendingLength, pendingRepeats);
} else {
if (rmtProgChannel != NULL)
ret = rmtProgChannel->RMTfillData(pendingPacket, pendingLength, pendingRepeats);
}
} while(ret > 0); } while(ret > 0);
} }
} }

View File

@ -1,3 +1,23 @@
/*
* © 2021 Fred Decker
* 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/>.
*/
#ifndef EXRAIL_H #ifndef EXRAIL_H
#define EXRAIL_H #define EXRAIL_H

View File

@ -4,6 +4,7 @@
* © 2021-2023 Harald Barth * © 2021-2023 Harald Barth
* © 2020-2023 Chris Harlow * © 2020-2023 Chris Harlow
* © 2022-2023 Colin Murdoch * © 2022-2023 Colin Murdoch
* © 2025 Morten Nielsen
* All rights reserved. * All rights reserved.
* *
* This file is part of CommandStation-EX * This file is part of CommandStation-EX
@ -677,13 +678,14 @@ void RMFT2::loop2() {
break; break;
case OPCODE_SET: case OPCODE_SET:
killBlinkOnVpin(operand);
IODevice::write(operand,true);
break;
case OPCODE_RESET: case OPCODE_RESET:
killBlinkOnVpin(operand); {
IODevice::write(operand,false); auto count=getOperand(1);
for (uint16_t i=0;i<count;i++) {
killBlinkOnVpin(operand+i);
IODevice::write(operand+i,opcode==OPCODE_SET);
}
}
break; break;
case OPCODE_BLINK: case OPCODE_BLINK:
@ -870,6 +872,14 @@ void RMFT2::loop2() {
DCC::changeFn(operand,getOperand(1)); DCC::changeFn(operand,getOperand(1));
break; break;
case OPCODE_XFWD:
DCC::setThrottle(operand,getOperand(1), true);
break;
case OPCODE_XREV:
DCC::setThrottle(operand,getOperand(1), false);
break;
case OPCODE_DCCACTIVATE: { case OPCODE_DCCACTIVATE: {
// operand is address<<3 | subaddr<<1 | active // operand is address<<3 | subaddr<<1 | active
int16_t addr=operand>>3; int16_t addr=operand>>3;
@ -921,8 +931,9 @@ void RMFT2::loop2() {
#ifndef DISABLE_PROG #ifndef DISABLE_PROG
case OPCODE_JOIN: case OPCODE_JOIN:
TrackManager::setPower(POWERMODE::ON);
TrackManager::setJoin(true); TrackManager::setJoin(true);
TrackManager::setMainPower(POWERMODE::ON);
TrackManager::setProgPower(POWERMODE::ON);
break; break;
case OPCODE_UNJOIN: case OPCODE_UNJOIN:
@ -1327,7 +1338,7 @@ void RMFT2::clockEvent(int16_t clocktime, bool change) {
void RMFT2::powerEvent(int16_t track, bool overload) { void RMFT2::powerEvent(int16_t track, bool overload) {
// Hunt for an ONOVERLOAD for this item // Hunt for an ONOVERLOAD for this item
if (Diag::CMD) if (Diag::CMD)
DIAG(F("powerEvent : %c"), track); DIAG(F("powerEvent : %c"), track + 'A');
if (overload) { if (overload) {
onOverloadLookup->handleEvent(F("POWER"),track); onOverloadLookup->handleEvent(F("POWER"),track);
} }

View File

@ -3,6 +3,7 @@
* © 2020-2022 Chris Harlow * © 2020-2022 Chris Harlow
* © 2022-2023 Colin Murdoch * © 2022-2023 Colin Murdoch
* © 2023 Harald Barth * © 2023 Harald Barth
* © 2025 Morten Nielsen
* All rights reserved. * All rights reserved.
* *
* This file is part of CommandStation-EX * This file is part of CommandStation-EX
@ -45,7 +46,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,OPCODE_TOGGLE_TURNOUT,
OPCODE_ENDIF,OPCODE_ELSE, OPCODE_ENDIF,OPCODE_ELSE,
OPCODE_DELAY,OPCODE_DELAYMINS,OPCODE_DELAYMS,OPCODE_RANDWAIT, OPCODE_DELAY,OPCODE_DELAYMINS,OPCODE_DELAYMS,OPCODE_RANDWAIT,
OPCODE_FON,OPCODE_FOFF,OPCODE_XFON,OPCODE_XFOFF, OPCODE_FON,OPCODE_FOFF,OPCODE_XFON,OPCODE_XFOFF,
OPCODE_FTOGGLE,OPCODE_XFTOGGLE, OPCODE_FTOGGLE,OPCODE_XFTOGGLE,OPCODE_XFWD,OPCODE_XREV,
OPCODE_RED,OPCODE_GREEN,OPCODE_AMBER,OPCODE_DRIVE, OPCODE_RED,OPCODE_GREEN,OPCODE_AMBER,OPCODE_DRIVE,
OPCODE_SERVO,OPCODE_SIGNAL,OPCODE_TURNOUT,OPCODE_WAITFOR, OPCODE_SERVO,OPCODE_SIGNAL,OPCODE_TURNOUT,OPCODE_WAITFOR,
OPCODE_PAD,OPCODE_FOLLOW,OPCODE_CALL,OPCODE_RETURN, OPCODE_PAD,OPCODE_FOLLOW,OPCODE_CALL,OPCODE_RETURN,

View File

@ -2,6 +2,7 @@
* © 2020-2022 Chris Harlow. All rights reserved. * © 2020-2022 Chris Harlow. All rights reserved.
* © 2022-2023 Colin Murdoch * © 2022-2023 Colin Murdoch
* © 2023 Harald Barth * © 2023 Harald Barth
* © 2025 Morten Nielsen
* *
* This file is part of CommandStation-EX * This file is part of CommandStation-EX
* *
@ -194,6 +195,8 @@
#undef XFOFF #undef XFOFF
#undef XFON #undef XFON
#undef XFTOGGLE #undef XFTOGGLE
#undef XREV
#undef XFWD
#ifndef RMFT2_UNDEF_ONLY #ifndef RMFT2_UNDEF_ONLY
#define ACTIVATE(addr,subaddr) #define ACTIVATE(addr,subaddr)
@ -214,12 +217,12 @@
#define BROADCAST(msg) #define BROADCAST(msg)
#define CALL(route) #define CALL(route)
#define CLEAR_STASH(id) #define CLEAR_STASH(id)
#define CLEAR_ALL_STASH(id) #define CLEAR_ALL_STASH
#define CLOSE(id) #define CLOSE(id)
#define CONFIGURE_SERVO(vpin,pos1,pos2,profile) #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 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)
#define DELAY(mindelay) #define DELAY(mindelay)
@ -233,7 +236,7 @@
#define ENDTASK #define ENDTASK
#define ESTOP #define ESTOP
#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)
#define FOFF(func) #define FOFF(func)
#define FOLLOW(route) #define FOLLOW(route)
@ -309,7 +312,7 @@
#define READ_LOCO #define READ_LOCO
#define RED(signal_id) #define RED(signal_id)
#define RESERVE(blockid) #define RESERVE(blockid)
#define RESET(pin) #define RESET(pin,count...)
#define RESUME #define RESUME
#define RETURN #define RETURN
#define REV(speed) #define REV(speed)
@ -335,7 +338,7 @@
#define SERVO2(id,position,duration) #define SERVO2(id,position,duration)
#define SERVO_SIGNAL(vpin,redpos,amberpos,greenpos) #define SERVO_SIGNAL(vpin,redpos,amberpos,greenpos)
#define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) #define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...)
#define SET(pin) #define SET(pin,count...)
#define SET_TRACK(track,mode) #define SET_TRACK(track,mode)
#define SET_POWER(track,onoff) #define SET_POWER(track,onoff)
#define SETLOCO(loco) #define SETLOCO(loco)
@ -365,5 +368,7 @@
#define XFOFF(cab,func) #define XFOFF(cab,func)
#define XFON(cab,func) #define XFON(cab,func)
#define XFTOGGLE(cab,func) #define XFTOGGLE(cab,func)
#define XFWD(cab,speed)
#define XREV(cab,speed)
#endif #endif

View File

@ -3,6 +3,7 @@
* © 2020-2022 Chris Harlow * © 2020-2022 Chris Harlow
* © 2022-2023 Colin Murdoch * © 2022-2023 Colin Murdoch
* © 2023 Harald Barth * © 2023 Harald Barth
* © 2025 Morten Nielsen
* All rights reserved. * All rights reserved.
* *
* This file is part of CommandStation-EX * This file is part of CommandStation-EX
@ -516,7 +517,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#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),
#endif #endif
#define FADE(pin,value,ms) OPCODE_SERVO,V(pin),OPCODE_PAD,V(value),OPCODE_PAD,V(PCA9685::ProfileType::UseDuration|PCA9685::NoPowerOff),OPCODE_PAD,V(ms/100L), #define FADE(pin,value,ms) OPCODE_SERVO,V(pin),OPCODE_PAD,V(value),OPCODE_PAD,V((int16_t)PCA9685::ProfileType::UseDuration|(int16_t)PCA9685::ProfileType::NoPowerOff),OPCODE_PAD,V(ms/100L),
#define FOFF(func) OPCODE_FOFF,V(func), #define FOFF(func) OPCODE_FOFF,V(func),
#define FOLLOW(route) OPCODE_FOLLOW,V(route), #define FOLLOW(route) OPCODE_FOLLOW,V(route),
#define FON(func) OPCODE_FON,V(func), #define FON(func) OPCODE_FON,V(func),
@ -607,7 +608,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define READ_LOCO OPCODE_READ_LOCO1,0,0,OPCODE_READ_LOCO2,0,0, #define READ_LOCO OPCODE_READ_LOCO1,0,0,OPCODE_READ_LOCO2,0,0,
#define RED(signal_id) OPCODE_RED,V(signal_id), #define RED(signal_id) OPCODE_RED,V(signal_id),
#define RESERVE(blockid) OPCODE_RESERVE,V(blockid), #define RESERVE(blockid) OPCODE_RESERVE,V(blockid),
#define RESET(pin) OPCODE_RESET,V(pin), #define RESET(pin,count...) OPCODE_RESET,V(pin),OPCODE_PAD,V(#count[0] ? count+0: 1),
#define RESUME OPCODE_RESUME,0,0, #define RESUME OPCODE_RESUME,0,0,
#define RETURN OPCODE_RETURN,0,0, #define RETURN OPCODE_RETURN,0,0,
#define REV(speed) OPCODE_REV,V(speed), #define REV(speed) OPCODE_REV,V(speed),
@ -635,7 +636,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define SERVO2(id,position,ms) OPCODE_SERVO,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(PCA9685::Instant),OPCODE_PAD,V(ms/100L), #define SERVO2(id,position,ms) OPCODE_SERVO,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(PCA9685::Instant),OPCODE_PAD,V(ms/100L),
#define SERVO_SIGNAL(vpin,redpos,amberpos,greenpos) #define SERVO_SIGNAL(vpin,redpos,amberpos,greenpos)
#define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) OPCODE_SERVOTURNOUT,V(id),OPCODE_PAD,V(pin),OPCODE_PAD,V(activeAngle),OPCODE_PAD,V(inactiveAngle),OPCODE_PAD,V(PCA9685::ProfileType::profile), #define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) OPCODE_SERVOTURNOUT,V(id),OPCODE_PAD,V(pin),OPCODE_PAD,V(activeAngle),OPCODE_PAD,V(inactiveAngle),OPCODE_PAD,V(PCA9685::ProfileType::profile),
#define SET(pin) OPCODE_SET,V(pin), #define SET(pin,count...) OPCODE_SET,V(pin),OPCODE_PAD,V(#count[0] ? count+0: 1),
#define SET_TRACK(track,mode) OPCODE_SET_TRACK,V(TRACK_MODE_##mode <<8 | TRACK_NUMBER_##track), #define SET_TRACK(track,mode) OPCODE_SET_TRACK,V(TRACK_MODE_##mode <<8 | TRACK_NUMBER_##track),
#define SET_POWER(track,onoff) OPCODE_SET_POWER,V(TRACK_POWER_##onoff),OPCODE_PAD, V(TRACK_NUMBER_##track), #define SET_POWER(track,onoff) OPCODE_SET_POWER,V(TRACK_POWER_##onoff),OPCODE_PAD, V(TRACK_NUMBER_##track),
#define SETLOCO(loco) OPCODE_SETLOCO,V(loco), #define SETLOCO(loco) OPCODE_SETLOCO,V(loco),
@ -665,6 +666,8 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define XFOFF(cab,func) OPCODE_XFOFF,V(cab),OPCODE_PAD,V(func), #define XFOFF(cab,func) OPCODE_XFOFF,V(cab),OPCODE_PAD,V(func),
#define XFON(cab,func) OPCODE_XFON,V(cab),OPCODE_PAD,V(func), #define XFON(cab,func) OPCODE_XFON,V(cab),OPCODE_PAD,V(func),
#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 XREV(cab,speed) OPCODE_XREV,V(cab),OPCODE_PAD,V(speed),
// Build RouteCode // Build RouteCode
const int StringMacroTracker2=__COUNTER__; const int StringMacroTracker2=__COUNTER__;

View File

@ -1 +1 @@
#define GITHUB_SHA "devel-202411091200Z" #define GITHUB_SHA "c389fe9"

View File

@ -38,6 +38,7 @@
#include "FSH.h" #include "FSH.h"
#include "I2CManager.h" #include "I2CManager.h"
#include "inttypes.h" #include "inttypes.h"
#include "TemplateForEnums.h"
typedef uint16_t VPIN; typedef uint16_t VPIN;
// Limit VPIN number to max 32767. Above this number, printing often gives negative values. // Limit VPIN number to max 32767. Above this number, printing often gives negative values.

View File

@ -121,6 +121,7 @@ void _begin() {
// Configuration isn't done frequently so we can use blocking I2C calls here, and so buffers can // Configuration isn't done frequently so we can use blocking I2C calls here, and so buffers can
// be allocated from the stack to reduce RAM allocation. // be allocated from the stack to reduce RAM allocation.
bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override { bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override {
(void)configType; (void)params; // unused
if(_verPrint) DIAG(F("_configure() driver IO_EXSensorCAM v0.%d.%d vpin: %d "), driverVer/100,driverVer%100,vpin); if(_verPrint) DIAG(F("_configure() driver IO_EXSensorCAM v0.%d.%d vpin: %d "), driverVer/100,driverVer%100,vpin);
_verPrint=false; //only give driver versions once _verPrint=false; //only give driver versions once
if (paramCount != 1) return false; if (paramCount != 1) return false;

View File

@ -129,7 +129,7 @@ class NeoPixel : public IODevice {
public: public:
static void create(VPIN vpin, int nPins, uint16_t mode=(NEO_GRB | NEO_KHZ800), I2CAddress i2cAddress=0x60) { static void create(VPIN vpin, int nPins, uint16_t mode=(NEO_GRB | NEO_KHZ800), I2CAddress i2cAddress=0x60) {
if (checkNoOverlap(vpin, nPins, mode, i2cAddress)) new NeoPixel(vpin, nPins, mode, i2cAddress); if (checkNoOverlap(vpin, nPins, i2cAddress)) new NeoPixel(vpin, nPins, mode, i2cAddress);
} }
private: private:

View File

@ -28,12 +28,9 @@
#include "DCCTimer.h" #include "DCCTimer.h"
#include <wiring_private.h> #include <wiring_private.h>
#include "TemplateForEnums.h"
// use powers of two so we can do logical and/or on the track modes in if clauses. // use powers of two so we can do logical and/or on the track modes in if clauses.
// For example TRACK_MODE_DC_INV is (TRACK_MODE_DC|TRACK_MODIFIER_INV) // For example TRACK_MODE_DC_INV is (TRACK_MODE_DC|TRACK_MODIFIER_INV)
template<class T> inline T operator~ (T a) { return (T)~(int)a; }
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 { enum TRACK_MODE : byte {
// main modes // main modes
TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4, TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4,

View File

@ -1,77 +1,39 @@
# What is DCC++ EX? # What is DCC-EX?
DCC++ EX is the organization maintaining several codebases that together represent a fully open source DCC system. Currently, this includes the following: DCC-EX is a team of dedicated enthusiasts producing open source DCC & DC solutions for you to run your complete model railroad layout. Our easy to use, do-it-yourself, and free open source products run on off-the-shelf Arduino technology and are supported by numerous third party hardware and apps like JMRI, Engine Driver, wiThrottle, Rocrail and more.
* [CommandStation-EX](https://github.com/DCC-EX/CommandStation-EX/releases) - the latest take on the DCC++ command station for controlling your trains. Runs on an Arduino board, and includes advanced features such as a WiThrottle server implementation, turnout operation, general purpose inputs and outputs (I/O), and JMRI integration. Currently, our products include the following:
* [exWebThrottle](https://github.com/DCC-EX/exWebThrottle) - a simple web based controller for your DCC++ command station.
* [BaseStation-installer](https://github.com/DCC-EX/BaseStation-Installer) - an installer executable that takes care of downloading and installing DCC++ firmware onto your hardware setup.
* [BaseStation-Classic](https://github.com/DCC-EX/BaseStation-Classic) - the original DCC++ software, packaged in a stable release. No active development, bug fixes only.
A basic DCC++ EX hardware setup can use easy to find, widely avalable Arduino boards that you can assemble yourself. * [EX-CommandStation](https://github.com/DCC-EX/CommandStation-EX/releases)
* [EX-WebThrottle](https://github.com/DCC-EX/exWebThrottle)
Both CommandStation-EX and BaseStation-Classic support much of the NMRA Digital Command Control (DCC) [standards](http://www.nmra.org/dcc-working-group "NMRA DCC Working Group"), including: * [EX-Installer](https://github.com/DCC-EX/EX-Installer)
* [EX-MotoShield8874](https://dcc-ex.com/reference/hardware/motorboards/ex-motor-shield-8874.html#gsc.tab=0)
* simultaneous control of multiple locomotives * [EX-DCCInspector](https://github.com/DCC-EX/DCCInspector-EX)
* 2-byte and 4-byte locomotive addressing * [EX-Toolbox](https://github.com/DCC-EX/EX-Toolbox)
* 28 or 128-step speed throttling * [EX-Turntable](https://github.com/DCC-EX/EX-Turntable)
* Activate/de-activate all accessory function addresses 0-2048 * [EX-IOExpander](https://github.com/DCC-EX/EX-IOExpander)
* Control of all cab functions F0-F28 and F29-F68 * [EX-FastClock](https://github.com/DCC-EX/EX-FastClock)
* Main Track: Write configuration variable bytes and set/clear specific configuration variable (CV) bits (aka Programming on Main or POM) * [DCCEXProtocol](https://github.com/DCC-EX/DCCEXProtocol)
* Programming Track: Same as the main track with the addition of reading configuration variable bytes
* And many more custom features. see [What's new in CommandStation-EX?](#whats-new-in-commandstation-ex)
Details of these projects can be found on [our web site](https://dcc-ex.com/).
# Whats in this Repository? # Whats in this Repository?
This repository, CommandStation-EX, contains a complete DCC++ EX Commmand Station sketch designed for compiling and uploading into an Arduino Uno, Mega, or Nano. This repository, CommandStation-EX, contains a complete DCC-EX *EX-CommmandStation* sketch designed for compiling and uploading into an Arduino Uno, Mega, or Nano.
To utilize this sketch, you can use the following: To utilize this sketch, you can use the following:
1. (beginner) our [automated installer](https://github.com/DCC-EX/BaseStation-Installer) 1. (recommended for all levels of user) our [automated installer](https://github.com/DCC-EX/EX-Installer)
2. (intermediate) download the latest version from the [releases page](https://github.com/DCC-EX/CommandStation-EX/releases) 2. (intermediate) download the latest version from the [releases page](https://github.com/DCC-EX/CommandStation-EX/releases)
3. (advanced) use git clone on this repository 3. (advanced) use git clone on this repository
Not using the installer? Open the file "CommandStation-EX.ino" in the Refer to [our web site](https://https://dcc-ex.com/ex-commandstation/get-started/index.html#/) for the hardware required for this project.
Arduino IDE. Please do not rename the folder containing the sketch
code, nor add any files in that folder. The Arduino IDE relies on the
structure and name of the folder to properly display and compile the
code. Rename or copy config.example.h to config.h. If you do not have
the standard setup, you must edit config.h according to the help texts
in config.h.
## What's new in CommandStation-EX? **We seriously recommend using the EX-Installer**, however if you choose not to use the installer...
* WiThrottle server built in. Connect Engine Driver or WiThrottle clients directly to your Command Station (or through JMRI as before) * Open the file ``CommandStation-EX.ino`` in the Arduino IDE or Visual Studio Code (VSC). Please do not rename the folder containing the sketch code, nor add any files in that folder. The Arduino IDE relies on the structure and name of the folder to properly display and compile the code.
* WiFi and Ethernet shield support * Rename or copy ``config.example.h`` to ``config.h``.
* No more jumpers or soldering! * You must edit ``config.h`` according to the help texts in ``config.h``.
* Direct support for all the most popular motor control boards including single pin (Arduino) or dual pin (IBT_2) type PWM inputs without the need for an adapter circuit
* I2C Display support (LCD and OLED)
* Improved short circuit detection and automatic reset from an overload
* Current reading, sensing and ACK detection settings in milliAmps instead of just pin readings
* Improved adherence to the NMRA DCC specification
* Complete support for all the old commands and front ends like JMRI
* Railcom cutout (beta)
* Simpler, modular, faster code with an API Library for developers for easy expansion
* New features and functions in JMRI
* Ability to join MAIN and PROG tracks into one MAIN track to run your locos
* "Drive-Away" feature - Throttles with support, like Engine Driver, can allow a loco to be programmed on a usable, electrically isolated programming track and then drive off onto the main track
* Diagnostic commands to test decoders that aren't reading or writing correctly
* Support for Uno, Nano, Mega, Nano Every and Teensy microcontrollers
* User Functions: Filter regular commands (like a turnout or output command) and pass it to your own function or accessory
* Support for LCN (layout control nodes)
* mySetup.h file that acts like an Autoexec.Bat command to send startup commands to the CS
* High Accuracty Waveform option for rock steady DCC signals
* New current response outputs current in mA, overlimit current, and maximum board capable current. Support for new current meter in JMRI
* USB Browser based EX-WebThrottle
* New, simpler, function control command
* Number of locos discovery command `<#>`
* Emergency stop command <!>
* Release cabs from memory command <-> all cabs, <- CAB> for just one loco address
* Automatic slot (register) management
* Automation (coming soon)
NOTE: DCC-EX is a major rewrite to the code. We started over and rebuilt it from the ground up! For what that means, you can read [HERE](https://dcc-ex.com/about/rewrite.html).
# More information # More information
You can learn more at the [DCC++ EX website](https://dcc-ex.com/) You can learn more at the [DCC-EX website](https://dcc-ex.com/)
- November 14, 2020

Binary file not shown.

View File

@ -126,29 +126,33 @@ void SerialManager::loop2() {
buffer[0] = '\0'; buffer[0] = '\0';
} }
} else { // if (inCommandPayload) } else { // if (inCommandPayload)
if (bufferLength < (COMMAND_BUFFER_SIZE-1)) if (bufferLength < (COMMAND_BUFFER_SIZE-1)) {
buffer[bufferLength++] = ch; buffer[bufferLength++] = ch; // advance bufferLength
if (inCommandPayload > PAYLOAD_NORMAL) { if (inCommandPayload > PAYLOAD_NORMAL) {
if (inCommandPayload > 32 + 2) { // String way too long if (inCommandPayload > 32 + 2) { // String way too long
ch = '>'; // we end this nonsense ch = '>'; // we end this nonsense
inCommandPayload = PAYLOAD_NORMAL; inCommandPayload = PAYLOAD_NORMAL;
DIAG(F("Parse error: Unbalanced string")); DIAG(F("Parse error: Unbalanced string"));
// fall through to ending parsing below // fall through to ending parsing below
} else if (ch == '"') { // String end } else if (ch == '"') { // String end
inCommandPayload = PAYLOAD_NORMAL; inCommandPayload = PAYLOAD_NORMAL;
continue; // do not fall through continue; // do not fall through
} else } else
inCommandPayload++; inCommandPayload++;
} }
if (inCommandPayload == PAYLOAD_NORMAL) { if (inCommandPayload == PAYLOAD_NORMAL) {
if (ch == '>') { if (ch == '>') {
buffer[bufferLength] = '\0'; buffer[bufferLength] = '\0'; // This \0 is after the '>'
DCCEXParser::parse(serial, buffer, NULL); DCCEXParser::parse(serial, buffer, NULL); // buffer parsed with trailing '>'
inCommandPayload = PAYLOAD_FALSE; inCommandPayload = PAYLOAD_FALSE;
break; break;
} else if (ch == '"') { } else if (ch == '"') {
inCommandPayload = PAYLOAD_STRING; inCommandPayload = PAYLOAD_STRING;
} }
}
} else {
DIAG(F("Parse error: input buffer overflow"));
inCommandPayload = PAYLOAD_FALSE;
} }
} }
} }

26
TemplateForEnums.h Normal file
View File

@ -0,0 +1,26 @@
/*
* © 2024, Harald Barth. All rights reserved.
*
* This file is part of DCC-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/>.
*/
#ifndef TemplateForEnums
#define TemplateForEnums
template<class T> inline T operator~ (T a) { return (T)~(int)a; }
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); }
#endif

View File

@ -246,9 +246,6 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
#endif #endif
#ifndef DISABLE_PROG #ifndef DISABLE_PROG
if (mode & TRACK_MODE_PROG) { if (mode & TRACK_MODE_PROG) {
#else
if (false) {
#endif
// only allow 1 track to be prog // only allow 1 track to be prog
FOR_EACH_TRACK(t) FOR_EACH_TRACK(t)
if ( (track[t]->getMode() & TRACK_MODE_PROG) && t != trackToSet) { if ( (track[t]->getMode() & TRACK_MODE_PROG) && t != trackToSet) {
@ -261,6 +258,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
} else { } else {
track[trackToSet]->makeProgTrack(false); // only the prog track knows it's type track[trackToSet]->makeProgTrack(false); // only the prog track knows it's type
} }
#endif
// When a track is switched, we must clear any side effects of its previous // When a track is switched, we must clear any side effects of its previous
// state, otherwise trains run away or just dont move. // state, otherwise trains run away or just dont move.
@ -358,11 +356,24 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
applyDCSpeed(trackToSet); applyDCSpeed(trackToSet);
} }
#ifdef ARDUINO_ARCH_ESP32
#ifndef DISABLE_PROG
if (tempProgTrack == trackToSet && oldmode & TRACK_MODE_MAIN && !(mode & TRACK_MODE_PROG)) {
// If we just take away the prog track, the join should not
// be active either. So do in effect an unjoin
//DIAG(F("Unsync"));
tempProgTrack = MAX_TRACKS+1;
progTrackSyncMain=false;
if (joinRelay!=UNUSED_PIN) digitalWrite(joinRelay,LOW);
}
#endif
#endif
// Turn off power if we changed the mode of this track // Turn off power if we changed the mode of this track
if (mode != oldmode) if (mode != oldmode) {
track[trackToSet]->setPower(POWERMODE::OFF); track[trackToSet]->setPower(POWERMODE::OFF);
streamTrackState(NULL,trackToSet); }
streamTrackState(NULL,trackToSet);
//DIAG(F("TrackMode=%d"),mode); //DIAG(F("TrackMode=%d"),mode);
return true; return true;
} }
@ -657,7 +668,8 @@ void TrackManager::setJoin(bool joined) {
if (track[t]->getMode() & TRACK_MODE_PROG) { // find PROG track if (track[t]->getMode() & TRACK_MODE_PROG) { // find PROG track
tempProgTrack = t; // remember PROG track tempProgTrack = t; // remember PROG track
setTrackMode(t, TRACK_MODE_MAIN); setTrackMode(t, TRACK_MODE_MAIN);
track[t]->setPower(POWERMODE::ON); // if joined, always on // setPower() of the track called after
// seperately after setJoin() instead
break; // there is only one prog track, done break; // there is only one prog track, done
} }
} }
@ -669,8 +681,6 @@ void TrackManager::setJoin(bool joined) {
setTrackMode(tempProgTrack, TRACK_MODE_PROG); // set track mode back to prog setTrackMode(tempProgTrack, TRACK_MODE_PROG); // set track mode back to prog
track[tempProgTrack]->setPower(tPTmode); // set power status as it was before track[tempProgTrack]->setPower(tPTmode); // set power status as it was before
tempProgTrack = MAX_TRACKS+1; tempProgTrack = MAX_TRACKS+1;
} else {
DIAG(F("Unjoin but no remembered prog track"));
} }
} }
#endif #endif

View File

@ -312,12 +312,6 @@
* *
*************************************************************************************/ *************************************************************************************/
#if defined(DCC_TURNOUTS_RCN_213)
const bool DCCTurnout::rcn213Compliant = true;
#else
const bool DCCTurnout::rcn213Compliant = false;
#endif
// DCCTurnoutData contains data specific to this subclass that is // DCCTurnoutData contains data specific to this subclass that is
// written to EEPROM when the turnout is saved. // written to EEPROM when the turnout is saved.
struct DCCTurnoutData { struct DCCTurnoutData {
@ -385,7 +379,10 @@
// DCC++ Classic behaviour is that Throw writes a 1 in the packet, // DCC++ Classic behaviour is that Throw writes a 1 in the packet,
// and Close writes a 0. // and Close writes a 0.
// RCN-213 specifies that Throw is 0 and Close is 1. // RCN-213 specifies that Throw is 0 and Close is 1.
DCC::setAccessory(_dccTurnoutData.address, _dccTurnoutData.subAddress, close ^ !rcn213Compliant); #ifndef DCC_TURNOUTS_RCN_213
close = !close;
#endif
DCC::setAccessory(_dccTurnoutData.address, _dccTurnoutData.subAddress, close);
return true; return true;
} }

View File

@ -245,8 +245,6 @@ public:
// Load a VPIN turnout definition from EEPROM. The common Turnout data has already been read at this point. // Load a VPIN turnout definition from EEPROM. The common Turnout data has already been read at this point.
static Turnout *load(struct TurnoutData *turnoutData); static Turnout *load(struct TurnoutData *turnoutData);
void print(Print *stream) override; void print(Print *stream) override;
// Flag whether DCC Accessory packets are to contain 1=close/0=throw(RCN-213) or 1=throw/0-close (DCC++ Classic)
static const bool rcn213Compliant;
protected: protected:
bool setClosedInternal(bool close) override; bool setClosedInternal(bool close) override;

View File

@ -500,9 +500,9 @@ void WiThrottle::getLocoCallback(int16_t locoid) {
char addcmd[20]={'M',stashThrottleChar,'+', addrchar}; char addcmd[20]={'M',stashThrottleChar,'+', addrchar};
itoa(locoid,addcmd+4,10); itoa(locoid,addcmd+4,10);
stashInstance->multithrottle(stashStream, (byte *)addcmd); stashInstance->multithrottle(stashStream, (byte *)addcmd);
TrackManager::setJoin(true); // <1 JOIN> so we can drive loco away
TrackManager::setMainPower(POWERMODE::ON); TrackManager::setMainPower(POWERMODE::ON);
TrackManager::setProgPower(POWERMODE::ON); TrackManager::setProgPower(POWERMODE::ON);
TrackManager::setJoin(true); // <1 JOIN> so we can drive loco away
DIAG(F("LocoCallback commit success")); DIAG(F("LocoCallback commit success"));
stashStream->commit(); stashStream->commit();
} }

View File

@ -1,7 +1,7 @@
/* /*
* © 2022 Paul M. Antoine * © 2022 Paul M. Antoine
* © 2021 Neil McKechnie * © 2021 Neil McKechnie
* © 2020-2023 Harald Barth * © 2020-2025 Harald Barth
* © 2020-2021 Fred Decker * © 2020-2021 Fred Decker
* © 2020-2021 Chris Harlow * © 2020-2021 Chris Harlow
* © 2023 Nathan Kellenicki * © 2023 Nathan Kellenicki
@ -45,15 +45,14 @@ The configuration file for DCC-EX Command Station
// the correct resistor could damage the sense pin on your Arduino or destroy // the correct resistor could damage the sense pin on your Arduino or destroy
// the device. // the device.
// //
// DEFINE MOTOR_SHIELD_TYPE BELOW. THESE ARE EXAMPLES. FULL LIST IN MotorDrivers.h // DEFINE MOTOR_SHIELD_TYPE BELOW. THESE ARE EXAMPLES. Full list in MotorDrivers.h
// //
// STANDARD_MOTOR_SHIELD : Arduino Motor shield Rev3 based on the L298 with 18V 2A per channel // STANDARD_MOTOR_SHIELD : Arduino Motor shield Rev3 based on the L298 with 18V 2A per channel
// POLOLU_MOTOR_SHIELD : Pololu MC33926 Motor Driver (not recommended for prog track) // POLOLU_MOTOR_SHIELD : Pololu MC33926 Motor Driver (not recommended for prog track)
// FUNDUMOTO_SHIELD : Fundumoto Shield, no current sensing (not recommended, no short protection)
// FIREBOX_MK1 : The Firebox MK1
// FIREBOX_MK1S : The Firebox MK1S
// IBT_2_WITH_ARDUINO : Arduino Motor Shield for PROG and IBT-2 for MAIN
// EX8874_SHIELD : DCC-EX TI DRV8874 based motor shield // EX8874_SHIELD : DCC-EX TI DRV8874 based motor shield
// EXCSB1 : DCC-EX CSB-1 hardware
// EXCSB1_WITH_EX8874 : DCC-EX CSB-1 hardware with DCC-EX TI DRV8874 shield
// NO_SHIELD : CS without any motor shield (as an accessory only CS)
// | // |
// +-----------------------v // +-----------------------v
// //
@ -81,7 +80,7 @@ The configuration file for DCC-EX Command Station
///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////
// //
// NOTE: Only supported on Arduino Mega // NOTE: Not supported on Arduino Uno or Nano
// Set to false if you not even want it on the Arduino Mega // Set to false if you not even want it on the Arduino Mega
// //
#define ENABLE_WIFI true #define ENABLE_WIFI true
@ -116,13 +115,13 @@ The configuration file for DCC-EX Command Station
// Your password may not contain ``"'' (double quote, ASCII 0x22). // Your password may not contain ``"'' (double quote, ASCII 0x22).
#define WIFI_PASSWORD "Your network passwd" #define WIFI_PASSWORD "Your network passwd"
// //
// WIFI_HOSTNAME: You probably don't need to change this // WIFI_HOSTNAME: You can change this if you have more than one
// CS to make them show up with different names on the network.
// Otherwise do not touch.
#define WIFI_HOSTNAME "dccex" #define WIFI_HOSTNAME "dccex"
// //
// WIFI_CHANNEL: If the line "#define ENABLE_WIFI true" is uncommented, // WIFI_CHANNEL: The default channel is set to "1". If you need to use an
// WiFi will be enabled (Mega only). The default channel is set to "1" whether // alternate channel (we recommend using only 1,6, or 11) you may change it here.
// this line exists or not. If you need to use an alternate channel (we recommend
// using only 1,6, or 11) you may change it here.
#define WIFI_CHANNEL 1 #define WIFI_CHANNEL 1
// //
// WIFI_FORCE_AP: If you'd like to specify your own WIFI_SSID in AP mode, set this // WIFI_FORCE_AP: If you'd like to specify your own WIFI_SSID in AP mode, set this
@ -132,8 +131,9 @@ The configuration file for DCC-EX Command Station
///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////
// //
// ENABLE_ETHERNET: Set to true if you have an Arduino Ethernet card (wired). This // ENABLE_ETHERNET: Set to true if you have an Arduino Ethernet card (wired) based
// is not for Wifi. You will then need the Arduino Ethernet library as well // on the W5100/W5500 ethernet chip or an STM32 CS with builin ethernet like the F429ZI.
// This is not for Wifi. You will then need the Arduino Ethernet library as well.
// //
//#define ENABLE_ETHERNET true //#define ENABLE_ETHERNET true
@ -270,8 +270,9 @@ The configuration file for DCC-EX Command Station
// for triggering DCC Accessory Decoders, so that <a addr subaddr 0> generates a // for triggering DCC Accessory Decoders, so that <a addr subaddr 0> generates a
// DCC packet with D=1 (close turnout) and <a addr subaddr 1> generates D=0 // DCC packet with D=1 (close turnout) and <a addr subaddr 1> generates D=0
// (throw turnout). // (throw turnout).
//#define DCC_ACCESSORY_RCN_213 //#define DCC_ACCESSORY_COMMAND_REVERSE
//
// HANDLING MULTIPLE SERIAL THROTTLES // HANDLING MULTIPLE SERIAL THROTTLES
// The command station always operates with the default Serial port. // The command station always operates with the default Serial port.
// Diagnostics are only emitted on the default serial port and not broadcast. // Diagnostics are only emitted on the default serial port and not broadcast.
@ -333,7 +334,7 @@ The configuration file for DCC-EX Command Station
// to the sabertooth controller _as_well_. Default: Undefined. // to the sabertooth controller _as_well_. Default: Undefined.
// //
//#define SABERTOOTH 1 //#define SABERTOOTH 1
//
///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////
// //
// SENSORCAM // SENSORCAM
@ -345,7 +346,7 @@ The configuration file for DCC-EX Command Station
//#define SENSORCAM2_VPIN 600 //define other CAM's if installed. //#define SENSORCAM2_VPIN 600 //define other CAM's if installed.
//#define CAM2 SENSORCAM2_VPIN+ //for EX-RAIL commands e.g. IFLT(CAM2 020,1) //#define CAM2 SENSORCAM2_VPIN+ //for EX-RAIL commands e.g. IFLT(CAM2 020,1)
// //
// For smoother power-up, define a STARTUP_DELAY to allow CAM to initialise ref images // For smoother power-up, when using the CAM, you may need a STARTUP_DELAY.
//#define STARTUP_DELAY 5000 // up to 20sec. CS delay // That is described further above.
// //
///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////

View File

@ -12,18 +12,10 @@
default_envs = default_envs =
mega2560 mega2560
uno uno
unowifiR2
nano nano
samd21-dev-usb
samd21-zero-usb
ESP32 ESP32
Nucleo-F411RE Nucleo-F411RE
Nucleo-F446RE Nucleo-F446RE
Teensy3_2
Teensy3_5
Teensy3_6
Teensy4_0
Teensy4_1
src_dir = . src_dir = .
include_dir = . include_dir = .

View File

@ -3,7 +3,24 @@
#include "StringFormatter.h" #include "StringFormatter.h"
#define VERSION "5.2.88" #define VERSION "5.4.7"
// 5.4.7 - Bugfix: EXRAIL fix CLEAR_ALL_STASH
// 5.4.6 - Bugfix: Do not drop further commands in same packet
// 5.4.5 - ESP32: Better detection of correct IDF version
// - track power is always turned on after setJoin() not by setJoin()
// 5.4.4 - bugfix in parser, input buffer overrun and trailing > that did break <+>
// 5.4.3 - bugfix changeFn for functions 29..31
// 5.4.2 - Reversed turnout bugfix
// 5.4.1 - ESP32 bugfix packet buffer race
// 5.4.0 - New version on master
// 5.2.96 - EXRAIL additions XFWD() and XREV()
// 5.2.95 - Release candidate for 5.4
// 5.2.94 - Bugfix: Less confusion and simpler code around the RCN213 defines
// 5.2.93 - Bugfix ESP32: clear progTrackSyncMain (join flag) when prog track is removed
// 5.2.92 - Bugfix: FADE power off fix, EXRAIL power diagnostic fix.
// 5.2.91 - Bugfix: Neopixel I2C overlap check
// 5.2.90 - Bugfix: EXRAIL EXTT_TURNTABLE() now has description as optional in line with ocumentation (also fixed DCC_TURNTABLE)
// 5.2.89 - EXRAIL SET(vpin[,npins]) RESET(vpin,[,npins]) pin range manipulation
// 5.2.88 - Fix bug where EX-Turntable objects return incorrect angle for home with <JP x> // 5.2.88 - Fix bug where EX-Turntable objects return incorrect angle for home with <JP x>
// 5.2.87 - CamParser and IO_EXSensorCam driver // 5.2.87 - CamParser and IO_EXSensorCam driver
// 5.2.86 - IO_TCA8418 driver for keypad matrix input now fully functioning, including being able to use an interrupt pin // 5.2.86 - IO_TCA8418 driver for keypad matrix input now fully functioning, including being able to use an interrupt pin