1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-07-28 18:03:45 +02:00

Compare commits

...

20 Commits

Author SHA1 Message Date
Asbelos
3b15491608 5.4.10 2025-04-28 09:30:24 +01:00
Asbelos
3fc3c2329a 5.4.9 CONSIST bad decoders 2025-04-22 09:38:51 +01:00
Harald Barth
c79e01056e idle packet count without checksum 2025-04-19 00:00:38 +02:00
Harald Barth
8ac61b88d4 version 5.4.8 2025-04-14 21:51:35 +02:00
Harald Barth
8aabede3ee treat all function groups equal; remove unused no reminders compile option 2025-04-14 21:27:51 +02:00
Harald Barth
839ea582a4 insert idle after speed packet only 2025-04-14 21:14:24 +02:00
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
14 changed files with 100 additions and 63 deletions

View File

@@ -280,6 +280,9 @@ void CommandDistributor::broadcastPower() {
state = '1';
}
if (state != '2')
broadcastReply(COMMAND_TYPE, F("<p%c>\n"),state);
// additional info about MAIN, PROG and JOIN
bool main=TrackManager::getMainPower()==POWERMODE::ON;
bool prog=TrackManager::getProgPower()==POWERMODE::ON;
@@ -288,7 +291,7 @@ void CommandDistributor::broadcastPower() {
const FSH * reason=F("");
if (join) {
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 {
if (main) {
//reason = F("MAIN");
@@ -299,9 +302,6 @@ void CommandDistributor::broadcastPower() {
broadcastReply(COMMAND_TYPE, F("<p1 PROG>\n"));
}
}
if (state != '2')
broadcastReply(COMMAND_TYPE, F("<p%c>\n"),state);
#ifdef CD_HANDLE_RING
// 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);

36
DCC.cpp
View File

@@ -523,6 +523,7 @@ const ackOp FLASH LOCO_ID_PROG[] = {
V0, WACK, MERGE,
V0, WACK, MERGE,
VB, WACK, NAKSKIP, // bad read of cv20, assume its 0
BAD20SKIP, // detect invalid cv20 value and ignore
STASHLOCOID, // keep cv 20 until we have cv19 as well.
SETCV, (ackOp)19,
STARTMERGE, // Setup to read cv 19
@@ -628,7 +629,9 @@ const ackOp FLASH CONSIST_ID_PROG[] = {
BASELINE,
SETCV,(ackOp)20,
SETBYTEH, // high byte to CV 20
WB,WACK, // ignore dedcoder without cv20 support
WB,WACK,ITSKIP,
FAIL_IF_NONZERO_NAK, // fail if writing long address to decoder that cant support it
SKIPTARGET,
SETCV,(ackOp)19,
SETBYTEL, // low byte of word
WB,WACK,ITC1, // If ACK, we are done - callback(1) means Ok
@@ -759,7 +762,15 @@ void DCC::issueReminders() {
if (!DCCWaveform::mainTrack.isReminderWindowOpen()) return;
// Move to next loco slot. If occupied, send a reminder.
int reg = lastLocoReminder+1;
if (reg > highestUsedReg) reg = 0; // Go to start of table
if (reg > highestUsedReg) {
if (loopStatus == 0 /*only needed if numLocos == 1 but we do not have a counter*/) {
// insert idle packet in the speed packet loop to fullfill the *censored*
// >5ms between packets to same decoder rule
const byte idlepacket[] = {0xFF, 0x00};
DCCWaveform::mainTrack.schedulePacket(idlepacket, 2, 0);
}
reg = 0; // Go to start of table
}
if (speedTable[reg].loco > 0) {
// have found loco to remind
if (issueReminder(reg))
@@ -780,40 +791,23 @@ bool DCC::issueReminder(int reg) {
break;
case 1: // remind function group 1 (F0-F4)
if (flags & FN_GROUP_1)
#ifndef DISABLE_FUNCTION_REMINDERS
setFunctionInternal(loco,0, 128 | ((functions>>1)& 0x0F) | ((functions & 0x01)<<4),0); // 100D DDDD
#else
setFunctionInternal(loco,0, 128 | ((functions>>1)& 0x0F) | ((functions & 0x01)<<4),2);
flags&= ~FN_GROUP_1; // dont send them again
#endif
break;
case 2: // remind function group 2 F5-F8
if (flags & FN_GROUP_2)
#ifndef DISABLE_FUNCTION_REMINDERS
setFunctionInternal(loco,0, 176 | ((functions>>5)& 0x0F),0); // 1011 DDDD
#else
setFunctionInternal(loco,0, 176 | ((functions>>5)& 0x0F),2);
flags&= ~FN_GROUP_2; // dont send them again
#endif
break;
case 3: // remind function group 3 F9-F12
if (flags & FN_GROUP_3)
#ifndef DISABLE_FUNCTION_REMINDERS
setFunctionInternal(loco,0, 160 | ((functions>>9)& 0x0F),0); // 1010 DDDD
#else
setFunctionInternal(loco,0, 160 | ((functions>>9)& 0x0F),2);
flags&= ~FN_GROUP_3; // dont send them again
#endif
break;
case 4: // remind function group 4 F13-F20
if (flags & FN_GROUP_4)
setFunctionInternal(loco,222, ((functions>>13)& 0xFF),2);
flags&= ~FN_GROUP_4; // dont send them again
setFunctionInternal(loco,222, ((functions>>13)& 0xFF),0);
break;
case 5: // remind function group 5 F21-F28
if (flags & FN_GROUP_5)
setFunctionInternal(loco,223, ((functions>>21)& 0xFF),2);
flags&= ~FN_GROUP_5; // dont send them again
setFunctionInternal(loco,223, ((functions>>21)& 0xFF),0);
break;
}
loopStatus++;

View File

@@ -347,6 +347,20 @@ void DCCACK::loop() {
opcode=GETFLASH(ackManagerProg);
}
break;
case BAD20SKIP:
if (ackManagerByte > 120) {
// skip to SKIPTARGET if cv20 is >120 (some decoders respond with 255)
if (Diag::ACK) DIAG(F("XX cv20=%d "),ackManagerByte);
while (opcode!=SKIPTARGET) {
ackManagerProg++;
opcode=GETFLASH(ackManagerProg);
}
}
break;
case FAIL_IF_NONZERO_NAK: // fail if writing long address to decoder that cant support it
if (ackManagerByte==0) break;
callback(-4);
return;
case SKIPTARGET:
break;
default:

View File

@@ -58,6 +58,8 @@ enum ackOp : byte
ITSKIP, // skip to SKIPTARGET if ack true
NAKSKIP, // skip to SKIPTARGET if ack false
COMBINE1920, // combine cvs 19 and 20 and callback
BAD20SKIP, // skip to SKIPTARGET if cv20 is >120 (some decoders respond with 255)
FAIL_IF_NONZERO_NAK, // fail if writing long address to decoder that cant support it
SKIPTARGET = 0xFF // jump to target
};

View File

@@ -167,8 +167,10 @@ int16_t DCCEXParser::splitValues(int16_t result[MAX_COMMAND_PARAMS], byte *cmd,
break;
if (hot == '\0')
return -1;
if (hot == '>')
if (hot == '>') {
*remainingCmd = '\0'; // terminate the cmd string with 0 instead of '>'
return parameterCount;
}
state = 2;
continue;
@@ -265,17 +267,22 @@ void DCCEXParser::parse(const FSH * cmd) {
// See documentation on DCC class for info on this section
void DCCEXParser::parse(Print *stream, byte *com, RingStream *ringStream) {
// 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
// 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 which results
// in parseOne() getting c="C OMM AND>"
byte *cForLater = NULL;
bool found = (com[0] != '<');
for (byte *c=com; c[0] != '\0'; c++) {
if (found) {
parseOne(stream, c, ringStream);
cForLater = c;
found=false;
}
if (c[0] == '<')
if (c[0] == '<') {
if (cForLater) parseOne(stream, cForLater, ringStream);
found = true;
}
}
if (cForLater) parseOne(stream, cForLater, ringStream);
}
void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
@@ -1445,6 +1452,7 @@ void DCCEXParser::callback_Wloco(int16_t result)
void DCCEXParser::callback_Wconsist(int16_t result)
{
if (result==-4) DIAG(F("Long Consist %d not supported by decoder"),stashP[1]);
if (result==1) result=stashP[1]; // pick up original requested id from command
StringFormatter::send(getAsyncReplyStream(), F("<w CONSIST %d%S>\n"),
result, stashP[2]=="REVERSE"_hk ? F(" REVERSE") : F(""));

View File

@@ -78,11 +78,17 @@ int DCCTimer::freeMemory() {
////////////////////////////////////////////////////////////////////////
#ifdef ARDUINO_ARCH_ESP32
#if __has_include("esp_idf_version.h")
#include "esp_idf_version.h"
#if ESP_IDF_VERSION_MAJOR > 4
#endif
#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
// protect all the rest of the code from IDF version 5
#if ESP_IDF_VERSION_MAJOR == 4
#include "DIAG.h"
#include <driver/adc.h>
#include <soc/sens_reg.h>
@@ -322,5 +328,5 @@ void ADCee::scan() {
void ADCee::begin() {
}
#endif //IDF v4
#endif //ESP32

View File

@@ -145,7 +145,7 @@ void DCCWaveform::interrupt2() {
// As we get to the end of the preambles, open the reminder window.
// This delays any reminder insertion until the last moment so
// that the reminder doesn't block a more urgent packet.
reminderWindowOpen=transmitRepeats==0 && remainingPreambles<4 && remainingPreambles>1;
reminderWindowOpen=transmitRepeats==0 && remainingPreambles<12 && remainingPreambles>1;
if (remainingPreambles==1) promotePendingPacket();
else if (remainingPreambles==10 && isMainTrack && railcomActive) DCCTimer::ackRailcomTimer();
// Update free memory diagnostic as we don't have anything else to do this time.

View File

@@ -931,8 +931,9 @@ void RMFT2::loop2() {
#ifndef DISABLE_PROG
case OPCODE_JOIN:
TrackManager::setPower(POWERMODE::ON);
TrackManager::setJoin(true);
TrackManager::setMainPower(POWERMODE::ON);
TrackManager::setProgPower(POWERMODE::ON);
break;
case OPCODE_UNJOIN:

View File

@@ -217,7 +217,7 @@
#define BROADCAST(msg)
#define CALL(route)
#define CLEAR_STASH(id)
#define CLEAR_ALL_STASH(id)
#define CLEAR_ALL_STASH
#define CLOSE(id)
#define CONFIGURE_SERVO(vpin,pos1,pos2,profile)
#define DCC_SIGNAL(id,add,subaddr)

Binary file not shown.

View File

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

View File

@@ -668,7 +668,8 @@ void TrackManager::setJoin(bool joined) {
if (track[t]->getMode() & TRACK_MODE_PROG) { // find PROG track
tempProgTrack = t; // remember PROG track
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
}
}
@@ -680,8 +681,6 @@ void TrackManager::setJoin(bool joined) {
setTrackMode(tempProgTrack, TRACK_MODE_PROG); // set track mode back to prog
track[tempProgTrack]->setPower(tPTmode); // set power status as it was before
tempProgTrack = MAX_TRACKS+1;
} else {
DIAG(F("Unjoin but no remembered prog track"));
}
}
#endif

View File

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

View File

@@ -3,7 +3,16 @@
#include "StringFormatter.h"
#define VERSION "5.4.3"
#define VERSION "5.4.10"
// 5.4.10 - Bugfix: MEGA DCC waveform starvation (sends too many idles)
// 5.4.9 - Handle non-compliant decoders returning 255 for cv 20 and confusing <R> with bad consist addresses.
// - <W CONSIST longaddr> handles non-compliant decoders which NAK cv 20 writes.
// 5.4.8 - Bugfix: Insert idle packet at end of speed reminder loop; treat all function groups equal
// 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