1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-12-23 12:51:24 +01:00

Merge pull request #121 from DCC-EX/ConsistR

Startup commands and < R > and < W > commands
This commit is contained in:
Asbelos 2021-01-21 11:16:15 +00:00 committed by GitHub
commit 3818a16808
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 145 additions and 12 deletions

View File

@ -58,6 +58,12 @@ void setup()
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"));
}

101
DCC.cpp
View File

@ -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,6 +618,7 @@ int DCC::nextLoco = 0;
ackOp const * DCC::ackManagerProg;
byte DCC::ackManagerByte;
byte DCC::ackManagerStash;
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.
@ -673,6 +747,14 @@ void DCC::ackManagerLoop(bool blocking) {
}
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)
if (!ackReceived) {
ackManagerProg = NULL; // all done now
@ -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:

7
DCC.h
View File

@ -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)

View File

@ -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)
{
@ -353,6 +361,9 @@ void DCCEXParser::parse(Print *stream, byte *com, bool blocking)
case 'W': // WRITE CV ON PROG <W CV VALUE CALLBACKNUM CALLBACKSUB>
if (!stashCallback(stream, p))
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);
return;
@ -780,6 +791,13 @@ void DCCEXParser::callback_R(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;
}

View File

@ -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;

View File

@ -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:
// <W addr> command to write loco address and clear consist
// <R> command will allow for consist address
// Startup commands implemented
#endif