1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-23 08:06:13 +01:00

format/indentation change only

This commit is contained in:
Harald Barth 2022-01-06 23:03:57 +01:00
parent 1934fdd0e1
commit b0915e8332
8 changed files with 1252 additions and 1256 deletions

View File

@ -41,9 +41,8 @@ void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream
ringClient=stream->peekTargetMark(); ringClient=stream->peekTargetMark();
if (buffer[0] == '<') { if (buffer[0] == '<') {
clients[clientId]=COMMAND_TYPE; clients[clientId]=COMMAND_TYPE;
DCCEXParser::parse(stream, buffer, ring); DCCEXParser::parse(stream, buffer, ring);
} } else {
else {
clients[clientId]=WITHROTTLE_TYPE; clients[clientId]=WITHROTTLE_TYPE;
WiThrottle::getThrottle(clientId)->parse(ring, buffer); WiThrottle::getThrottle(clientId)->parse(ring, buffer);
} }
@ -68,32 +67,32 @@ void CommandDistributor::broadcast(bool includeWithrottleClients) {
if (ringClient!=NO_CLIENT) ring->commit(); if (ringClient!=NO_CLIENT) ring->commit();
/* loop through ring clients */ /* loop through ring clients */
for (byte clientId=0; clientId<sizeof(clients); clientId++) { for (byte clientId=0; clientId<sizeof(clients); clientId++) {
if (clients[clientId]==NONE_TYPE) continue; if (clients[clientId]==NONE_TYPE) continue;
if ( clients[clientId]==WITHROTTLE_TYPE && !includeWithrottleClients) continue; if ( clients[clientId]==WITHROTTLE_TYPE && !includeWithrottleClients) continue;
ring->mark(clientId); ring->mark(clientId);
broadcastBufferWriter->printBuffer(ring); broadcastBufferWriter->printBuffer(ring);
ring->commit(); ring->commit();
} }
if (ringClient!=NO_CLIENT) ring->mark(ringClient); if (ringClient!=NO_CLIENT) ring->mark(ringClient);
#endif #endif
broadcastBufferWriter->flush(); broadcastBufferWriter->flush();
} }
#else #else
// For a UNO/NANO we can broadcast direct to just one Serial instead of the ring // For a UNO/NANO we can broadcast direct to just one Serial instead of the ring
// Redirect ring output ditrect to Serial // Redirect ring output ditrect to Serial
#define broadcastBufferWriter &Serial #define broadcastBufferWriter &Serial
// and ignore the internal broadcast call. // and ignore the internal broadcast call.
void CommandDistributor::broadcast(bool includeWithrottleClients) { void CommandDistributor::broadcast(bool includeWithrottleClients) {
(void)includeWithrottleClients; (void)includeWithrottleClients;
} }
#endif #endif
void CommandDistributor::broadcastSensor(int16_t id, bool on ) { void CommandDistributor::broadcastSensor(int16_t id, bool on ) {
StringFormatter::send(broadcastBufferWriter,F("<%c %d>\n"), on?'Q':'q', id); StringFormatter::send(broadcastBufferWriter,F("<%c %d>\n"), on?'Q':'q', id);
broadcast(false); broadcast(false);
} }
void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) { void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) {
// For DCC++ classic compatibility, state reported to JMRI is 1 for thrown and 0 for closed; // For DCC++ classic compatibility, state reported to JMRI is 1 for thrown and 0 for closed;
@ -104,12 +103,12 @@ void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) {
StringFormatter::send(broadcastBufferWriter,F("PTA%c%d\n"), isClosed?'2':'4', id); StringFormatter::send(broadcastBufferWriter,F("PTA%c%d\n"), isClosed?'2':'4', id);
#endif #endif
broadcast(true); broadcast(true);
} }
void CommandDistributor::broadcastLoco(byte slot) { void CommandDistributor::broadcastLoco(byte slot) {
DCC::LOCO * sp=&DCC::speedTable[slot]; DCC::LOCO * sp=&DCC::speedTable[slot];
StringFormatter::send(broadcastBufferWriter,F("<l %d %d %d %l>\n"), StringFormatter::send(broadcastBufferWriter,F("<l %d %d %d %l>\n"),
sp->loco,slot,sp->speedCode,sp->functions); sp->loco,slot,sp->speedCode,sp->functions);
broadcast(false); broadcast(false);
#if defined(WIFI_ON) | defined(ETHERNET_ON) #if defined(WIFI_ON) | defined(ETHERNET_ON)
WiThrottle::markForBroadcast(sp->loco); WiThrottle::markForBroadcast(sp->loco);
@ -133,5 +132,3 @@ void CommandDistributor::broadcastPower() {
LCD(2,F("Power %S%S"),state=='1'?F("On"):F("Off"),reason); LCD(2,F("Power %S%S"),state=='1'?F("On"):F("Off"),reason);
broadcast(true); broadcast(true);
} }

View File

@ -69,7 +69,7 @@ void setup()
// This block is still executed for DIAGS if LCD not in use // This block is still executed for DIAGS if LCD not in use
LCD(0,F("DCC++ EX v%S"),F(VERSION)); LCD(0,F("DCC++ EX v%S"),F(VERSION));
LCD(1,F("Lic GPLv3")); LCD(1,F("Lic GPLv3"));
} }
// Responsibility 2: Start all the communications before the DCC engine // Responsibility 2: Start all the communications before the DCC engine
// Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi // Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi
@ -96,14 +96,14 @@ void setup()
// Invoke any DCC++EX commands in the form "SETUP("xxxx");"" found in optional file mySetup.h. // Invoke any DCC++EX commands in the form "SETUP("xxxx");"" found in optional file mySetup.h.
// This can be used to create turnouts, outputs, sensors etc. through the normal text commands. // This can be used to create turnouts, outputs, sensors etc. through the normal text commands.
#if __has_include ( "mySetup.h") #if __has_include ( "mySetup.h")
#define SETUP(cmd) DCCEXParser::parse(F(cmd)) #define SETUP(cmd) DCCEXParser::parse(F(cmd))
#include "mySetup.h" #include "mySetup.h"
#undef SETUP #undef SETUP
#endif #endif
#if defined(LCN_SERIAL) #if defined(LCN_SERIAL)
LCN_SERIAL.begin(115200); LCN_SERIAL.begin(115200);
LCN::init(LCN_SERIAL); LCN::init(LCN_SERIAL);
#endif #endif
LCD(3,F("Ready")); LCD(3,F("Ready"));
@ -121,7 +121,7 @@ void loop()
// Responsibility 2: handle any incoming commands on USB connection // Responsibility 2: handle any incoming commands on USB connection
SerialManager::loop(); SerialManager::loop();
// Responsibility 3: Optionally handle any incoming WiFi traffic // Responsibility 3: Optionally handle any incoming WiFi traffic
#if WIFI_ON #if WIFI_ON
WifiInterface::loop(); WifiInterface::loop();
#endif #endif
@ -132,7 +132,7 @@ void loop()
RMFT::loop(); // ignored if no automation RMFT::loop(); // ignored if no automation
#if defined(LCN_SERIAL) #if defined(LCN_SERIAL)
LCN::loop(); LCN::loop();
#endif #endif
LCDDisplay::loop(); // ignored if LCD not in use LCDDisplay::loop(); // ignored if LCD not in use
@ -146,8 +146,7 @@ void loop()
static int ramLowWatermark = __INT_MAX__; // replaced on first loop static int ramLowWatermark = __INT_MAX__; // replaced on first loop
int freeNow = minimumFreeMemory(); int freeNow = minimumFreeMemory();
if (freeNow < ramLowWatermark) if (freeNow < ramLowWatermark) {
{
ramLowWatermark = freeNow; ramLowWatermark = freeNow;
LCD(3,F("Free RAM=%5db"), ramLowWatermark); LCD(3,F("Free RAM=%5db"), ramLowWatermark);
} }

