From 895b2aaaaaee935b92fef5d41c0487dfa6db9fd2 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 7 Jan 2021 20:58:23 +0000 Subject: [PATCH 1/9] Implement mySetup.h facility --- CommandStation-EX.ino | 10 ++++++++-- DCCEXParser.cpp | 8 ++++++++ DCCEXParser.h | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index 18f2aaa..c0c3a1d 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -53,11 +53,17 @@ void setup() // waveform generation. e.g. DCC::begin(STANDARD_MOTOR_SHIELD,2); to use timer 2 DCC::begin(MOTOR_SHIELD_TYPE); - + #if defined(RMFT_ACTIVE) RMFT::begin(); #endif - + + #if __has_include ( "mySetup.h") + #define SETUP(cmd) serialParser.parse(F(cmd)) + #include "mySetup.h" + #undef SETUP + #endif + LCD(1,F("Ready")); } diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index c488fc1..59d0b74 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -236,6 +236,14 @@ void DCCEXParser::setAtCommandCallback(AT_COMMAND_CALLBACK callback) atCommandCallback = callback; } +// Parse an F() string +void DCCEXParser::parse(const __FlashStringHelper * cmd) { + int size=strlen_P((char *)cmd)+1; + char buffer[size]; + strcpy_P(buffer,(char *)cmd); + parse(&Serial,(byte *)buffer,true); +} + // See documentation on DCC class for info on this section void DCCEXParser::parse(Print *stream, byte *com, bool blocking) { diff --git a/DCCEXParser.h b/DCCEXParser.h index bef1199..d2423d0 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -28,6 +28,7 @@ struct DCCEXParser DCCEXParser(); void loop(Stream & stream); void parse(Print * stream, byte * command, bool blocking); + void parse(const __FlashStringHelper * cmd); void flush(); static void setFilter(FILTER_CALLBACK filter); static void setRMFTFilter(FILTER_CALLBACK filter); From b537d7a31849f091b18c70c0b6c226b2c5271306 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sun, 17 Jan 2021 13:22:16 +0000 Subject: [PATCH 2/9] command consist support R command will return address suitable for throttle if consist has been setup. --- DCC.cpp | 42 +++++++++++++++++++++++++++++++++++++++--- DCC.h | 2 ++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 96868ca..421dd9b 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -331,10 +331,31 @@ const ackOp PROGMEM READ_CV_PROG[] = { const ackOp PROGMEM LOCO_ID_PROG[] = { BASELINE, + SETCV, (ackOp)1, + SETBIT, (ackOp)7, + V0,WACK,NAKFAIL, // test CV 1 bit 7 is a zero... NAK means no loco found + + SETCV, (ackOp)19, // CV 19 is consist setting + SETBYTE, (ackOp)0, + VB, WACK, ITSKIP, // ignore consist if cv19 is zero (no consist) + SETBYTE, (ackOp)128, + VB, WACK, ITSKIP, // ignore consist if cv19 is 128 (no consist, direction bit set) + STARTMERGE, // Setup to read cv 19 + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + V0, WACK, MERGE, + VB, WACK, ITCB7, // return 7 bits only, No_ACK means CV19 not supported so ignore it + + SKIPTARGET, // continue here if CV 19 is zero or fails all validation SETCV,(ackOp)29, SETBIT,(ackOp)5, V0, WACK, ITSKIP, // Skip to SKIPTARGET if bit 5 of CV29 is zero - V1, WACK, NAKFAIL, // fast fail if no loco on track + // Long locoid SETCV, (ackOp)17, // CV 17 is part of locoid STARTMERGE, @@ -366,7 +387,7 @@ const ackOp PROGMEM LOCO_ID_PROG[] = { SKIPTARGET, SETCV, (ackOp)1, STARTMERGE, - V0, WACK, MERGE, // read and merge bit 1 etc + SETBIT, (ackOp)6, // skip over first bit as we know its a zero V0, WACK, MERGE, V0, WACK, MERGE, V0, WACK, MERGE, @@ -668,7 +689,15 @@ void DCC::ackManagerLoop(bool blocking) { case ITCB: // If True callback(byte) if (ackReceived) { ackManagerProg = NULL; // all done now - callback(ackManagerByte); + callback(ackManagerByte); + return; + } + break; + + case ITCB7: // If True callback(byte & 0xF) + if (ackReceived) { + ackManagerProg = NULL; // all done now + callback(ackManagerByte & 0x7F); return; } break; @@ -708,6 +737,11 @@ void DCC::ackManagerLoop(bool blocking) { ackManagerCv=pgm_read_byte_near(ackManagerProg); break; + case SETBYTE: + ackManagerProg++; + ackManagerByte=pgm_read_byte_near(ackManagerProg); + break; + case STASHLOCOID: ackManagerStash=ackManagerByte; // stash value from CV17 break; @@ -724,6 +758,8 @@ void DCC::ackManagerLoop(bool blocking) { while (opcode!=SKIPTARGET) { ackManagerProg++; opcode=pgm_read_byte_near(ackManagerProg); + // Jump over second byte of any 2-byte opcodes. + if (opcode==SETBIT || opcode==SETBYTE || opcode==SETCV) ackManagerProg++; } break; case SKIPTARGET: diff --git a/DCC.h b/DCC.h index 122e934..d7430c6 100644 --- a/DCC.h +++ b/DCC.h @@ -37,12 +37,14 @@ enum ackOp ITC1, // If True Callback(1) (if prevous WACK got an ACK) ITC0, // If True callback(0); ITCB, // If True callback(byte) + ITCB7, // If True callback(byte &0x7F) NAKFAIL, // if false callback(-1) FAIL, // callback(-1) STARTMERGE, // Clear bit and byte settings ready for merge pass MERGE, // Merge previous wack response with byte value and decrement bit number (use for readimng CV bytes) SETBIT, // sets bit number to next prog byte SETCV, // sets cv number to next prog byte + SETBYTE, // sets current byte to next prog byte STASHLOCOID, // keeps current byte value for later COMBINELOCOID, // combines current value with stashed value and returns it ITSKIP, // skip to SKIPTARGET if ack true From 7d90e4241a3e27f6348a9f7a0fa9eaa3ed711d6b Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 18 Jan 2021 10:06:46 +0000 Subject: [PATCH 3/9] Add command Automatically clears consist and manages short/long addresses --- DCC.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++++++- DCC.h | 5 ++++ DCCEXParser.cpp | 18 ++++++++++---- DCCEXParser.h | 1 + 4 files changed, 82 insertions(+), 5 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 421dd9b..f192ef5 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -399,6 +399,44 @@ const ackOp PROGMEM LOCO_ID_PROG[] = { FAIL }; +const ackOp PROGMEM SHORT_LOCO_ID_PROG[] = { + BASELINE, + SETCV,(ackOp)19, + SETBYTE, (ackOp)0, + WB,WACK, // ignore router without cv19 support + // Turn off long address flag + SETCV,(ackOp)29, + SETBIT,(ackOp)5, + W0,WACK,NAKFAIL, + SETCV, (ackOp)1, + SETBYTEL, // low byte of word + WB,WACK,NAKFAIL, + VB,WACK,ITCB, + FAIL +}; + +const ackOp PROGMEM LONG_LOCO_ID_PROG[] = { + BASELINE, + // Clear consist CV 19 + SETCV,(ackOp)19, + SETBYTE, (ackOp)0, + WB,WACK, // ignore router without cv19 support + // Turn on long address flag cv29 bit 5 + SETCV,(ackOp)29, + SETBIT,(ackOp)5, + W1,WACK,NAKFAIL, + // Store high byte of address in cv 17 + SETCV, (ackOp)17, + SETBYTEH, // high byte of word + WB,WACK,NAKFAIL, + VB,WACK,NAKFAIL, + // store + SETCV, (ackOp)18, + SETBYTEL, // low byte of word + WB,WACK,NAKFAIL, + VB,WACK,ITC1, // callback(1) means Ok + FAIL +}; // On the following prog-track functions blocking defaults to false. // blocking=true forces the API to block, waiting for the response and invoke the callback BEFORE returning. @@ -441,6 +479,13 @@ void DCC::getLocoId(ACK_CALLBACK callback, bool blocking) { ackManagerSetup(0,0, LOCO_ID_PROG, callback, blocking); } +void DCC::setLocoId(int id,ACK_CALLBACK callback, bool blocking) { + if (id<=0 || id>9999) callback(-1); + int wordval; + if (id<=127) ackManagerSetup(id,SHORT_LOCO_ID_PROG, callback, blocking); + else ackManagerSetup(id | 0xc000,LONG_LOCO_ID_PROG, callback, blocking); +} + void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco int reg=lookupSpeedTable(cab); if (reg>=0) speedTable[reg].loco=0; @@ -573,7 +618,8 @@ int DCC::nextLoco = 0; ackOp const * DCC::ackManagerProg; byte DCC::ackManagerByte; byte DCC::ackManagerStash; -int DCC::ackManagerCv; +int DCC::ackManagerWord; +int DCC::ackManagerCv; byte DCC::ackManagerBitNum; bool DCC::ackReceived; @@ -588,6 +634,13 @@ void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[] if (blocking) ackManagerLoop(blocking); } +void DCC::ackManagerSetup(int wordval, ackOp const program[], ACK_CALLBACK callback, bool blocking) { + ackManagerWord=wordval; + ackManagerProg = program; + ackManagerCallback = callback; + if (blocking) ackManagerLoop(blocking); +} + const byte RESET_MIN=8; // tuning of reset counter before sending message // checkRessets return true if the caller should yield back to loop and try later. @@ -742,6 +795,14 @@ void DCC::ackManagerLoop(bool blocking) { ackManagerByte=pgm_read_byte_near(ackManagerProg); break; + case SETBYTEH: + ackManagerByte=highByte(ackManagerWord); + break; + + case SETBYTEL: + ackManagerByte=lowByte(ackManagerWord); + break; + case STASHLOCOID: ackManagerStash=ackManagerByte; // stash value from CV17 break; diff --git a/DCC.h b/DCC.h index d7430c6..d8a1b6b 100644 --- a/DCC.h +++ b/DCC.h @@ -45,6 +45,8 @@ enum ackOp SETBIT, // sets bit number to next prog byte SETCV, // sets cv number to next prog byte SETBYTE, // sets current byte to next prog byte + SETBYTEH, // sets current byte to word high byte + SETBYTEL, // sets current byte to word low byte STASHLOCOID, // keeps current byte value for later COMBINELOCOID, // combines current value with stashed value and returns it ITSKIP, // skip to SKIPTARGET if ack true @@ -90,6 +92,7 @@ public: static void verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking = false); static void getLocoId(ACK_CALLBACK callback, bool blocking = false); + static void setLocoId(int id,ACK_CALLBACK callback, bool blocking = false); // Enhanced API functions static void forgetLoco(int cab); // removes any speed reminders for this loco @@ -126,10 +129,12 @@ private: static byte ackManagerByte; static byte ackManagerBitNum; static int ackManagerCv; + static int ackManagerWord; static byte ackManagerStash; static bool ackReceived; static ACK_CALLBACK ackManagerCallback; static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback, bool blocking); + static void ackManagerSetup(int wordval, ackOp const program[], ACK_CALLBACK callback, bool blocking); static void ackManagerLoop(bool blocking); static bool checkResets(bool blocking, uint8_t numResets); static const int PROG_REPEATS = 8; // repeats of programming commands (some decoders need at least 8 to be reliable) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index fd1cea0..efbba32 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -351,9 +351,12 @@ void DCCEXParser::parse(Print *stream, byte *com, bool blocking) return; case 'W': // WRITE CV ON PROG - if (!stashCallback(stream, p)) - break; - DCC::writeCVByte(p[0], p[1], callback_W, blocking); + if (!stashCallback(stream, p)) + break; + if (params == 1) // Write new loco id (clearing consist and managing short/long) + DCC::setLocoId(p[0],callback_Wloco, blocking); + else // WRITE CV ON PROG + DCC::writeCVByte(p[0], p[1], callback_W, blocking); return; case 'V': // VERIFY CV ON PROG @@ -780,6 +783,13 @@ void DCCEXParser::callback_R(int result) void DCCEXParser::callback_Rloco(int result) { - StringFormatter::send(stashStream, F(""), result); + StringFormatter::send(stashStream, F(""), result & 0x3FFF); + stashBusy = false; +} + +void DCCEXParser::callback_Wloco(int result) +{ + if (result==1) result=stashP[0]; // pick up original requested id from command + StringFormatter::send(stashStream, F(""), result); stashBusy = false; } diff --git a/DCCEXParser.h b/DCCEXParser.h index bef1199..8304e95 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -59,6 +59,7 @@ struct DCCEXParser static void callback_B(int result); static void callback_R(int result); static void callback_Rloco(int result); + static void callback_Wloco(int result); static void callback_Vbit(int result); static void callback_Vbyte(int result); static FILTER_CALLBACK filterCallback; From 2ce4c8066e49c6bbbc5073db480c860644d00736 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 21 Jan 2021 11:10:52 +0000 Subject: [PATCH 4/9] Update version.h --- version.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/version.h b/version.h index 43f4764..e47b1ec 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,10 @@ #include "StringFormatter.h" // const char VERSION[] PROGMEM ="0.2.0"; -#define VERSION "3.0.2" - +#define VERSION "3.0.3" +// 3.0.3 Includes: +// command to write loco address and clear consist +// command will allow for consist address +// Startup commands implemented #endif From 7c7305ba1d3846cd32bdb6cb7e66db09cf5a3767 Mon Sep 17 00:00:00 2001 From: Fred Date: Thu, 21 Jan 2021 10:08:35 -0500 Subject: [PATCH 5/9] Update Prod-Release-Notes.md --- Release - Architecture Doc/Prod-Release-Notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Release - Architecture Doc/Prod-Release-Notes.md b/Release - Architecture Doc/Prod-Release-Notes.md index eeaa466..e0c89c4 100644 --- a/Release - Architecture Doc/Prod-Release-Notes.md +++ b/Release - Architecture Doc/Prod-Release-Notes.md @@ -4,6 +4,10 @@ The DCC-EX Team is pleased to release CommandStation-EX-v3.0.0 as a Production R - **Consisting through JMRI** - currently does not work in this release. A number of testers were able to develop a work around. If interested enter a Support Ticket. - **Wi-Fi** - works, but can be challenging to use if you want to switch between AP mode and STA station mode. - **Pololu Motor Shield** - is supported with this release, but the user may have to play around with some timings to enable programming mode due to limitation in its current sensing circuitry +**Summary of the key new features added to CommandStation-EX V3.0.3** + - ** command to write loco address and clear consist** + - ** command will allow for consist address** + - **Startup commands implemented** **Summary of the key new features added to CommandStation-EX V3.0.2:** - **Create new output for current in mA for ```` command** - New current response outputs current in mA, overlimit current, and maximum board capable current From a91dc981841e48bd6b5ffb846108d41b265e4ec4 Mon Sep 17 00:00:00 2001 From: Fred Date: Thu, 21 Jan 2021 10:13:38 -0500 Subject: [PATCH 6/9] Update Prod-Release-Notes.md --- Release - Architecture Doc/Prod-Release-Notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Release - Architecture Doc/Prod-Release-Notes.md b/Release - Architecture Doc/Prod-Release-Notes.md index e0c89c4..35e8f4b 100644 --- a/Release - Architecture Doc/Prod-Release-Notes.md +++ b/Release - Architecture Doc/Prod-Release-Notes.md @@ -4,6 +4,7 @@ The DCC-EX Team is pleased to release CommandStation-EX-v3.0.0 as a Production R - **Consisting through JMRI** - currently does not work in this release. A number of testers were able to develop a work around. If interested enter a Support Ticket. - **Wi-Fi** - works, but can be challenging to use if you want to switch between AP mode and STA station mode. - **Pololu Motor Shield** - is supported with this release, but the user may have to play around with some timings to enable programming mode due to limitation in its current sensing circuitry + **Summary of the key new features added to CommandStation-EX V3.0.3** - ** command to write loco address and clear consist** - ** command will allow for consist address** From f646f12c655801e208abbae70c7dd9c3454d87f2 Mon Sep 17 00:00:00 2001 From: Fred Date: Thu, 21 Jan 2021 10:19:34 -0500 Subject: [PATCH 7/9] Update Prod-Release-Notes.md --- Release - Architecture Doc/Prod-Release-Notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Release - Architecture Doc/Prod-Release-Notes.md b/Release - Architecture Doc/Prod-Release-Notes.md index 35e8f4b..0893e1a 100644 --- a/Release - Architecture Doc/Prod-Release-Notes.md +++ b/Release - Architecture Doc/Prod-Release-Notes.md @@ -1,7 +1,7 @@ The DCC-EX Team is pleased to release CommandStation-EX-v3.0.0 as a Production Release. This release is a major re-write of earlier versions. We've re-architected the code-base so that it can better handle new features going forward. **Known Bugs:** - - **Consisting through JMRI** - currently does not work in this release. A number of testers were able to develop a work around. If interested enter a Support Ticket. + - **Consisting through JMRI** - currently does not work in this release. You may use the command to do this manually. - **Wi-Fi** - works, but can be challenging to use if you want to switch between AP mode and STA station mode. - **Pololu Motor Shield** - is supported with this release, but the user may have to play around with some timings to enable programming mode due to limitation in its current sensing circuitry From 0b3e904ffb01bb8cf03950510682613621935dbf Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 30 Jan 2021 22:53:05 +0100 Subject: [PATCH 8/9] correct logic in setLocoId --- DCC.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index f192ef5..b658fce 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -480,10 +480,14 @@ void DCC::getLocoId(ACK_CALLBACK callback, bool blocking) { } void DCC::setLocoId(int id,ACK_CALLBACK callback, bool blocking) { - if (id<=0 || id>9999) callback(-1); - int wordval; - if (id<=127) ackManagerSetup(id,SHORT_LOCO_ID_PROG, callback, blocking); - else ackManagerSetup(id | 0xc000,LONG_LOCO_ID_PROG, callback, blocking); + if (id<1 || id>10239) { //0x27FF according to standard + callback(-1); + return; + } + if (id<=127) + ackManagerSetup(id, SHORT_LOCO_ID_PROG, callback, blocking); + else + ackManagerSetup(id | 0xc000,LONG_LOCO_ID_PROG, callback, blocking); } void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco From 6cc5550927fa74195cf5cd177c25df9a08c0d75b Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 30 Jan 2021 22:54:38 +0100 Subject: [PATCH 9/9] result should be as is (can be -1 to indicate fail) --- DCCEXParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 04546a3..010cf8b 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -791,7 +791,7 @@ void DCCEXParser::callback_R(int result) void DCCEXParser::callback_Rloco(int result) { - StringFormatter::send(stashStream, F(""), result & 0x3FFF); + StringFormatter::send(stashStream, F(""), result); stashBusy = false; }