mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-22 23:56:13 +01:00
Apparently working Nucleo
This commit is contained in:
parent
5a2b008367
commit
9cdabb0acf
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
28
EXRAIL2.cpp
28
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<sizeof(iFlash); f++) {
|
||||
|
@ -120,10 +130,6 @@ int RingStream::read() {
|
|||
_flashInsert=reinterpret_cast<char * >( 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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
327
WiThrottle.cpp
327
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;loco<MAX_MY_LOCO; loco++) myLocos[loco].throttle='\0';
|
||||
}
|
||||
|
@ -187,54 +122,19 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
|
|||
|
||||
heartBeat=millis();
|
||||
if (Diag::WITHROTTLE) DIAG(F("%l WiThrottle(%d)<-[%e]"),millis(),clientid,cmd);
|
||||
|
||||
if (initSent) {
|
||||
// Send turnout list if changed since last sent (will replace list on client)
|
||||
if (turnoutListHash != Turnout::turnoutlistHash) {
|
||||
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"));
|
||||
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;r<RMFT2::rosterNameCount;r++) {
|
||||
int16_t cabid=GETHIGHFLASHW(RMFT2::rosterIdList,r*2);
|
||||
StringFormatter::send(stream,F("]\\[%S}|{%d}|{%c"),
|
||||
RMFT2::getRosterName(cabid),cabid,cabid<128?'S':'L');
|
||||
}
|
||||
stream->write('\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<MAX_MY_LOCO;loco++) {
|
||||
if (myLocos[loco].throttle=='\0') {
|
||||
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
|
||||
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<<fkeys;
|
||||
firstchar=false;
|
||||
}
|
||||
else {
|
||||
firstchar=false;
|
||||
stream->write(c);
|
||||
}
|
||||
}
|
||||
StringFormatter::send(stream,F("\n"));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
for(int fKey=0; fKey<fkeys; 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);
|
||||
}
|
||||
//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<RMFT2::rosterNameCount;r++) {
|
||||
int16_t cabid=GETHIGHFLASHW(RMFT2::rosterIdList,r*2);
|
||||
StringFormatter::send(stream,F("]\\[%S}|{%d}|{%c"),
|
||||
RMFT2::getRosterName(cabid),cabid,cabid<128?'S':'L');
|
||||
}
|
||||
StringFormatter::send(stream,F("\n"));
|
||||
#endif
|
||||
}
|
||||
void WiThrottle::sendRoutes(Print* stream) {
|
||||
routesSent=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
|
||||
}
|
||||
|
||||
void WiThrottle::sendFunctions(Print* stream, byte loco) {
|
||||
int16_t locoid=myLocos[loco].cab;
|
||||
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 non-exrail 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<;>]\\["), 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<<fkeys;
|
||||
firstchar=false;
|
||||
}
|
||||
else {
|
||||
firstchar=false;
|
||||
stream->write(c);
|
||||
}
|
||||
}
|
||||
StringFormatter::send(stream,F("\n"));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
for(int fKey=0; fKey<fkeys; fKey++) {
|
||||
int fstate=DCC::getFn(locoid,fKey);
|
||||
if (fstate>=0) StringFormatter::send(stream,F("M%cA%c%d<;>F%d%d\n"),myLocos[loco].throttle,LorS(locoid),locoid,fstate,fKey);
|
||||
}
|
||||
}
|
11
WiThrottle.h
11
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;
|
||||
|
|
|
@ -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;i<count;i++) cmd[i]=inboundRing->read();
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user