mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-23 21:01:25 +01:00
Merge pull request #121 from DCC-EX/ConsistR
Startup commands and < R > and < W > commands
This commit is contained in:
commit
3818a16808
@ -58,6 +58,12 @@ void setup()
|
|||||||
RMFT::begin();
|
RMFT::begin();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if __has_include ( "mySetup.h")
|
||||||
|
#define SETUP(cmd) serialParser.parse(F(cmd))
|
||||||
|
#include "mySetup.h"
|
||||||
|
#undef SETUP
|
||||||
|
#endif
|
||||||
|
|
||||||
LCD(1,F("Ready"));
|
LCD(1,F("Ready"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
101
DCC.cpp
101
DCC.cpp
@ -331,10 +331,31 @@ const ackOp PROGMEM READ_CV_PROG[] = {
|
|||||||
|
|
||||||
const ackOp PROGMEM LOCO_ID_PROG[] = {
|
const ackOp PROGMEM LOCO_ID_PROG[] = {
|
||||||
BASELINE,
|
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,
|
SETCV,(ackOp)29,
|
||||||
SETBIT,(ackOp)5,
|
SETBIT,(ackOp)5,
|
||||||
V0, WACK, ITSKIP, // Skip to SKIPTARGET if bit 5 of CV29 is zero
|
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
|
// Long locoid
|
||||||
SETCV, (ackOp)17, // CV 17 is part of locoid
|
SETCV, (ackOp)17, // CV 17 is part of locoid
|
||||||
STARTMERGE,
|
STARTMERGE,
|
||||||
@ -366,7 +387,7 @@ const ackOp PROGMEM LOCO_ID_PROG[] = {
|
|||||||
SKIPTARGET,
|
SKIPTARGET,
|
||||||
SETCV, (ackOp)1,
|
SETCV, (ackOp)1,
|
||||||
STARTMERGE,
|
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,
|
V0, WACK, MERGE,
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
@ -378,6 +399,44 @@ const ackOp PROGMEM LOCO_ID_PROG[] = {
|
|||||||
FAIL
|
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.
|
// 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.
|
// 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);
|
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
|
void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco
|
||||||
int reg=lookupSpeedTable(cab);
|
int reg=lookupSpeedTable(cab);
|
||||||
if (reg>=0) speedTable[reg].loco=0;
|
if (reg>=0) speedTable[reg].loco=0;
|
||||||
@ -552,6 +618,7 @@ int DCC::nextLoco = 0;
|
|||||||
ackOp const * DCC::ackManagerProg;
|
ackOp const * DCC::ackManagerProg;
|
||||||
byte DCC::ackManagerByte;
|
byte DCC::ackManagerByte;
|
||||||
byte DCC::ackManagerStash;
|
byte DCC::ackManagerStash;
|
||||||
|
int DCC::ackManagerWord;
|
||||||
int DCC::ackManagerCv;
|
int DCC::ackManagerCv;
|
||||||
byte DCC::ackManagerBitNum;
|
byte DCC::ackManagerBitNum;
|
||||||
bool DCC::ackReceived;
|
bool DCC::ackReceived;
|
||||||
@ -567,6 +634,13 @@ void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[]
|
|||||||
if (blocking) ackManagerLoop(blocking);
|
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
|
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.
|
// checkRessets return true if the caller should yield back to loop and try later.
|
||||||
@ -673,6 +747,14 @@ void DCC::ackManagerLoop(bool blocking) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ITCB7: // If True callback(byte & 0xF)
|
||||||
|
if (ackReceived) {
|
||||||
|
ackManagerProg = NULL; // all done now
|
||||||
|
callback(ackManagerByte & 0x7F);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case NAKFAIL: // If nack callback(-1)
|
case NAKFAIL: // If nack callback(-1)
|
||||||
if (!ackReceived) {
|
if (!ackReceived) {
|
||||||
ackManagerProg = NULL; // all done now
|
ackManagerProg = NULL; // all done now
|
||||||
@ -708,6 +790,19 @@ void DCC::ackManagerLoop(bool blocking) {
|
|||||||
ackManagerCv=pgm_read_byte_near(ackManagerProg);
|
ackManagerCv=pgm_read_byte_near(ackManagerProg);
|
||||||
break;
|
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:
|
case STASHLOCOID:
|
||||||
ackManagerStash=ackManagerByte; // stash value from CV17
|
ackManagerStash=ackManagerByte; // stash value from CV17
|
||||||
break;
|
break;
|
||||||
@ -724,6 +819,8 @@ void DCC::ackManagerLoop(bool blocking) {
|
|||||||
while (opcode!=SKIPTARGET) {
|
while (opcode!=SKIPTARGET) {
|
||||||
ackManagerProg++;
|
ackManagerProg++;
|
||||||
opcode=pgm_read_byte_near(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;
|
break;
|
||||||
case SKIPTARGET:
|
case SKIPTARGET:
|
||||||
|
7
DCC.h
7
DCC.h
@ -37,12 +37,16 @@ enum ackOp
|
|||||||
ITC1, // If True Callback(1) (if prevous WACK got an ACK)
|
ITC1, // If True Callback(1) (if prevous WACK got an ACK)
|
||||||
ITC0, // If True callback(0);
|
ITC0, // If True callback(0);
|
||||||
ITCB, // If True callback(byte)
|
ITCB, // If True callback(byte)
|
||||||
|
ITCB7, // If True callback(byte &0x7F)
|
||||||
NAKFAIL, // if false callback(-1)
|
NAKFAIL, // if false callback(-1)
|
||||||
FAIL, // callback(-1)
|
FAIL, // callback(-1)
|
||||||
STARTMERGE, // Clear bit and byte settings ready for merge pass
|
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)
|
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
|
SETBIT, // sets bit number to next prog byte
|
||||||
SETCV, // sets cv 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
|
STASHLOCOID, // keeps current byte value for later
|
||||||
COMBINELOCOID, // combines current value with stashed value and returns it
|
COMBINELOCOID, // combines current value with stashed value and returns it
|
||||||
ITSKIP, // skip to SKIPTARGET if ack true
|
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 verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking = false);
|
||||||
|
|
||||||
static void getLocoId(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
|
// Enhanced API functions
|
||||||
static void forgetLoco(int cab); // removes any speed reminders for this loco
|
static void forgetLoco(int cab); // removes any speed reminders for this loco
|
||||||
@ -124,10 +129,12 @@ private:
|
|||||||
static byte ackManagerByte;
|
static byte ackManagerByte;
|
||||||
static byte ackManagerBitNum;
|
static byte ackManagerBitNum;
|
||||||
static int ackManagerCv;
|
static int ackManagerCv;
|
||||||
|
static int ackManagerWord;
|
||||||
static byte ackManagerStash;
|
static byte ackManagerStash;
|
||||||
static bool ackReceived;
|
static bool ackReceived;
|
||||||
static ACK_CALLBACK ackManagerCallback;
|
static ACK_CALLBACK ackManagerCallback;
|
||||||
static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback, bool blocking);
|
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 void ackManagerLoop(bool blocking);
|
||||||
static bool checkResets(bool blocking, uint8_t numResets);
|
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)
|
static const int PROG_REPEATS = 8; // repeats of programming commands (some decoders need at least 8 to be reliable)
|
||||||
|
@ -236,6 +236,14 @@ void DCCEXParser::setAtCommandCallback(AT_COMMAND_CALLBACK callback)
|
|||||||
atCommandCallback = 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
|
// See documentation on DCC class for info on this section
|
||||||
void DCCEXParser::parse(Print *stream, byte *com, bool blocking)
|
void DCCEXParser::parse(Print *stream, byte *com, bool blocking)
|
||||||
{
|
{
|
||||||
@ -353,6 +361,9 @@ void DCCEXParser::parse(Print *stream, byte *com, bool blocking)
|
|||||||
case 'W': // WRITE CV ON PROG <W CV VALUE CALLBACKNUM CALLBACKSUB>
|
case 'W': // WRITE CV ON PROG <W CV VALUE CALLBACKNUM CALLBACKSUB>
|
||||||
if (!stashCallback(stream, p))
|
if (!stashCallback(stream, p))
|
||||||
break;
|
break;
|
||||||
|
if (params == 1) // <W id> Write new loco id (clearing consist and managing short/long)
|
||||||
|
DCC::setLocoId(p[0],callback_Wloco, blocking);
|
||||||
|
else // WRITE CV ON PROG <W CV VALUE [CALLBACKNUM] [CALLBACKSUB]>
|
||||||
DCC::writeCVByte(p[0], p[1], callback_W, blocking);
|
DCC::writeCVByte(p[0], p[1], callback_W, blocking);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -780,6 +791,13 @@ void DCCEXParser::callback_R(int result)
|
|||||||
|
|
||||||
void DCCEXParser::callback_Rloco(int result)
|
void DCCEXParser::callback_Rloco(int result)
|
||||||
{
|
{
|
||||||
StringFormatter::send(stashStream, F("<r %d>"), result);
|
StringFormatter::send(stashStream, F("<r %d>"), 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("<w %d>"), result);
|
||||||
stashBusy = false;
|
stashBusy = false;
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ struct DCCEXParser
|
|||||||
DCCEXParser();
|
DCCEXParser();
|
||||||
void loop(Stream & stream);
|
void loop(Stream & stream);
|
||||||
void parse(Print * stream, byte * command, bool blocking);
|
void parse(Print * stream, byte * command, bool blocking);
|
||||||
|
void parse(const __FlashStringHelper * cmd);
|
||||||
void flush();
|
void flush();
|
||||||
static void setFilter(FILTER_CALLBACK filter);
|
static void setFilter(FILTER_CALLBACK filter);
|
||||||
static void setRMFTFilter(FILTER_CALLBACK filter);
|
static void setRMFTFilter(FILTER_CALLBACK filter);
|
||||||
@ -59,6 +60,7 @@ struct DCCEXParser
|
|||||||
static void callback_B(int result);
|
static void callback_B(int result);
|
||||||
static void callback_R(int result);
|
static void callback_R(int result);
|
||||||
static void callback_Rloco(int result);
|
static void callback_Rloco(int result);
|
||||||
|
static void callback_Wloco(int result);
|
||||||
static void callback_Vbit(int result);
|
static void callback_Vbit(int result);
|
||||||
static void callback_Vbyte(int result);
|
static void callback_Vbyte(int result);
|
||||||
static FILTER_CALLBACK filterCallback;
|
static FILTER_CALLBACK filterCallback;
|
||||||
|
@ -4,7 +4,10 @@
|
|||||||
#include "StringFormatter.h"
|
#include "StringFormatter.h"
|
||||||
|
|
||||||
// const char VERSION[] PROGMEM ="0.2.0";
|
// const char VERSION[] PROGMEM ="0.2.0";
|
||||||
#define VERSION "3.0.2"
|
#define VERSION "3.0.3"
|
||||||
|
// 3.0.3 Includes:
|
||||||
|
// <W addr> command to write loco address and clear consist
|
||||||
|
// <R> command will allow for consist address
|
||||||
|
// Startup commands implemented
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user