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/DCC.cpp b/DCC.cpp index 96868ca..f192ef5 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, @@ -378,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. @@ -420,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; @@ -552,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; @@ -567,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. @@ -668,7 +742,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 +790,19 @@ void DCC::ackManagerLoop(bool blocking) { ackManagerCv=pgm_read_byte_near(ackManagerProg); break; + case SETBYTE: + ackManagerProg++; + 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; @@ -724,6 +819,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..d8a1b6b 100644 --- a/DCC.h +++ b/DCC.h @@ -37,12 +37,16 @@ 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 + 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 @@ -88,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 @@ -124,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 3ecdb61..04546a3 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) { @@ -351,9 +359,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 +791,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..c9197de 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); @@ -59,6 +60,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; 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