mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-23 08:06:13 +01:00
withrottle changes
disallow acquire if address in use return error on address 0 and mismatched L/S move init string to 'HU' reply some message format fixes comment-out some DIAGs to show only in and out message traffic
This commit is contained in:
parent
9cc8843e15
commit
ecbedd26bc
9
DCC.cpp
9
DCC.cpp
|
@ -95,6 +95,15 @@ bool DCC::getThrottleDirection(int cab) {
|
||||||
return (speedTable[reg].speedCode & 0x80) !=0;
|
return (speedTable[reg].speedCode & 0x80) !=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DCC::isThrottleInUse(int locoId) {
|
||||||
|
// return true if this loco address is already in table, false otherwise
|
||||||
|
int reg;
|
||||||
|
for (reg = 0; reg < MAX_LOCOS; reg++) {
|
||||||
|
if (speedTable[reg].loco == locoId) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Set function to value on or off
|
// Set function to value on or off
|
||||||
void DCC::setFn( int cab, byte functionNumber, bool on) {
|
void DCC::setFn( int cab, byte functionNumber, bool on) {
|
||||||
if (cab<=0 || functionNumber>28) return;
|
if (cab<=0 || functionNumber>28) return;
|
||||||
|
|
1
DCC.h
1
DCC.h
|
@ -56,6 +56,7 @@ class DCC {
|
||||||
static void setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection);
|
static void setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection);
|
||||||
static uint8_t getThrottleSpeed(int cab);
|
static uint8_t getThrottleSpeed(int cab);
|
||||||
static bool getThrottleDirection(int cab);
|
static bool getThrottleDirection(int cab);
|
||||||
|
static bool isThrottleInUse(int cab);
|
||||||
static void writeCVByteMain(int cab, int cv, byte bValue);
|
static void writeCVByteMain(int cab, int cv, byte bValue);
|
||||||
static void writeCVBitMain(int cab, int cv, byte bNum, bool bValue);
|
static void writeCVBitMain(int cab, int cv, byte bNum, bool bValue);
|
||||||
static void setFunction( int cab, byte fByte, byte eByte);
|
static void setFunction( int cab, byte fByte, byte eByte);
|
||||||
|
|
|
@ -147,16 +147,17 @@ void DCCEXParser::parse(Print * stream, byte *com, bool blocking) {
|
||||||
case '\0': return; // filterCallback asked us to ignore
|
case '\0': return; // filterCallback asked us to ignore
|
||||||
case 't': // THROTTLE <t REGISTER CAB SPEED DIRECTION>
|
case 't': // THROTTLE <t REGISTER CAB SPEED DIRECTION>
|
||||||
{
|
{
|
||||||
|
if (p[1] == 0) break; // ignore requests for throttle address 0 (returns 'X')
|
||||||
// Convert JMRI bizarre -1=emergency stop, 0-126 as speeds
|
// Convert JMRI bizarre -1=emergency stop, 0-126 as speeds
|
||||||
// to DCC 0=stop, 1= emergency stop, 2-127 speeds
|
// to DCC 0=stop, 1= emergency stop, 2-127 speeds
|
||||||
int tspeed=p[2];
|
int tspeed=p[2];
|
||||||
if (tspeed>126 || tspeed<-1) break; // invalid JMRI speed code
|
if (tspeed>126 || tspeed<-1) break; // invalid JMRI speed code
|
||||||
if (tspeed<0) tspeed=1; // emergency stop DCC speed
|
if (tspeed<0) tspeed=1; // emergency stop DCC speed
|
||||||
else if (tspeed>0) tspeed++; // map 1-126 -> 2-127
|
else if (tspeed>0) tspeed++; // map 1-126 -> 2-127
|
||||||
DCC::setThrottle(p[1],tspeed,p[3]);
|
DCC::setThrottle(p[1],tspeed,p[3]);
|
||||||
// report speed 0 after emergency stop
|
// report speed 0 after emergency stop
|
||||||
StringFormatter::send(stream,F("<T %d %d %d>"), p[0], p[2]<0?0:p[2],p[3]);
|
StringFormatter::send(stream,F("<T %d %d %d>"), p[0], p[2]<0?0:p[2],p[3]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case 'f': // FUNCTION <f CAB BYTE1 [BYTE2]>
|
case 'f': // FUNCTION <f CAB BYTE1 [BYTE2]>
|
||||||
if (parsef(stream,params,p)) return;
|
if (parsef(stream,params,p)) return;
|
||||||
|
|
|
@ -66,9 +66,10 @@ WiThrottle::WiThrottle( int wificlientid) {
|
||||||
nextThrottle=firstThrottle;
|
nextThrottle=firstThrottle;
|
||||||
firstThrottle= this;
|
firstThrottle= this;
|
||||||
clientid=wificlientid;
|
clientid=wificlientid;
|
||||||
heartBeatEnable=false; // until client turns it on
|
heartBeatEnable=false; // until client turns it on
|
||||||
callState=0;
|
initSent=false;
|
||||||
for (int loco=0;loco<MAX_MY_LOCO; loco++) myLocos[loco].throttle='\0';
|
turnoutListSent=false;
|
||||||
|
for (int loco=0;loco<MAX_MY_LOCO; loco++) myLocos[loco].throttle='\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
WiThrottle::~WiThrottle() {
|
WiThrottle::~WiThrottle() {
|
||||||
|
@ -97,28 +98,20 @@ void WiThrottle::parse(Print & stream, byte * cmdx) {
|
||||||
byte * cmd=local;
|
byte * cmd=local;
|
||||||
|
|
||||||
heartBeat=millis();
|
heartBeat=millis();
|
||||||
DIAG(F("\nWiThrottle(%d) [%e]"),clientid, cmd);
|
// DIAG(F("\nWiThrottle(%d)<-[%e]\n"),clientid, cmd);
|
||||||
switch (callState) {
|
|
||||||
case 0: // first call in
|
//send turnoutlist on next response after the init string
|
||||||
callState++;
|
if (initSent && !turnoutListSent) {
|
||||||
StringFormatter::send(stream,F("VN2.0\nHTDCC++EX\nRL0\nPPA%x\n"),DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON);
|
// Send turnout list if populated
|
||||||
if (annotateLeftRight) StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[Left}|{2]\\[Right}|{4\n"));
|
if (Turnout::firstTurnout) {
|
||||||
else StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[Closed}|{2]\\[Thrown}|{4\n"));
|
StringFormatter::send(stream,F("PTL"));
|
||||||
StringFormatter::send(stream,F("*%d\n"),HEARTBEAT_TIMEOUT);
|
for(Turnout *tt=Turnout::firstTurnout;tt!=NULL;tt=tt->nextTurnout){
|
||||||
break;
|
StringFormatter::send(stream,F("]\\[%d}|{T%d}|{%d"), tt->data.id, tt->data.id, (bool)(tt->data.tStatus & STATUS_ACTIVE));
|
||||||
case 1: // second call... send the turnout table if we have one
|
|
||||||
callState++;
|
|
||||||
if (Turnout::firstTurnout) {
|
|
||||||
StringFormatter::send(stream,F("PTL"));
|
|
||||||
for(Turnout *tt=Turnout::firstTurnout;tt!=NULL;tt=tt->nextTurnout){
|
|
||||||
StringFormatter::send(stream,F("]\\[%d}|{T%d}|{%d"), tt->data.id, tt->data.id, (bool)(tt->data.tStatus & STATUS_ACTIVE));
|
|
||||||
}
|
|
||||||
StringFormatter::send(stream,F("\n"));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default: // no more special headers required
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
StringFormatter::send(stream,F("\n"));
|
||||||
|
}
|
||||||
|
turnoutListSent = true;
|
||||||
|
}
|
||||||
|
|
||||||
while (cmd[0]) {
|
while (cmd[0]) {
|
||||||
switch (cmd[0]) {
|
switch (cmd[0]) {
|
||||||
|
@ -161,10 +154,17 @@ void WiThrottle::parse(Print & stream, byte * cmdx) {
|
||||||
case 'M': // multithrottle
|
case 'M': // multithrottle
|
||||||
multithrottle(stream, cmd);
|
multithrottle(stream, cmd);
|
||||||
break;
|
break;
|
||||||
case 'H': // hardware introduction....
|
case 'H': // send initial connection info after receiving "HU" message
|
||||||
|
if (cmd[1] == 'U') {
|
||||||
|
StringFormatter::send(stream,F("VN2.0\nHTDCC++EX\nRL0\nPPA%x\n"),DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON);
|
||||||
|
if (annotateLeftRight) StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[Left}|{2]\\[Right}|{4\n"));
|
||||||
|
else StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[Closed}|{2]\\[Thrown}|{4\n"));
|
||||||
|
StringFormatter::send(stream,F("*%d\n"),HEARTBEAT_TIMEOUT);
|
||||||
|
initSent = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'Q': //
|
case 'Q': //
|
||||||
DIAG(F("\nWiThrottle Quit"));
|
DIAG(F("WiThrottle Quit\n"));
|
||||||
delete this;
|
delete this;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -194,19 +194,36 @@ void WiThrottle::multithrottle(Print & stream, byte * cmd){
|
||||||
while(*aval !=';' && *aval !='\0') aval++;
|
while(*aval !=';' && *aval !='\0') aval++;
|
||||||
if (*aval) aval+=2; // skip ;>
|
if (*aval) aval+=2; // skip ;>
|
||||||
|
|
||||||
DIAG(F("\nMultithrottle aval=%c cab=%d"), aval[0],locoid);
|
// DIAG(F("\nMultithrottle aval=%c cab=%d"), aval[0],locoid);
|
||||||
switch(cmd[2]) {
|
switch(cmd[2]) {
|
||||||
case '+': // add loco
|
case '+': // add loco request
|
||||||
|
//return error if address zero requested
|
||||||
|
if (locoid==0) {
|
||||||
|
StringFormatter::send(stream, F("HMAddress '0' not supported!\n"), cmd[3] ,locoid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//return error if L or S from request doesn't match DCC++ assumptions
|
||||||
|
if (cmd[3] != LorS(locoid)) {
|
||||||
|
StringFormatter::send(stream, F("HMLength '%c' not valid for %d!\n"), cmd[3] ,locoid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//return error if address is already in use
|
||||||
|
if (DCC::isThrottleInUse(locoid)) {
|
||||||
|
StringFormatter::send(stream, F("HMAddress '%d' in use!\n"), locoid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (int loco=0;loco<MAX_MY_LOCO;loco++) {
|
for (int loco=0;loco<MAX_MY_LOCO;loco++) {
|
||||||
|
//use first empty "slot" on this client's list, and add to registration list
|
||||||
if (myLocos[loco].throttle=='\0') {
|
if (myLocos[loco].throttle=='\0') {
|
||||||
myLocos[loco].throttle=throttleChar;
|
myLocos[loco].throttle=throttleChar;
|
||||||
myLocos[loco].cab=locoid;
|
myLocos[loco].cab=locoid;
|
||||||
|
DCC::setThrottle(locoid,0,0); //register this loco address
|
||||||
StringFormatter::send(stream, F("M%c+%c%d<;>\n"), throttleChar, cmd[3] ,locoid);
|
StringFormatter::send(stream, F("M%c+%c%d<;>\n"), throttleChar, cmd[3] ,locoid);
|
||||||
// TODO... get known Fn states from DCC (need memoryStream improvements to handle data length)
|
// TODO... get known Fn states from DCC (need memoryStream improvements to handle data length)
|
||||||
// for(fKey=0; fKey<29; fKey++)StringFormatter::send(stream,F("M%cA%c<;>F0&s\n"),throttleChar,cmd[3],fkey);
|
// for(fKey=0; fKey<29; fKey++)StringFormatter::send(stream,F("M%cA%c<;>F0&s\n"),throttleChar,cmd[3],fkey);
|
||||||
StringFormatter::send(stream, F("M%c+%c%d<;>V0\n"), throttleChar, cmd[3], locoid);
|
StringFormatter::send(stream, F("M%cA%c%d<;>V0\n"), throttleChar, cmd[3], locoid); //default speed 0
|
||||||
StringFormatter::send(stream, F("M%c+%c%d<;>R1\n"), throttleChar, cmd[3], locoid);
|
StringFormatter::send(stream, F("M%cA%c%d<;>R1\n"), throttleChar, cmd[3], locoid); //default forward
|
||||||
StringFormatter::send(stream, F("M%c+%c%d<;>s1\n"), throttleChar, cmd[3], locoid);
|
StringFormatter::send(stream, F("M%cA%c%d<;>s1\n"), throttleChar, cmd[3], locoid); //default speed step 128
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,7 +232,8 @@ void WiThrottle::multithrottle(Print & stream, byte * cmd){
|
||||||
LOOPLOCOS(throttleChar, locoid) {
|
LOOPLOCOS(throttleChar, locoid) {
|
||||||
myLocos[loco].throttle='\0';
|
myLocos[loco].throttle='\0';
|
||||||
DCC::setThrottle(myLocos[loco].cab,0,0);
|
DCC::setThrottle(myLocos[loco].cab,0,0);
|
||||||
StringFormatter::send(stream, F("M%c-<;>\n"), throttleChar);
|
DCC::forgetLoco(myLocos[loco].cab); //unregister this loco address
|
||||||
|
StringFormatter::send(stream, F("M%c-%c%d<;>\n"), throttleChar, LorS(myLocos[loco].cab), myLocos[loco].cab);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -226,7 +244,7 @@ void WiThrottle::multithrottle(Print & stream, byte * cmd){
|
||||||
|
|
||||||
void WiThrottle::locoAction(Print & stream, byte* aval, char throttleChar, int cab){
|
void WiThrottle::locoAction(Print & stream, byte* aval, char throttleChar, int cab){
|
||||||
// Note cab=-1 for all cabs in the consist called throttleChar.
|
// Note cab=-1 for all cabs in the consist called throttleChar.
|
||||||
DIAG(F("\nLoco Action aval=%c%c throttleChar=%c, cab=%d"), aval[0],aval[1],throttleChar, cab);
|
// DIAG(F("\nLoco Action aval=%c%c throttleChar=%c, cab=%d"), aval[0],aval[1],throttleChar, cab);
|
||||||
switch (aval[0]) {
|
switch (aval[0]) {
|
||||||
case 'V': // Vspeed
|
case 'V': // Vspeed
|
||||||
{
|
{
|
||||||
|
@ -275,8 +293,8 @@ void WiThrottle::locoAction(Print & stream, byte* aval, char throttleChar, int c
|
||||||
DCC::setThrottle(myLocos[loco].cab,1, DCC::getThrottleDirection(myLocos[loco].cab));
|
DCC::setThrottle(myLocos[loco].cab,1, DCC::getThrottleDirection(myLocos[loco].cab));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'I': // Idle
|
case 'I': // Idle, set speed to 0
|
||||||
case 'Q': // Quit
|
case 'Q': // Quit, set speed to 0
|
||||||
LOOPLOCOS(throttleChar, cab) {
|
LOOPLOCOS(throttleChar, cab) {
|
||||||
DCC::setThrottle(myLocos[loco].cab,0, DCC::getThrottleDirection(myLocos[loco].cab));
|
DCC::setThrottle(myLocos[loco].cab,0, DCC::getThrottleDirection(myLocos[loco].cab));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#ifndef WiThrottle_h
|
#ifndef WiThrottle_h
|
||||||
#define WiTHrottle_h
|
#define WiThrottle_h
|
||||||
|
|
||||||
|
|
||||||
struct MYLOCO {
|
struct MYLOCO {
|
||||||
|
@ -35,8 +35,8 @@ class WiThrottle {
|
||||||
WiThrottle( int wifiClientId);
|
WiThrottle( int wifiClientId);
|
||||||
~WiThrottle();
|
~WiThrottle();
|
||||||
|
|
||||||
static const int MAX_MY_LOCO=10;
|
static const int MAX_MY_LOCO=10; //maximum number of locos assigned to a single client
|
||||||
static const int HEARTBEAT_TIMEOUT=10;// Timeout after 10 seconds, heartbeat at 5
|
static const int HEARTBEAT_TIMEOUT=4;// heartbeat at 4secs to provide messaging transport
|
||||||
static WiThrottle* firstThrottle;
|
static WiThrottle* firstThrottle;
|
||||||
static int getInt(byte * cmd);
|
static int getInt(byte * cmd);
|
||||||
static int getLocoId(byte * cmd);
|
static int getLocoId(byte * cmd);
|
||||||
|
@ -46,7 +46,8 @@ class WiThrottle {
|
||||||
|
|
||||||
MYLOCO myLocos[MAX_MY_LOCO];
|
MYLOCO myLocos[MAX_MY_LOCO];
|
||||||
bool heartBeatEnable;
|
bool heartBeatEnable;
|
||||||
byte callState; // 0=first, 1=second, 3=third, 4=fourth call... >4=rest.
|
bool initSent=false;
|
||||||
|
bool turnoutListSent=false;
|
||||||
unsigned long heartBeat;
|
unsigned long heartBeat;
|
||||||
|
|
||||||
void multithrottle(Print & stream, byte * cmd);
|
void multithrottle(Print & stream, byte * cmd);
|
||||||
|
|
|
@ -167,7 +167,7 @@ void WifiInterface::loop() {
|
||||||
int ch=wifiStream->read();
|
int ch=wifiStream->read();
|
||||||
|
|
||||||
// echo the char to the diagnostic stream in escaped format
|
// echo the char to the diagnostic stream in escaped format
|
||||||
StringFormatter::printEscape(&DIAGSERIAL,ch); // DIAG in disguise
|
// StringFormatter::printEscape(&DIAGSERIAL,ch); // DIAG in disguise
|
||||||
|
|
||||||
switch (loopstate) {
|
switch (loopstate) {
|
||||||
case 0: // looking for +IPD
|
case 0: // looking for +IPD
|
||||||
|
@ -208,7 +208,7 @@ void WifiInterface::loop() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ch=='>'){
|
if (ch=='>'){
|
||||||
DIAG(F("\n> [%e]\n"),buffer);
|
// DIAG(F("\n> [%e]\n"),buffer);
|
||||||
wifiStream->print((char *) buffer);
|
wifiStream->print((char *) buffer);
|
||||||
loopTimeoutStart=millis();
|
loopTimeoutStart=millis();
|
||||||
loopstate=closeAfter?11:0;
|
loopstate=closeAfter?11:0;
|
||||||
|
@ -232,7 +232,7 @@ void WifiInterface::loop() {
|
||||||
// AT this point we have read an incoming message into the buffer
|
// AT this point we have read an incoming message into the buffer
|
||||||
streamer.print('\0'); // null the end of the buffer so we can treat it as a string
|
streamer.print('\0'); // null the end of the buffer so we can treat it as a string
|
||||||
|
|
||||||
DIAG(F("\nWifiRead:%d:%e\n"),connectionId,buffer);
|
DIAG(F("\nWifi(%d)<-[%e]\n"),connectionId,buffer);
|
||||||
streamer.setBufferContentPosition(0,0); // reset write position to start of buffer
|
streamer.setBufferContentPosition(0,0); // reset write position to start of buffer
|
||||||
// SIDE EFFECT WARNING:::
|
// SIDE EFFECT WARNING:::
|
||||||
// We know that parser will read the entire buffer before starting to write to it.
|
// We know that parser will read the entire buffer before starting to write to it.
|
||||||
|
@ -256,7 +256,7 @@ void WifiInterface::loop() {
|
||||||
}
|
}
|
||||||
// prepare to send reply
|
// prepare to send reply
|
||||||
streamer.print('\0'); // null the end of the buffer so we can treat it as a string
|
streamer.print('\0'); // null the end of the buffer so we can treat it as a string
|
||||||
DIAG(F("\nWiFiInterface reply c(%d) l(%d) [%e]\n"),connectionId,streamer.available()-1,buffer);
|
DIAG(F("WiFi(%d)->[%e] l(%d)\n"),connectionId,buffer,streamer.available()-1);
|
||||||
StringFormatter::send(wifiStream,F("AT+CIPSEND=%d,%d\r\n"),connectionId,streamer.available()-1);
|
StringFormatter::send(wifiStream,F("AT+CIPSEND=%d,%d\r\n"),connectionId,streamer.available()-1);
|
||||||
loopTimeoutStart=millis();
|
loopTimeoutStart=millis();
|
||||||
loopstate=10; // non-blocking loop waits for > before sending
|
loopstate=10; // non-blocking loop waits for > before sending
|
||||||
|
|
Loading…
Reference in New Issue
Block a user