From 9cdabb0acfb0a295fc3cba228647978d18765fc1 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 7 Dec 2022 09:52:28 +0000 Subject: [PATCH] Apparently working Nucleo --- CommandDistributor.cpp | 2 +- CommandStation-EX.ino | 21 ++- EXRAIL2.cpp | 28 +--- EXRAIL2.h | 3 - RingStream.cpp | 16 +- RingStream.h | 2 +- SerialManager.cpp | 4 +- StringFormatter.cpp | 7 +- WiThrottle.cpp | 327 +++++++++++++++++------------------------ WiThrottle.h | 11 +- WifiInboundHandler.cpp | 31 ++-- WifiInboundHandler.h | 3 + 12 files changed, 212 insertions(+), 243 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 8c79652..9f2baa3 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -97,7 +97,7 @@ void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream } void CommandDistributor::forget(byte clientId) { - // keep for later if (clients[clientId]==WITHROTTLE_TYPE) WiThrottle::forget(clientId); + if (clients[clientId]==WITHROTTLE_TYPE) WiThrottle::forget(clientId); clients[clientId]=NONE_TYPE; } #endif diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index f003ef6..ceb9533 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -124,52 +124,69 @@ void setup() #endif LCD(3, F("Ready")); CommandDistributor::broadcastPower(); + Diag::WIFI=true; } void loop() { + static bool XX=true; + if (XX) DIAG(F("loop 1")); // The main sketch has responsibilities during loop() // Responsibility 1: Handle DCC background processes // (loco reminders and power checks) DCC::loop(); - +if (XX) DIAG(F("loop 2")); // Responsibility 2: handle any incoming commands on USB connection SerialManager::loop(); - +if (XX) DIAG(F("loop 3")); // Responsibility 3: Optionally handle any incoming WiFi traffic #ifndef ARDUINO_ARCH_ESP32 #if WIFI_ON +if (XX) DIAG(F("loop 4")); WifiInterface::loop(); #endif //WIFI_ON #else //ARDUINO_ARCH_ESP32 #ifndef WIFI_TASK_ON_CORE0 + if (XX) DIAG(F("loop 5")); WifiESP::loop(); #endif #endif //ARDUINO_ARCH_ESP32 #if ETHERNET_ON + if (XX) DIAG(F("loop 6")); EthernetInterface::loop(); #endif + if (XX) DIAG(F("loop 7")); RMFT::loop(); // ignored if no automation + if (XX) DIAG(F("loop 8")); #if defined(LCN_SERIAL) LCN::loop(); + if (XX) DIAG(F("loop 9")); + #endif LCDDisplay::loop(); // ignored if LCD not in use + if (XX) DIAG(F("loop A")); // Handle/update IO devices. IODevice::loop(); + if (XX) DIAG(F("loop B")); Sensor::checkAll(); // Update and print changes + if (XX) DIAG(F("loop C")); // Report any decrease in memory (will automatically trigger on first call) static int ramLowWatermark = __INT_MAX__; // replaced on first loop int freeNow = DCCTimer::getMinimumFreeMemory(); + if (XX) DIAG(F("loop D")); + if (freeNow < ramLowWatermark) { ramLowWatermark = freeNow; LCD(3,F("Free RAM=%5db"), ramLowWatermark); } + if (XX) DIAG(F("loop E")); + XX=false; } diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 3e8fe61..d72f8b0 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -95,23 +95,6 @@ LookList * RMFT2::onGreenLookup=NULL; #define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter) #define SKIPOP progCounter+=3 -// RouteCodeFar is a far pointer to flash on anything other than a uno/nano where it is just a near pointer to flash -uint32_t RMFT2::RouteCodeFar; - -uint16_t RMFT2::getOperand2(uint32_t farAddr) { - #if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) - // AVR_MEGA memory uses far pointers - return pgm_read_word_far(farAddr); - #elif defined(ARDUINO_ARCH_AVR) - // UNO/NANO have no far memory - return pgm_read_word_near(farAddr); - #else - // other cpus dont care but may be averse to reading an int16_tr at an odd byte boundary. - const byte * op=(const byte *)farAddr; - return *op | (*(op+1) << 8); - #endif - } - // getOperand instance version, uses progCounter from instance. uint16_t RMFT2::getOperand(byte n) { return getOperand(progCounter,n); @@ -119,7 +102,13 @@ uint16_t RMFT2::getOperand(byte n) { // getOperand static version, must be provided prog counter from loop etc. uint16_t RMFT2::getOperand(int progCounter,byte n) { - return getOperand2(RouteCodeFar+progCounter+1+(n*3)); + int offset=progCounter+1+(n*3); + if (offset&1) { + byte lsb=GETHIGHFLASH(RouteCode,offset); + byte msb=GETHIGHFLASH(RouteCode,offset+1); + return msb<<8|lsb; + } + return GETHIGHFLASHW(RouteCode,offset); } LookList::LookList(int16_t size) { @@ -168,8 +157,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { /* static */ void RMFT2::begin() { - RouteCodeFar=GETFARPTR(RMFT2::RouteCode); - DIAG(F("EXRAIL RouteAddr=%l"),RouteCodeFar); + DIAG(F("EXRAIL RoutCode at =%P"),RouteCode); bool saved_diag=diag; diag=true; diff --git a/EXRAIL2.h b/EXRAIL2.h index 9831a41..6e6d0ca 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -143,7 +143,6 @@ private: OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL); static void handleEvent(const FSH* reason,LookList* handlers, int16_t id); static uint16_t getOperand(int progCounter,byte n); - static uint16_t getOperand2(uint32_t farAddr); static RMFT2 * loopTask; static RMFT2 * pausingTask; void delayMe(long millisecs); @@ -170,8 +169,6 @@ private: static LookList * onRedLookup; static LookList * onAmberLookup; static LookList * onGreenLookup; - // RouteCodeFar is a far pointer to RouteCode flash on anything other than a uno/nano where it is just a near pointer to flash - static uint32_t RouteCodeFar; // Local variables - exist for each instance/task RMFT2 *next; // loop chain diff --git a/RingStream.cpp b/RingStream.cpp index c63ebf7..9377a0a 100644 --- a/RingStream.cpp +++ b/RingStream.cpp @@ -65,6 +65,13 @@ int RingStream::availableForWrite() { } size_t RingStream::printFlash(const FSH * flashBuffer) { + // This function does not work on a 32 bit processor where the runtime + // sometimes misrepresents the pointer size in uintptr_t. + // In any case its not really necessary in a 32 bit processor because + // we have adequate ram. + if (sizeof(void*)>2) return print(flashBuffer); + + // We are about to add a PROGMEM string to the buffer. // To save RAM we can insert a marker and the // progmem address into the buffer instead. @@ -107,8 +114,11 @@ int RingStream::read() { if ((_pos_read==_pos_write) && !_overflow) return -1; // empty byte b=readRawByte(); if (b!=FLASH_INSERT_MARKER) return b; -#ifndef ARDUINO_ARCH_ESP32 // Detected a flash insert + if (sizeof(void*)>2) { + DIAG(F("Detected invalid flash insert marker at pos %d"),_pos_read); + return '?'; + } // read address bytes LSB first (size depends on CPU) uintptr_t iFlash=0; for (byte f=0; f( iFlash); // and try again... so will read the first byte of the insert. return read(); -#else - DIAG(F("Detected flash insert marker at pos %d but there should not be one"),_pos_read); - return '\0'; -#endif } byte RingStream::readRawByte() { diff --git a/RingStream.h b/RingStream.h index 4f51a65..b477b77 100644 --- a/RingStream.h +++ b/RingStream.h @@ -27,7 +27,7 @@ class RingStream : public Print { public: RingStream( const uint16_t len); - static const int THIS_IS_A_RINGSTREAM=77; + static const int THIS_IS_A_RINGSTREAM=777; virtual size_t write(uint8_t b); // This availableForWrite function is subverted from its original intention so that a caller diff --git a/SerialManager.cpp b/SerialManager.cpp index 0ac567d..84e4ad6 100644 --- a/SerialManager.cpp +++ b/SerialManager.cpp @@ -102,7 +102,9 @@ void SerialManager::loop() { void SerialManager::loop2() { while (serial->available()) { - char ch = serial->read(); + int intch=serial->read(); + if (intch<0) break; + char ch = (char)intch; if (ch == '<') { inCommandPayload = true; bufferLength = 0; diff --git a/StringFormatter.cpp b/StringFormatter.cpp index 152e6e7..cc78714 100644 --- a/StringFormatter.cpp +++ b/StringFormatter.cpp @@ -91,9 +91,6 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) { { const FSH* flash= (const FSH*)va_arg(args, char*); -#ifndef ARDUINO_ARCH_ESP32 - // On ESP32 the reading flashstring from rinstream code - // crashes, so don't use the flashstream hack on ESP32 #if WIFI_ON | ETHERNET_ON // RingStream has special logic to handle flash strings // but is not implemented unless wifi or ethernet are enabled. @@ -101,11 +98,11 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) { if (stream->availableForWrite()==RingStream::THIS_IS_A_RINGSTREAM) ((RingStream *)stream)->printFlash(flash); else -#endif #endif stream->print(flash); break; } + case 'P': stream->print((uint32_t)va_arg(args, void*), HEX); break; case 'd': printPadded(stream,va_arg(args, int), formatWidth, formatLeft); break; case 'u': printPadded(stream,va_arg(args, unsigned int), formatWidth, formatLeft); break; case 'l': printPadded(stream,va_arg(args, long), formatWidth, formatLeft); break; @@ -169,7 +166,7 @@ void StringFormatter::printEscape(Print * stream, char c) { case '\0': stream->print(F("\\0")); return; case '\t': stream->print(F("\\t")); break; case '\\': stream->print(F("\\\\")); break; - default: stream->print(c); + default: stream->write(c); } } diff --git a/WiThrottle.cpp b/WiThrottle.cpp index cba1a4f..4976870 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -63,69 +63,6 @@ WiThrottle * WiThrottle::firstThrottle=NULL; -static uint8_t xstrncmp(const char *s1, const char *s2, uint8_t n) { - if (n == 0) - return 0; - do { - if (*s1 != *s2++) - return 1; - if (*s1++ == 0) - break; - } while (--n != 0); - return 0; -} - -void WiThrottle::findUniqThrottle(int id, char *u) { - WiThrottle *wtmyid = NULL; - WiThrottle *wtmyuniq = NULL; - - // search 1, look for clientid match - for (WiThrottle* wt=firstThrottle; wt!=NULL ; wt=wt->nextThrottle){ - if (wt->clientid == id) { - if (xstrncmp(u, wt->uniq, 16) == 0) // should be most common case - return; - wtmyid = wt; - break; - } - } - // search 2, look for string match - for (WiThrottle* wt=firstThrottle; wt!=NULL ; wt=wt->nextThrottle){ - if (xstrncmp(u, wt->uniq, 16) == 0) { - wtmyuniq = wt; - break; - } - } - - // analyse result of the two for loops: - if (wtmyid == NULL) { // should not happen - DIAG(F("Did not find my own wiThrottle handle")); - return; - } - // wtmyuniq == wtmyid has already returned in for loop 1 - if (wtmyuniq == NULL) { // register uniq in the found id - strncpy(wtmyid->uniq, u, 16); - wtmyid->uniq[16] = '\0'; - if (Diag::WITHROTTLE) DIAG(F("Client %d registered as %s"),wtmyid->clientid, wtmyid->uniq); - return; - } - // if we get here wtmyid and wtmyuniq point on objects but differnet ones - // so we need to do the copy (all other options covered above) - for(int n=0; n < MAX_MY_LOCO; n++) - wtmyid->myLocos[n] = wtmyuniq->myLocos[n]; - wtmyid->heartBeatEnable = wtmyuniq->heartBeatEnable; - wtmyid->heartBeat = wtmyuniq->heartBeat; - wtmyid->initSent = wtmyuniq->initSent; - wtmyid->exRailSent = wtmyuniq->exRailSent; - wtmyid->mostRecentCab = wtmyuniq->mostRecentCab; - wtmyid->turnoutListHash = wtmyuniq->turnoutListHash; - wtmyid->lastPowerState = wtmyuniq->lastPowerState; - strncpy(wtmyid->uniq, u, 16); - wtmyid->uniq[16] = '\0'; - if (Diag::WITHROTTLE) - DIAG(F("New client %d replaces old client %d as %s"), wtmyid->clientid, wtmyuniq->clientid, wtmyid->uniq); - forget(wtmyuniq->clientid); // do not use wtmyid after this -} - WiThrottle* WiThrottle::getThrottle( int wifiClient) { for (WiThrottle* wt=firstThrottle; wt!=NULL ; wt=wt->nextThrottle) if (wt->clientid==wifiClient) return wt; @@ -135,6 +72,7 @@ WiThrottle* WiThrottle::getThrottle( int wifiClient) { void WiThrottle::forget( byte clientId) { for (WiThrottle* wt=firstThrottle; wt!=NULL ; wt=wt->nextThrottle) if (wt->clientid==clientId) { + DIAG(F("Withrottle client %d dropped"),clientId); delete wt; break; } @@ -159,10 +97,7 @@ WiThrottle::WiThrottle( int wificlientid) { nextThrottle=firstThrottle; firstThrottle= this; clientid=wificlientid; - initSent=false; // prevent sending heartbeats before connection completed heartBeatEnable=false; // until client turns it on - turnoutListHash = -1; // make sure turnout list is sent once - exRailSent=false; mostRecentCab=0; for (int loco=0;loconext()){ - if (tt->isHidden()) continue; - int id=tt->getId(); - const FSH * tdesc=NULL; - #ifdef EXRAIL_ACTIVE - tdesc=RMFT2::getTurnoutDescription(id); - #endif - char tchar=Turnout::isClosed(id)?'2':'4'; - if (tdesc==NULL) // turnout with no description - StringFormatter::send(stream,F("]\\[%d}|{T%d}|{T%c"), id,id,tchar); - else - StringFormatter::send(stream,F("]\\[%d}|{%S}|{%c"), id,tdesc,tchar); - } - StringFormatter::send(stream,F("\n")); - turnoutListHash = Turnout::turnoutlistHash; // keep a copy of hash for later comparison - } - - else if (!exRailSent) { - // Send EX-RAIL routes list if not already sent (but not at same time as turnouts above) - exRailSent=true; -#ifdef EXRAIL_ACTIVE - StringFormatter::send(stream,F("PRT]\\[Routes}|{Route]\\[Set}|{2]\\[Handoff}|{4\nPRL")); - // first pass automations - for (int ix=0;;ix+=2) { - int16_t id =GETHIGHFLASHW(RMFT2::automationIdList,ix); - if (id==0) break; - const FSH * desc=RMFT2::getRouteDescription(id); - StringFormatter::send(stream,F("]\\[A%d}|{%S}|{4"),id,desc); - } - // second pass routes. - for (int ix=0;;ix+=2) { - int16_t id=GETHIGHFLASHW(RMFT2::routeIdList,ix); - if (id==0) break; - const FSH * desc=RMFT2::getRouteDescription(id); - StringFormatter::send(stream,F("]\\[R%d}|{%S}|{2"),id,desc); - } - StringFormatter::send(stream,F("\n")); -#endif + + // On first few commands, send turnout, roster and routes + + if (!turnoutsSent) sendTurnouts(stream); + else if(!rosterSent) sendRoster(stream); + else if (!routesSent) sendRoutes(stream); + else if (!heartrateSent) { + heartrateSent=true; // allow heartbeat to slow down once all metadata sent StringFormatter::send(stream,F("*%d\n"),HEARTBEAT_SECONDS); - } } + while (cmd[0]) { switch (cmd[0]) { case '*': // heartbeat control @@ -287,32 +187,20 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { } break; case 'N': // Heartbeat (2), only send if connection completed by 'HU' message - StringFormatter::send(stream, F("*%d\n"), initSent ? HEARTBEAT_SECONDS : HEARTBEAT_SECONDS/2); // return timeout value + StringFormatter::send(stream, F("*%d\n"), heartrateSent ? HEARTBEAT_SECONDS : HEARTBEAT_SECONDS/2); // return timeout value break; case 'M': // multithrottle multithrottle(stream, cmd); break; case 'H': // send initial connection info after receiving "HU" message - if (cmd[1] == 'U') { - WiThrottle::findUniqThrottle(clientid, (char *)cmd+2); - StringFormatter::send(stream,F("VN2.0\nHTDCC-EX\nRL0\n")); + if (cmd[1] == 'U') { + 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("PTT]\\[Turnouts}|{Turnout]\\[THROW}|{2]\\[CLOSE}|{4\n")); - StringFormatter::send(stream,F("PPA%x\n"),TrackManager::getMainPower()==POWERMODE::ON); -#ifdef EXRAIL_ACTIVE - StringFormatter::send(stream,F("RL%d"), RMFT2::rosterNameCount); - for (int16_t r=0;rwrite('\n'); // end roster -#endif - - + StringFormatter::send(stream,F("PPA%x\n"),TrackManager::getMainPower()==POWERMODE::ON); // set heartbeat to 5 seconds because we need to sync the metadata (1 second is too short!) StringFormatter::send(stream,F("*%d\n"), HEARTBEAT_SECONDS/2); - initSent = true; + } break; case 'Q': // @@ -321,7 +209,7 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { 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("WiThrottle(%d) Quit"),clientid); delete this; break; } @@ -382,65 +270,17 @@ void WiThrottle::multithrottle(RingStream * stream, byte * cmd){ } //use first empty "slot" on this client's list, will be added to DCC registration list for (int loco=0;loco\n"), throttleChar, cmd[3] ,locoid); //tell client to add loco - int fkeys=29; - myLocos[loco].functionToggles=1<<2; // F2 (HORN) is a non-toggle - -#ifdef EXRAIL_ACTIVE - const char * functionNames=(char *) RMFT2::getRosterFunctions(locoid); - if (!functionNames) { - // no roster, use presets as above - } - else if (GETFLASH(functionNames)=='\0') { - // "" = Roster but no functions given - fkeys=0; - } - else { - // we have function names... - // scan names list emitting names, counting functions and - // flagging non-toggling things like horn. - myLocos[loco].functionToggles =0; - StringFormatter::send(stream, F("M%cL%c%d<;>]\\["), throttleChar,cmd[3],locoid); - fkeys=0; - bool firstchar=true; - for (int fx=0;;fx++) { - char c=GETFLASH(functionNames+fx); - if (c=='\0') { - fkeys++; - break; - } - if (c=='/') { - fkeys++; - StringFormatter::send(stream,F("]\\[")); - firstchar=true; - } - else if (firstchar && c=='*') { - myLocos[loco].functionToggles |= 1UL<write(c); - } - } - StringFormatter::send(stream,F("\n")); - } - -#endif - - for(int fKey=0; fKey=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 - StringFormatter::send(stream, F("M%cA%c%d<;>s1\n"), throttleChar, cmd[3], locoid); //default speed step 128 - return; + if (myLocos[loco].throttle=='\0' || myLocos[loco].cab == locoid) { + myLocos[loco].throttle=throttleChar; + myLocos[loco].cab=locoid; + myLocos[loco].functionMap=DCC::getFunctionMap(locoid); + myLocos[loco].broadcastPending=true; // means speed/dir will be sent later + mostRecentCab=locoid; + StringFormatter::send(stream, F("M%c+%c%d<;>\n"), throttleChar, cmd[3] ,locoid); //tell client to add loco + sendFunctions(stream,loco); + //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 + return; } } StringFormatter::send(stream, F("HMMax locos (%d) exceeded, %d not added!\n"), MAX_MY_LOCO ,locoid); @@ -544,8 +384,6 @@ void WiThrottle::loop(RingStream * stream) { // for each WiThrottle, check the heartbeat and broadcast needed for (WiThrottle* wt=firstThrottle; wt!=NULL ; wt=wt->nextThrottle) wt->checkHeartbeat(stream); - - } void WiThrottle::checkHeartbeat(RingStream * stream) { @@ -559,8 +397,8 @@ void WiThrottle::checkHeartbeat(RingStream * stream) { heartBeat=millis(); // We have just stopped everyting, we don't need to do that again at next loop. } } - //haba no, not necessary the only throttle and it may come back - //delete this; + // if it does come back, the throttle should re-acquire + delete this; return; } @@ -660,5 +498,110 @@ void WiThrottle::getLocoCallback(int16_t locoid) { DIAG(F("LocoCallback commit success")); stashStream->commit(); CommandDistributor::broadcastPower(); - } + +void WiThrottle::sendTurnouts(Print* stream) { + turnoutsSent=true; + StringFormatter::send(stream,F("PTL")); + for(Turnout *tt=Turnout::first();tt!=NULL;tt=tt->next()){ + if (tt->isHidden()) continue; + int id=tt->getId(); + const FSH * tdesc=NULL; + #ifdef EXRAIL_ACTIVE + tdesc=RMFT2::getTurnoutDescription(id); + #endif + char tchar=Turnout::isClosed(id)?'2':'4'; + if (tdesc==NULL) // turnout with no description + StringFormatter::send(stream,F("]\\[%d}|{T%d}|{T%c"), id,id,tchar); + else + StringFormatter::send(stream,F("]\\[%d}|{%S}|{%c"), id,tdesc,tchar); + } + StringFormatter::send(stream,F("\n")); +} +void WiThrottle::sendRoster(Print* stream) { + rosterSent=true; + #ifdef EXRAIL_ACTIVE + StringFormatter::send(stream,F("RL%d"), RMFT2::rosterNameCount); + for (int16_t r=0;r]\\["), myLocos[loco].throttle,LorS(locoid),locoid); + fkeys=0; + bool firstchar=true; + for (int fx=0;;fx++) { + char c=GETFLASH(functionNames+fx); + if (c=='\0') { + fkeys++; + break; + } + if (c=='/') { + fkeys++; + StringFormatter::send(stream,F("]\\[")); + firstchar=true; + } + else if (firstchar && c=='*') { + myLocos[loco].functionToggles |= 1UL<write(c); + } + } + StringFormatter::send(stream,F("\n")); + } + +#endif + + for(int fKey=0; fKey=0) StringFormatter::send(stream,F("M%cA%c%d<;>F%d%d\n"),myLocos[loco].throttle,LorS(locoid),locoid,fstate,fKey); + } +} \ No newline at end of file diff --git a/WiThrottle.h b/WiThrottle.h index 3fa973a..5481cbe 100644 --- a/WiThrottle.h +++ b/WiThrottle.h @@ -61,10 +61,11 @@ class WiThrottle { MYLOCO myLocos[MAX_MY_LOCO]; bool heartBeatEnable; unsigned long heartBeat; - bool initSent; // valid connection established - bool exRailSent; // valid connection established + bool turnoutsSent=false; + bool rosterSent=false; + bool routesSent=false; + bool heartrateSent=false; uint16_t mostRecentCab; - int turnoutListHash; // used to check for changes to turnout list bool lastPowerState; // last power state sent to this client int DCCToWiTSpeed(int DCCSpeed); @@ -74,6 +75,10 @@ class WiThrottle { void accessory(RingStream *, byte* cmd); void checkHeartbeat(RingStream * stream); void markForBroadcast2(int cab); + void sendTurnouts(Print * stream); + void sendRoster(Print * stream); + void sendRoutes(Print * stream); + void sendFunctions(Print* stream, byte loco); // callback stuff to support prog track acquire static RingStream * stashStream; static WiThrottle * stashInstance; diff --git a/WifiInboundHandler.cpp b/WifiInboundHandler.cpp index 0b13bb5..ce8d3cf 100644 --- a/WifiInboundHandler.cpp +++ b/WifiInboundHandler.cpp @@ -31,6 +31,7 @@ WifiInboundHandler * WifiInboundHandler::singleton; void WifiInboundHandler::setup(Stream * ESStream) { singleton=new WifiInboundHandler(ESStream); + // DIAG(F("WifiInbound Setup2 %P %P"), ESStream,singleton); } void WifiInboundHandler::loop() { @@ -44,6 +45,7 @@ WifiInboundHandler::WifiInboundHandler(Stream * ESStream) { inboundRing=new RingStream(INBOUND_RING); outboundRing=new RingStream(OUTBOUND_RING); pendingCipsend=false; + // DIAG(F("WifiInbound setup1 %P"), wifiStream); } @@ -51,11 +53,19 @@ WifiInboundHandler::WifiInboundHandler(Stream * ESStream) { // +IPD,x,lll:data is stored in streamer[x] // Other input returns void WifiInboundHandler::loop1() { + static bool XX=true; + if (XX) DIAG(F("Wifi 1")); + // First handle all inbound traffic events because they will block the sending - if (loop2()!=INBOUND_IDLE) return; + if (loop2()!=INBOUND_IDLE) { + if (XX) DIAG(F("Wifi 2")); + return; + } +if (XX) DIAG(F("Wifi 3")); WiThrottle::loop(outboundRing); - + if (XX) DIAG(F("Wifi 4")); + XX=false; // if nothing is already CIPSEND pending, we can CIPSEND one reply if (clientPendingCIPSEND<0) { clientPendingCIPSEND=outboundRing->read(); @@ -66,14 +76,13 @@ void WifiInboundHandler::loop1() { } - if (pendingCipsend) { + if (pendingCipsend && millis()-lastCIPSEND > CIPSENDgap) { if (Diag::WIFI) DIAG( F("WiFi: [[CIPSEND=%d,%d]]"), clientPendingCIPSEND, currentReplySize); StringFormatter::send(wifiStream, F("AT+CIPSEND=%d,%d\r\n"), clientPendingCIPSEND, currentReplySize); pendingCipsend=false; return; } - // if something waiting to execute, we can call it int clientId=inboundRing->read(); if (clientId>=0) { @@ -83,7 +92,6 @@ void WifiInboundHandler::loop1() { for (int i=0;iread(); cmd[count]=0; if (Diag::WIFI) DIAG(F("%e"),cmd); - CommandDistributor::parse(clientId,cmd,outboundRing); return; } @@ -91,16 +99,17 @@ void WifiInboundHandler::loop1() { + // This is a Finite State Automation (FSA) handling the inbound bytes from an ES AT command processor WifiInboundHandler::INBOUND_STATE WifiInboundHandler::loop2() { - while (wifiStream->available()) { - char ch = wifiStream->read(); - + while (wifiStream->available()>=0) { + int chint = wifiStream->read(); + if (chint<0) break; + byte ch=(char)chint; // echo the char to the diagnostic stream in escaped format if (Diag::WIFI) { - // DIAG(F(" %d/"), loopState); - StringFormatter::printEscape(ch); // DIAG in disguise + StringFormatter::printEscape((char)ch); // DIAG in disguise } switch (loopState) { @@ -131,11 +140,13 @@ WifiInboundHandler::INBOUND_STATE WifiInboundHandler::loop2() { if (ch=='S') { // SEND OK probably loopState=SKIPTOEND; + lastCIPSEND=0; // no need to wait next time break; } if (ch=='b') { // This is a busy indicator... probabaly must restart a CIPSEND pendingCipsend=(clientPendingCIPSEND>=0); + if (pendingCipsend) lastCIPSEND=millis(); // forces a gap to next CIPSEND loopState=SKIPTOEND; break; } diff --git a/WifiInboundHandler.h b/WifiInboundHandler.h index d3410cd..85cc449 100644 --- a/WifiInboundHandler.h +++ b/WifiInboundHandler.h @@ -69,6 +69,7 @@ class WifiInboundHandler { static const int INBOUND_RING = 512; static const int OUTBOUND_RING = 2048; + static const int CIPSENDgap=100; // millis() between retries of cipsend. RingStream * inboundRing; RingStream * outboundRing; @@ -79,5 +80,7 @@ class WifiInboundHandler { int clientPendingCIPSEND=-1; int currentReplySize; bool pendingCipsend; + uint32_t lastCIPSEND=0; // millis() of previous cipsend + }; #endif