View File

@ -200,7 +200,7 @@ void DCC::changeFn( int cab, int16_t functionNumber) {
if (reg<0) return; if (reg<0) return;
unsigned long funcmask = (1UL<<functionNumber); unsigned long funcmask = (1UL<<functionNumber);
speedTable[reg].functions ^= funcmask; speedTable[reg].functions ^= funcmask;
updateGroupflags(speedTable[reg].groupFlags, functionNumber); updateGroupflags(speedTable[reg].groupFlags, functionNumber);
CommandDistributor::broadcastLoco(reg); CommandDistributor::broadcastLoco(reg);
} }

1223
RMFT2.cpp

File diff suppressed because it is too large Load Diff

View File

@ -117,11 +117,11 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
for(Turnout *tt=Turnout::first();tt!=NULL;tt=tt->next()){ for(Turnout *tt=Turnout::first();tt!=NULL;tt=tt->next()){
int id=tt->getId(); int id=tt->getId();
StringFormatter::send(stream,F("]\\[%d}|{"), id); StringFormatter::send(stream,F("]\\[%d}|{"), id);
#ifdef RMFT_ACTIVE #ifdef RMFT_ACTIVE
RMFT2::emitTurnoutDescription(stream,id); RMFT2::emitTurnoutDescription(stream,id);
#else #else
StringFormatter::send(stream,F("%d"), id); StringFormatter::send(stream,F("%d"), id);
#endif #endif
StringFormatter::send(stream,F("}|{%c"), Turnout::isClosed(id)?'2':'4'); StringFormatter::send(stream,F("}|{%c"), Turnout::isClosed(id)?'2':'4');
} }
StringFormatter::send(stream,F("\n")); StringFormatter::send(stream,F("\n"));
@ -134,105 +134,104 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
#ifdef RMFT_ACTIVE #ifdef RMFT_ACTIVE
RMFT2::emitWithrottleRouteList(stream); RMFT2::emitWithrottleRouteList(stream);
#endif #endif
// allow heartbeat to slow down once all metadata sent // allow heartbeat to slow down once all metadata sent
StringFormatter::send(stream,F("*%d\n"),HEARTBEAT_SECONDS); StringFormatter::send(stream,F("*%d\n"),HEARTBEAT_SECONDS);
} }
} }
while (cmd[0]) { while (cmd[0]) {
switch (cmd[0]) { switch (cmd[0]) {
case '*': // heartbeat control case '*': // heartbeat control
if (cmd[1]=='+') heartBeatEnable=true; if (cmd[1]=='+') heartBeatEnable=true;
else if (cmd[1]=='-') heartBeatEnable=false; else if (cmd[1]=='-') heartBeatEnable=false;
break; break;
case 'P': case 'P':
if (cmd[1]=='P' && cmd[2]=='A' ) { //PPA power mode if (cmd[1]=='P' && cmd[2]=='A' ) { //PPA power mode
DCCWaveform::mainTrack.setPowerMode(cmd[3]=='1'?POWERMODE::ON:POWERMODE::OFF); DCCWaveform::mainTrack.setPowerMode(cmd[3]=='1'?POWERMODE::ON:POWERMODE::OFF);
if (MotorDriver::commonFaultPin) // commonFaultPin prevents individual track handling if (MotorDriver::commonFaultPin) // commonFaultPin prevents individual track handling
DCCWaveform::progTrack.setPowerMode(cmd[3]=='1'?POWERMODE::ON:POWERMODE::OFF); DCCWaveform::progTrack.setPowerMode(cmd[3]=='1'?POWERMODE::ON:POWERMODE::OFF);
CommandDistributor::broadcastPower(); CommandDistributor::broadcastPower();
} }
#if defined(RMFT_ACTIVE) #if defined(RMFT_ACTIVE)
else if (cmd[1]=='R' && cmd[2]=='A' && cmd[3]=='2' ) { // Route activate else if (cmd[1]=='R' && cmd[2]=='A' && cmd[3]=='2' ) { // Route activate
// exrail routes are RA2Rn , Animations are RA2An // exrail routes are RA2Rn , Animations are RA2An
int route=getInt(cmd+5); int route=getInt(cmd+5);
uint16_t cab=cmd[4]=='A' ? mostRecentCab : 0; uint16_t cab=cmd[4]=='A' ? mostRecentCab : 0;
RMFT2::createNewTask(route, cab); RMFT2::createNewTask(route, cab);
} }
#endif #endif
else if (cmd[1]=='T' && cmd[2]=='A') { // PTA accessory toggle else if (cmd[1]=='T' && cmd[2]=='A') { // PTA accessory toggle
int id=getInt(cmd+4); int id=getInt(cmd+4);
if (!Turnout::exists(id)) { if (!Turnout::exists(id)) {
// If turnout does not exist, create it // If turnout does not exist, create it
int addr = ((id - 1) / 4) + 1; int addr = ((id - 1) / 4) + 1;
int subaddr = (id - 1) % 4; int subaddr = (id - 1) % 4;
DCCTurnout::create(id,addr,subaddr); DCCTurnout::create(id,addr,subaddr);
StringFormatter::send(stream, F("HmTurnout %d created\n"),id); StringFormatter::send(stream, F("HmTurnout %d created\n"),id);
} }
switch (cmd[3]) { switch (cmd[3]) {
// T and C according to RCN-213 where 0 is Stop, Red, Thrown, Diverging. // T and C according to RCN-213 where 0 is Stop, Red, Thrown, Diverging.
case 'T': case 'T':
Turnout::setClosed(id,false); Turnout::setClosed(id,false);
break; break;
case 'C': case 'C':
Turnout::setClosed(id,true); Turnout::setClosed(id,true);
break; break;
case '2': case '2':
Turnout::setClosed(id,!Turnout::isClosed(id)); Turnout::setClosed(id,!Turnout::isClosed(id));
break; break;
default : default :
Turnout::setClosed(id,true); Turnout::setClosed(id,true);
break; break;
} }
StringFormatter::send(stream, F("PTA%c%d\n"),Turnout::isClosed(id)?'2':'4',id ); StringFormatter::send(stream, F("PTA%c%d\n"),Turnout::isClosed(id)?'2':'4',id );
} }
break; break;
case 'N': // Heartbeat (2), only send if connection completed by 'HU' message case 'N': // Heartbeat (2), only send if connection completed by 'HU' message
if (initSent) { if (initSent) {
StringFormatter::send(stream, F("*%d\n"),HEARTBEAT_SECONDS); // return timeout value StringFormatter::send(stream, F("*%d\n"),HEARTBEAT_SECONDS); // return timeout value
} }
break; break;
case 'M': // multithrottle case 'M': // multithrottle
multithrottle(stream, cmd); multithrottle(stream, cmd);
break; break;
case 'H': // send initial connection info after receiving "HU" message case 'H': // send initial connection info after receiving "HU" message
if (cmd[1] == 'U') { if (cmd[1] == 'U') {
StringFormatter::send(stream,F("VN2.0\nHTDCC-EX\nRL0\n")); StringFormatter::send(stream,F("VN2.0\nHTDCC-EX\nRL0\n"));
StringFormatter::send(stream,F("HtDCC-EX v%S, %S, %S, %S\n"), F(VERSION), F(ARDUINO_TYPE), DCC::getMotorShieldName(), F(GITHUB_SHA)); StringFormatter::send(stream,F("HtDCC-EX v%S, %S, %S, %S\n"), F(VERSION), F(ARDUINO_TYPE), DCC::getMotorShieldName(), F(GITHUB_SHA));
StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[THROW}|{2]\\[CLOSE}|{4\n")); StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[THROW}|{2]\\[CLOSE}|{4\n"));
StringFormatter::send(stream,F("PPA%x\n"),DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON); StringFormatter::send(stream,F("PPA%x\n"),DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON);
#ifdef RMFT_ACTIVE #ifdef RMFT_ACTIVE
RMFT2::emitWithrottleRoster(stream); RMFT2::emitWithrottleRoster(stream);
#endif #endif
// set heartbeat to 1 second because we need to sync the metadata // set heartbeat to 1 second because we need to sync the metadata
StringFormatter::send(stream,F("*1\n")); StringFormatter::send(stream,F("*1\n"));
initSent = true; initSent = true;
} }
break; break;
case 'Q': // case 'Q': //
LOOPLOCOS('*', -1) { // tell client to drop any locos still assigned to this WiThrottle LOOPLOCOS('*', -1) { // tell client to drop any locos still assigned to this WiThrottle
if (myLocos[loco].throttle!='\0') { if (myLocos[loco].throttle!='\0') {
StringFormatter::send(stream, F("M%c-%c%d<;>\n"), myLocos[loco].throttle, LorS(myLocos[loco].cab), myLocos[loco].cab); StringFormatter::send(stream, F("M%c-%c%d<;>\n"), myLocos[loco].throttle, LorS(myLocos[loco].cab), myLocos[loco].cab);
} }
} }
if (Diag::WITHROTTLE) DIAG(F("%l WiThrottle(%d) Quit"),millis(),clientid); if (Diag::WITHROTTLE) DIAG(F("%l WiThrottle(%d) Quit"),millis(),clientid);
delete this; delete this;
break; break;
}
// skip over cmd until 0 or past \r or \n
while(*cmd !='\0' && *cmd != '\r' && *cmd !='\n') cmd++;
if (*cmd!='\0') cmd++; // skip \r or \n
}
}
int WiThrottle::getInt(byte * cmd) {
int i=0;
while (cmd[0]>='0' && cmd[0]<='9') {
i=i*10 + (cmd[0]-'0');
cmd++;
} }
return i; // skip over cmd until 0 or past \r or \n
while(*cmd !='\0' && *cmd != '\r' && *cmd !='\n') cmd++;
if (*cmd!='\0') cmd++; // skip \r or \n
}
}
int WiThrottle::getInt(byte * cmd) {
int i=0;
while (cmd[0]>='0' && cmd[0]<='9') {
i=i*10 + (cmd[0]-'0');
cmd++;
}
return i;
} }
int WiThrottle::getLocoId(byte * cmd) { int WiThrottle::getLocoId(byte * cmd) {
@ -242,183 +241,183 @@ int WiThrottle::getLocoId(byte * cmd) {
} }
void WiThrottle::multithrottle(RingStream * stream, byte * cmd){ void WiThrottle::multithrottle(RingStream * stream, byte * cmd){
char throttleChar=cmd[1]; char throttleChar=cmd[1];
int locoid=getLocoId(cmd+3); // -1 for * int locoid=getLocoId(cmd+3); // -1 for *
byte * aval=cmd; byte * aval=cmd;
while(*aval !=';' && *aval !='\0') aval++; while(*aval !=';' && *aval !='\0') aval++;
if (*aval) aval+=2; // skip ;> if (*aval) aval+=2; // skip ;>
// DIAG(F("Multithrottle aval=%c cab=%d"), aval[0],locoid); // DIAG(F("Multithrottle aval=%c cab=%d"), aval[0],locoid);
switch(cmd[2]) { switch(cmd[2]) {
case '+': // add loco request case '+': // add loco request
if (cmd[3]=='*') { if (cmd[3]=='*') {
// M+* means get loco from prog track, then join tracks ready to drive away // M+* means get loco from prog track, then join tracks ready to drive away
// Stash the things the callback will need later // Stash the things the callback will need later
stashStream= stream; stashStream= stream;
stashClient=stream->peekTargetMark(); stashClient=stream->peekTargetMark();
stashThrottleChar=throttleChar; stashThrottleChar=throttleChar;
stashInstance=this; stashInstance=this;
// ask DCC to call us back when the loco id has been read // ask DCC to call us back when the loco id has been read
DCC::getLocoId(getLocoCallback); // will remove any previous join DCC::getLocoId(getLocoCallback); // will remove any previous join
return; // return nothing in stream as response is sent later in the callback return; // return nothing in stream as response is sent later in the callback
} }
//return error if address zero requested //return error if address zero requested
if (locoid==0) { if (locoid==0) {
StringFormatter::send(stream, F("HMAddress '0' not supported!\n"), cmd[3] ,locoid); StringFormatter::send(stream, F("HMAddress '0' not supported!\n"), cmd[3] ,locoid);
return; return;
} }
//return error if L or S from request doesn't match DCC++ assumptions //return error if L or S from request doesn't match DCC++ assumptions
if (cmd[3] != LorS(locoid)) { if (cmd[3] != LorS(locoid)) {
StringFormatter::send(stream, F("HMLength '%c' not valid for %d!\n"), cmd[3] ,locoid); StringFormatter::send(stream, F("HMLength '%c' not valid for %d!\n"), cmd[3] ,locoid);
return; return;
} }
//use first empty "slot" on this client's list, will be added to DCC registration list //use first empty "slot" on this client's list, will be added to DCC registration list
for (int loco=0;loco<MAX_MY_LOCO;loco++) { for (int loco=0;loco<MAX_MY_LOCO;loco++) {
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;
myLocos[loco].functionMap=DCC::getFunctionMap(locoid); myLocos[loco].functionMap=DCC::getFunctionMap(locoid);
myLocos[loco].broadcastPending=true; // means speed/dir will be sent later myLocos[loco].broadcastPending=true; // means speed/dir will be sent later
mostRecentCab=locoid; mostRecentCab=locoid;
StringFormatter::send(stream, F("M%c+%c%d<;>\n"), throttleChar, cmd[3] ,locoid); //tell client to add loco StringFormatter::send(stream, F("M%c+%c%d<;>\n"), throttleChar, cmd[3] ,locoid); //tell client to add loco
int fkeys=29; int fkeys=29;
myLocos[loco].functionToggles=1<<2; // F2 (HORN) is a non-toggle myLocos[loco].functionToggles=1<<2; // F2 (HORN) is a non-toggle
#ifdef RMFT_ACTIVE #ifdef RMFT_ACTIVE
const char * functionNames=(char *) RMFT2::getRosterFunctions(locoid); const char * functionNames=(char *) RMFT2::getRosterFunctions(locoid);
if (!functionNames) { if (!functionNames) {
// no roster, use presets as above // no roster, use presets as above
} }
else if (GETFLASH(functionNames)=='\0') { else if (GETFLASH(functionNames)=='\0') {
// "" = Roster but no functions given // "" = Roster but no functions given
fkeys=0; fkeys=0;
} }
else { else {
// we have function names... // we have function names...
// scan names list emitting names, counting functions and // scan names list emitting names, counting functions and
// flagging non-toggling things like horn. // flagging non-toggling things like horn.
myLocos[loco].functionToggles =0; myLocos[loco].functionToggles =0;
StringFormatter::send(stream, F("M%cL%c%d<;>]\\["), throttleChar,cmd[3],locoid); StringFormatter::send(stream, F("M%cL%c%d<;>]\\["), throttleChar,cmd[3],locoid);
fkeys=0; fkeys=0;
bool firstchar=true; bool firstchar=true;
for (int fx=0;;fx++) { for (int fx=0;;fx++) {
char c=GETFLASH(functionNames+fx); char c=GETFLASH(functionNames+fx);
if (c=='\0') { if (c=='\0') {
fkeys++; fkeys++;
break; break;
} }
if (c=='/') { if (c=='/') {
fkeys++; fkeys++;
StringFormatter::send(stream,F("]\\[")); StringFormatter::send(stream,F("]\\["));
firstchar=true; firstchar=true;
} }
else if (firstchar && c=='*') { else if (firstchar && c=='*') {
myLocos[loco].functionToggles |= 1UL<<fkeys; myLocos[loco].functionToggles |= 1UL<<fkeys;
firstchar=false; firstchar=false;
} }
else { else {
firstchar=false; firstchar=false;
stream->write(c); stream->write(c);
} }
} }
StringFormatter::send(stream,F("\n")); StringFormatter::send(stream,F("\n"));
} }
#endif #endif
for(int fKey=0; fKey<fkeys; fKey++) { for(int fKey=0; fKey<fkeys; fKey++) {
int fstate=DCC::getFn(locoid,fKey); int fstate=DCC::getFn(locoid,fKey);
if (fstate>=0) StringFormatter::send(stream,F("M%cA%c%d<;>F%d%d\n"),throttleChar,cmd[3],locoid,fstate,fKey); if (fstate>=0) StringFormatter::send(stream,F("M%cA%c%d<;>F%d%d\n"),throttleChar,cmd[3],locoid,fstate,fKey);
} }
//speed and direction will be published at next broadcast cycle //speed and direction will be published at next broadcast cycle
StringFormatter::send(stream, F("M%cA%c%d<;>s1\n"), throttleChar, cmd[3], locoid); //default speed step 128 StringFormatter::send(stream, F("M%cA%c%d<;>s1\n"), throttleChar, cmd[3], locoid); //default speed step 128
return; return;
} }
} }
StringFormatter::send(stream, F("HMMax locos (%d) exceeded, %d not added!\n"), MAX_MY_LOCO ,locoid); StringFormatter::send(stream, F("HMMax locos (%d) exceeded, %d not added!\n"), MAX_MY_LOCO ,locoid);
break; break;
case '-': // remove loco(s) from this client (leave in DCC registration) case '-': // remove loco(s) from this client (leave in DCC registration)
LOOPLOCOS(throttleChar, locoid) { LOOPLOCOS(throttleChar, locoid) {
myLocos[loco].throttle='\0'; myLocos[loco].throttle='\0';
StringFormatter::send(stream, F("M%c-%c%d<;>\n"), throttleChar, LorS(myLocos[loco].cab), myLocos[loco].cab); StringFormatter::send(stream, F("M%c-%c%d<;>\n"), throttleChar, LorS(myLocos[loco].cab), myLocos[loco].cab);
} }
break; break;
case 'A': case 'A':
locoAction(stream,aval, throttleChar, locoid); locoAction(stream,aval, throttleChar, locoid);
} }
} }
void WiThrottle::locoAction(RingStream * stream, byte* aval, char throttleChar, int cab){ void WiThrottle::locoAction(RingStream * 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("Loco Action aval=%c%c throttleChar=%c, cab=%d"), aval[0],aval[1],throttleChar, cab); // DIAG(F("Loco Action aval=%c%c throttleChar=%c, cab=%d"), aval[0],aval[1],throttleChar, cab);
(void) stream; (void) stream;
switch (aval[0]) { switch (aval[0]) {
case 'V': // Vspeed case 'V': // Vspeed
{ {
int witSpeed=getInt(aval+1); int witSpeed=getInt(aval+1);
LOOPLOCOS(throttleChar, cab) { LOOPLOCOS(throttleChar, cab) {
mostRecentCab=myLocos[loco].cab; mostRecentCab=myLocos[loco].cab;
DCC::setThrottle(myLocos[loco].cab, WiTToDCCSpeed(witSpeed), DCC::getThrottleDirection(myLocos[loco].cab)); DCC::setThrottle(myLocos[loco].cab, WiTToDCCSpeed(witSpeed), DCC::getThrottleDirection(myLocos[loco].cab));
// SetThrottle will cause speed change broadcast // SetThrottle will cause speed change broadcast
} }
} }
break; break;
case 'F': // Function key pressed/released case 'F': // Function key pressed/released
{ {
bool pressed=aval[1]=='1'; bool pressed=aval[1]=='1';
int fKey = getInt(aval+2); int fKey = getInt(aval+2);
LOOPLOCOS(throttleChar, cab) { LOOPLOCOS(throttleChar, cab) {
bool unsetOnRelease = myLocos[loco].functionToggles & (1L<<fKey); bool unsetOnRelease = myLocos[loco].functionToggles & (1L<<fKey);
if (unsetOnRelease) DCC::setFn(myLocos[loco].cab,fKey, pressed); if (unsetOnRelease) DCC::setFn(myLocos[loco].cab,fKey, pressed);
else if (pressed) DCC::changeFn(myLocos[loco].cab, fKey); else if (pressed) DCC::changeFn(myLocos[loco].cab, fKey);
} }
break; break;
} }
case 'q': case 'q':
if (aval[1]=='V' || aval[1]=='R' ) { //qV or qR if (aval[1]=='V' || aval[1]=='R' ) { //qV or qR
// just flag the loco for broadcast and it will happen. // just flag the loco for broadcast and it will happen.
LOOPLOCOS(throttleChar, cab) { LOOPLOCOS(throttleChar, cab) {
myLocos[loco].broadcastPending=true; myLocos[loco].broadcastPending=true;
} }
} }
break; break;
case 'R': case 'R':
{ {
bool forward=aval[1]!='0'; bool forward=aval[1]!='0';
LOOPLOCOS(throttleChar, cab) { LOOPLOCOS(throttleChar, cab) {
mostRecentCab=myLocos[loco].cab; mostRecentCab=myLocos[loco].cab;
DCC::setThrottle(myLocos[loco].cab, DCC::getThrottleSpeed(myLocos[loco].cab), forward); DCC::setThrottle(myLocos[loco].cab, DCC::getThrottleSpeed(myLocos[loco].cab), forward);
// setThrottle will cause a broadcast so notification will be sent // setThrottle will cause a broadcast so notification will be sent
} }
} }
break; break;
case 'X': case 'X':
//Emergency Stop (speed code 1) //Emergency Stop (speed code 1)
LOOPLOCOS(throttleChar, cab) { LOOPLOCOS(throttleChar, cab) {
DCC::setThrottle(myLocos[loco].cab, 1, DCC::getThrottleDirection(myLocos[loco].cab)); DCC::setThrottle(myLocos[loco].cab, 1, DCC::getThrottleDirection(myLocos[loco].cab));
// setThrottle will cause a broadcast so notification will be sent // setThrottle will cause a broadcast so notification will be sent
} }
break; break;
case 'I': // Idle, set speed to 0 case 'I': // Idle, set speed to 0
case 'Q': // Quit, set speed to 0 case 'Q': // Quit, set speed to 0
LOOPLOCOS(throttleChar, cab) { LOOPLOCOS(throttleChar, cab) {
mostRecentCab=myLocos[loco].cab; mostRecentCab=myLocos[loco].cab;
DCC::setThrottle(myLocos[loco].cab, 0, DCC::getThrottleDirection(myLocos[loco].cab)); DCC::setThrottle(myLocos[loco].cab, 0, DCC::getThrottleDirection(myLocos[loco].cab));
// setThrottle will cause a broadcast so notification will be sent // setThrottle will cause a broadcast so notification will be sent
} }
break; break;
} }
} }
// convert between DCC++ speed values and WiThrottle speed values // convert between DCC++ speed values and WiThrottle speed values
int WiThrottle::DCCToWiTSpeed(int DCCSpeed) { int WiThrottle::DCCToWiTSpeed(int DCCSpeed) {
if (DCCSpeed == 0) return 0; //stop is stop if (DCCSpeed == 0) return 0; //stop is stop
if (DCCSpeed == 1) return -1; //eStop value if (DCCSpeed == 1) return -1; //eStop value
return DCCSpeed - 1; //offset others by 1 return DCCSpeed - 1; //offset others by 1
} }
// convert between WiThrottle speed values and DCC++ speed values // convert between WiThrottle speed values and DCC++ speed values
int WiThrottle::WiTToDCCSpeed(int WiTSpeed) { int WiThrottle::WiTToDCCSpeed(int WiTSpeed) {
if (WiTSpeed == 0) return 0; //stop is stop if (WiTSpeed == 0) return 0; //stop is stop
if (WiTSpeed == -1) return 1; //eStop value if (WiTSpeed == -1) return 1; //eStop value
@ -428,7 +427,7 @@ int WiThrottle::WiTToDCCSpeed(int WiTSpeed) {
void WiThrottle::loop(RingStream * stream) { void WiThrottle::loop(RingStream * stream) {
// for each WiThrottle, check the heartbeat and broadcast needed // for each WiThrottle, check the heartbeat and broadcast needed
for (WiThrottle* wt=firstThrottle; wt!=NULL ; wt=wt->nextThrottle) for (WiThrottle* wt=firstThrottle; wt!=NULL ; wt=wt->nextThrottle)
wt->checkHeartbeat(stream); wt->checkHeartbeat(stream);
} }
@ -436,7 +435,7 @@ void WiThrottle::loop(RingStream * stream) {
void WiThrottle::checkHeartbeat(RingStream * stream) { void WiThrottle::checkHeartbeat(RingStream * stream) {
// if eStop time passed... eStop any locos still assigned to this client and then drop the connection // if eStop time passed... eStop any locos still assigned to this client and then drop the connection
if(heartBeatEnable && (millis()-heartBeat > ESTOP_SECONDS*1000)) { if(heartBeatEnable && (millis()-heartBeat > ESTOP_SECONDS*1000)) {
if (Diag::WITHROTTLE) DIAG(F("%l WiThrottle(%d) eStop(%ds) timeout, drop connection"), millis(), clientid, ESTOP_SECONDS); if (Diag::WITHROTTLE) DIAG(F("%l WiThrottle(%d) eStop(%ds) timeout, drop connection"), millis(), clientid, ESTOP_SECONDS);
LOOPLOCOS('*', -1) { LOOPLOCOS('*', -1) {
if (myLocos[loco].throttle!='\0') { if (myLocos[loco].throttle!='\0') {
if (Diag::WITHROTTLE) DIAG(F("%l eStopping cab %d"),millis(),myLocos[loco].cab); if (Diag::WITHROTTLE) DIAG(F("%l eStopping cab %d"),millis(),myLocos[loco].cab);
@ -445,45 +444,45 @@ void WiThrottle::checkHeartbeat(RingStream * stream) {
} }
delete this; delete this;
return; return;
} }
// send any outstanding speed/direction/function changes for this clients locos // send any outstanding speed/direction/function changes for this clients locos
// Changes may have been caused by this client, or another non-Withrottle or Exrail // Changes may have been caused by this client, or another non-Withrottle or Exrail
bool streamHasBeenMarked=false; bool streamHasBeenMarked=false;
LOOPLOCOS('*', -1) { LOOPLOCOS('*', -1) {
if (myLocos[loco].throttle!='\0' && myLocos[loco].broadcastPending) { if (myLocos[loco].throttle!='\0' && myLocos[loco].broadcastPending) {
if (!streamHasBeenMarked) { if (!streamHasBeenMarked) {
stream->mark(clientid); stream->mark(clientid);
streamHasBeenMarked=true; streamHasBeenMarked=true;
} }
myLocos[loco].broadcastPending=false; myLocos[loco].broadcastPending=false;
int cab=myLocos[loco].cab; int cab=myLocos[loco].cab;
char lors=LorS(cab); char lors=LorS(cab);
char throttle=myLocos[loco].throttle; char throttle=myLocos[loco].throttle;
StringFormatter::send(stream,F("M%cA%c%d<;>V%d\n"), StringFormatter::send(stream,F("M%cA%c%d<;>V%d\n"),
throttle, lors , cab, DCCToWiTSpeed(DCC::getThrottleSpeed(cab))); throttle, lors , cab, DCCToWiTSpeed(DCC::getThrottleSpeed(cab)));
StringFormatter::send(stream,F("M%cA%c%d<;>R%d\n"), StringFormatter::send(stream,F("M%cA%c%d<;>R%d\n"),
throttle, lors , cab, DCC::getThrottleDirection(cab)); throttle, lors , cab, DCC::getThrottleDirection(cab));
// compare the DCC functionmap with the local copy and send changes // compare the DCC functionmap with the local copy and send changes
uint32_t dccFunctionMap=DCC::getFunctionMap(cab); uint32_t dccFunctionMap=DCC::getFunctionMap(cab);
uint32_t myFunctionMap=myLocos[loco].functionMap; uint32_t myFunctionMap=myLocos[loco].functionMap;
myLocos[loco].functionMap=dccFunctionMap; myLocos[loco].functionMap=dccFunctionMap;
// loop the maps sending any bit changed // loop the maps sending any bit changed
// Loop is terminated as soon as no changes are left // Loop is terminated as soon as no changes are left
for (byte fn=0;dccFunctionMap!=myFunctionMap;fn++) { for (byte fn=0;dccFunctionMap!=myFunctionMap;fn++) {
if ((dccFunctionMap&1) != (myFunctionMap&1)) { if ((dccFunctionMap&1) != (myFunctionMap&1)) {
StringFormatter::send(stream,F("M%cA%c%d<;>F%c%d\n"), StringFormatter::send(stream,F("M%cA%c%d<;>F%c%d\n"),
throttle, lors , cab, (dccFunctionMap&1)?'1':'0',fn); throttle, lors , cab, (dccFunctionMap&1)?'1':'0',fn);
} }
// shift just checked bit off end of both maps // shift just checked bit off end of both maps
dccFunctionMap>>=1; dccFunctionMap>>=1;
myFunctionMap>>=1; myFunctionMap>>=1;
}
} }
} }
if (streamHasBeenMarked) stream->commit(); }
if (streamHasBeenMarked) stream->commit();
} }
void WiThrottle::markForBroadcast(int cab) { void WiThrottle::markForBroadcast(int cab) {
@ -492,13 +491,13 @@ void WiThrottle::markForBroadcast(int cab) {
} }
void WiThrottle::markForBroadcast2(int cab) { void WiThrottle::markForBroadcast2(int cab) {
LOOPLOCOS('*', cab) { LOOPLOCOS('*', cab) {
myLocos[loco].broadcastPending=true; myLocos[loco].broadcastPending=true;
} }
} }
char WiThrottle::LorS(int cab) { char WiThrottle::LorS(int cab) {
return (cab<=HIGHEST_SHORT_ADDR)?'S':'L'; return (cab<=HIGHEST_SHORT_ADDR)?'S':'L';
} }
// Drive Away feature. Callback handling // Drive Away feature. Callback handling

View File

@ -335,10 +335,10 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password,
void WifiInterface::ATCommand(HardwareSerial * stream,const byte * command) { void WifiInterface::ATCommand(HardwareSerial * stream,const byte * command) {
command++; command++;
if (*command=='\0') { // User gave <+> command if (*command=='\0') { // User gave <+> command
stream->print(F("\nES AT command passthrough mode, use ! to exit\n")); stream->print(F("\nES AT command passthrough mode, use ! to exit\n"));
while(stream->available()) stream->read(); // Drain serial input first while(stream->available()) stream->read(); // Drain serial input first
bool startOfLine=true; bool startOfLine=true;
while(true) { while(true) {
while (wifiStream->available()) stream->write(wifiStream->read()); while (wifiStream->available()) stream->write(wifiStream->read());
if (stream->available()) { if (stream->available()) {
int cx=stream->read(); int cx=stream->read();
@ -348,19 +348,19 @@ void WifiInterface::ATCommand(HardwareSerial * stream,const byte * command) {
else startOfLine=false; else startOfLine=false;
stream->write(cx); stream->write(cx);
wifiStream->write(cx); wifiStream->write(cx);
} }
} }
stream->print(F("Passthrough Ended")); stream->print(F("Passthrough Ended"));
return; return;
} }
if (*command=='X') { if (*command=='X') {
connected = true; connected = true;
DIAG(F("++++++ Wifi Connction forced on ++++++++")); DIAG(F("++++++ Wifi Connction forced on ++++++++"));
} }
else { else {
StringFormatter:: send(wifiStream, F("AT+%s\r\n"), command); StringFormatter:: send(wifiStream, F("AT+%s\r\n"), command);
checkForOK(10000, true); checkForOK(10000, true);
} }
} }