diff --git a/.gitattributes b/.gitattributes index dfe0770..09d8971 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ # Auto detect text files and perform LF normalization * text=auto +*.svg -text diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index d16b21e..3d3bed6 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -49,6 +49,8 @@ const int HASH_KEYWORD_PROGBOOST = -6353; const int HASH_KEYWORD_EEPROM = -7168; const int HASH_KEYWORD_LIMIT = 27413; const int HASH_KEYWORD_ETHERNET = -30767; +const int HASH_KEYWORD_MAX = 16244; +const int HASH_KEYWORD_MIN = 15978; int DCCEXParser::stashP[MAX_PARAMS]; bool DCCEXParser::stashBusy; @@ -477,6 +479,8 @@ void DCCEXParser::parse(Print *stream, byte *com, bool blocking) case '+': // Complex Wifi interface command (not usual parse) if (atCommandCallback) { + DCCWaveform::mainTrack.setPowerMode(POWERMODE::OFF); + DCCWaveform::progTrack.setPowerMode(POWERMODE::OFF); atCommandCallback(com); return; } @@ -669,12 +673,22 @@ bool DCCEXParser::parseD(Print *stream, int params, int p[]) StringFormatter::send(stream, F("\nFree memory=%d\n"), freeMemory()); break; - case HASH_KEYWORD_ACK: // - if (params >= 2 && p[1] == HASH_KEYWORD_LIMIT) { - DCCWaveform::progTrack.setAckLimit(p[2]); - StringFormatter::send(stream, F("\nAck limit=%dmA\n"), p[2]); - } else + case HASH_KEYWORD_ACK: // + if (params >= 3) { + if (p[1] == HASH_KEYWORD_LIMIT) { + DCCWaveform::progTrack.setAckLimit(p[2]); + StringFormatter::send(stream, F("\nAck limit=%dmA\n"), p[2]); + } else if (p[1] == HASH_KEYWORD_MIN) { + DCCWaveform::progTrack.setMinAckPulseDuration(p[2]); + StringFormatter::send(stream, F("\nAck min=%dus\n"), p[2]); + } else if (p[1] == HASH_KEYWORD_MAX) { + DCCWaveform::progTrack.setMaxAckPulseDuration(p[2]); + StringFormatter::send(stream, F("\nAck max=%dus\n"), p[2]); + } + } else { + StringFormatter::send(stream, F("\nAck diag %S\n"), onOff ? F("on") : F("off")); Diag::ACK = onOff; + } return true; case HASH_KEYWORD_CMD: // @@ -701,8 +715,8 @@ bool DCCEXParser::parseD(Print *stream, int params, int p[]) DCC::setProgTrackBoost(true); return true; - case HASH_KEYWORD_EEPROM: - if (params >= 1) + case HASH_KEYWORD_EEPROM: // + if (params >= 2) EEStore::dump(p[1]); return true; diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index bec16b3..123ba9e 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -295,9 +295,10 @@ void DCCWaveform::setAckBaseline() { if (isMainTrack) return; int baseline = motorDriver->getCurrentRaw(); ackThreshold= baseline + motorDriver->mA2raw(ackLimitmA); - if (Diag::ACK) DIAG(F("\nACK baseline=%d/%dmA threshold=%d/%dmA"), + if (Diag::ACK) DIAG(F("\nACK baseline=%d/%dmA Threshold=%d/%dmA Duration: %dus <= pulse <= %dus"), baseline,motorDriver->raw2mA(baseline), - ackThreshold,motorDriver->raw2mA(ackThreshold)); + ackThreshold,motorDriver->raw2mA(ackThreshold), + minAckPulseDuration, maxAckPulseDuration); } void DCCWaveform::setAckPending() { @@ -312,7 +313,7 @@ void DCCWaveform::setAckPending() { byte DCCWaveform::getAck() { if (ackPending) return (2); // still waiting - if (Diag::ACK) DIAG(F("\nACK-%S after %dmS max=%d/%dmA pulse=%duS"),ackDetected?F("OK"):F("FAIL"), ackCheckDuration, + if (Diag::ACK) DIAG(F("\n%S after %dmS max=%d/%dmA pulse=%duS"),ackDetected?F("ACK"):F("NO-ACK"), ackCheckDuration, ackMaxCurrent,motorDriver->raw2mA(ackMaxCurrent), ackPulseDuration); if (ackDetected) return (1); // Yes we had an ack return(0); // pending set off but not detected means no ACK. @@ -329,7 +330,7 @@ void DCCWaveform::checkAck() { lastCurrent=motorDriver->getCurrentRaw(); if (lastCurrent > ackMaxCurrent) ackMaxCurrent=lastCurrent; - // An ACK is a pulse lasting between MIN_ACK_PULSE_DURATION and MAX_ACK_PULSE_DURATION uSecs (refer @haba) + // An ACK is a pulse lasting between minAckPulseDuration and maxAckPulseDuration uSecs (refer @haba) if (lastCurrent>ackThreshold) { if (ackPulseStart==0) ackPulseStart=micros(); // leading edge of pulse detected @@ -342,7 +343,7 @@ void DCCWaveform::checkAck() { // detected trailing edge of pulse ackPulseDuration=micros()-ackPulseStart; - if (ackPulseDuration>=MIN_ACK_PULSE_DURATION && ackPulseDuration<=MAX_ACK_PULSE_DURATION) { + if (ackPulseDuration>=minAckPulseDuration && ackPulseDuration<=maxAckPulseDuration) { ackCheckDuration=millis()-ackCheckStart; ackDetected=true; ackPending=false; diff --git a/DCCWaveform.h b/DCCWaveform.h index 32041e6..c658577 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -27,10 +27,6 @@ const int POWER_SAMPLE_ON_WAIT = 100; const int POWER_SAMPLE_OFF_WAIT = 1000; const int POWER_SAMPLE_OVERLOAD_WAIT = 20; -// Ack time thresholds. Unit: microseconds -const int MIN_ACK_PULSE_DURATION = 2000; -const int MAX_ACK_PULSE_DURATION = 8500; - // Number of preamble bits. const int PREAMBLE_BITS_MAIN = 16; const int PREAMBLE_BITS_PROG = 22; @@ -79,7 +75,13 @@ class DCCWaveform { inline void setAckLimit(int mA) { ackLimitmA = mA; } - + inline void setMinAckPulseDuration(unsigned int i) { + minAckPulseDuration = i; + } + inline void setMaxAckPulseDuration(unsigned int i) { + maxAckPulseDuration = i; + } + private: static VirtualTimer * interruptTimer; static void interruptHandler(); @@ -128,6 +130,9 @@ class DCCWaveform { unsigned int ackPulseDuration; // micros unsigned long ackPulseStart; // micros + + unsigned int minAckPulseDuration = 2000; // micros + unsigned int maxAckPulseDuration = 8500; // micros }; #endif diff --git a/Release - Architecture Doc/Prod-Release-Notes.md b/Release - Architecture Doc/Prod-Release-Notes.md index 526c3b7..2b95877 100644 --- a/Release - Architecture Doc/Prod-Release-Notes.md +++ b/Release - Architecture Doc/Prod-Release-Notes.md @@ -31,41 +31,41 @@ The DCC-EX Team is pleased to release CommandStation-EX-v3.0.0 as a Production R **Key Contributors** **Project Lead** - Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) +- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) **CommandStation-EX Developers** - Chris Harlow - Bournemouth, UK (UKBloke) - Harald Barth - Stockholm, Sweden (Haba) - Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) - Dave Cutting - Logan, Utah, USA (Dave Cutting/ David Cutting) - M Steve Todd - - Engine Driver and JMRI Interface - Scott Catalanno - Pennsylvania - Gregor Baues - Île-de-France, France (grbba) +- Chris Harlow - Bournemouth, UK (UKBloke) +- Harald Barth - Stockholm, Sweden (Haba) +- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) +- Dave Cutting - Logan, Utah, USA (Dave Cutting/ David Cutting) +- M Steve Todd - - Engine Driver and JMRI Interface +- Scott Catalanno - Pennsylvania +- Gregor Baues - Île-de-France, France (grbba) **exInstaller Software** - Anthony W - Dayton, Ohio, USA (Dex, Dex++) +- Anthony W - Dayton, Ohio, USA (Dex, Dex++) **Website and Documentation** - Mani Kumar - Bangalor, India (Mani / Mani Kumar) - Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) - Dave Cutting - Logan, Utah, USA (Dave Cutting/ David Cutting) - Roger Beschizza - Dorset, UK (Roger Beschizza) - Keith Ledbetter - Chicago, Illinois, USA (Keith Ledbetter) - Kevin Smith - (KCSmith) +- Mani Kumar - Bangalor, India (Mani / Mani Kumar) +- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) +- Dave Cutting - Logan, Utah, USA (Dave Cutting/ David Cutting) +- Roger Beschizza - Dorset, UK (Roger Beschizza) +- Keith Ledbetter - Chicago, Illinois, USA (Keith Ledbetter) +-Kevin Smith - (KCSmith) **Beta Testing / Release Management / Support** - Larry Dribin larry@dribin.org - Release Management - Keith Ledbetter kwledbetter@hotmail.com - BradVan der Elst brad.vanderelst@gci.net - Andrew Pye andrew@ajpye.org - Mike Bowers springlake1925@gmail.com - Randy McKenzie kk4ej@yahoo.com - Roberto Bravin roberto.bravin@gmail.com - Sim Brigden sim.brigden@gmail.com - Alan Lautenslager alan.lautenslager@gmail.com - Martin Bafver martin.bafver@gmail.com - Mário André Silva andre_bb@live.com.pt - Anthony Kochevar ajkochevar@comcast.net - Gajanatha Kobbekaduwe gajanatha@gmail.com - Sumner Patterson - Blanding, Utah, USA (Sumner) - Paul - Virginia, USA (Paul1361) +- Larry Dribin - Release Management +- Keith Ledbetter +- BradVan der Elst +- Andrew Pye +- Mike Bowers +- Randy McKenzie +- Roberto Bravin +- Sim Brigden +- Alan Lautenslager +- Martin Bafver +- Mário André Silva +- Anthony Kochevar +- Gajanatha Kobbekaduwe +- Sumner Patterson +- Paul - Virginia, USA diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 8020d85..45531fb 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -34,6 +34,10 @@ const unsigned long LOOP_TIMEOUT = 2000; bool WifiInterface::connected = false; Stream * WifiInterface::wifiStream; +#ifndef WIFI_CONNECT_TIMEOUT +// Tested how long it takes to FAIL an unknown SSID on firmware 1.7.4. +#define WIFI_CONNECT_TIMEOUT 14000 +#endif //////////////////////////////////////////////////////////////////////////////// // @@ -170,15 +174,16 @@ wifiSerialState WifiInterface::setup2(const __FlashStringHelper* SSid, const __F // If the source code looks unconfigured, check if the // ESP8266 is preconfigured. We check the first 13 chars - // of the password. - if (strncmp_P("Your network ",(const char*)password,13) == 0) { + // of the SSid. + const char *yourNetwork = "Your network "; + if (strncmp_P(yourNetwork, (const char*)SSid, 13) == 0 || ((const char *)SSid)[0] == '\0') { delay(8000); // give a preconfigured ES8266 a chance to connect to a router - + // typical connect time approx 7 seconds StringFormatter::send(wifiStream, F("AT+CIFSR\r\n")); if (checkForOK(5000, (const char*) F("+CIFSR:STAIP"), true,false)) if (!checkForOK(1000, (const char*) F("0.0.0.0"), true,false)) ipOK = true; - } else { + } else { // Should this really be "else" here /haba if (!ipOK) { @@ -191,7 +196,7 @@ wifiSerialState WifiInterface::setup2(const __FlashStringHelper* SSid, const __F // AT command early version supports CWJAP/CWSAP if (SSid) { StringFormatter::send(wifiStream, F("AT+CWJAP=\"%S\",\"%S\"\r\n"), SSid, password); - ipOK = checkForOK(16000, OK_SEARCH, true); + ipOK = checkForOK(WIFI_CONNECT_TIMEOUT, OK_SEARCH, true); } DIAG(F("\n**\n")); @@ -203,10 +208,9 @@ wifiSerialState WifiInterface::setup2(const __FlashStringHelper* SSid, const __F if (SSid) { StringFormatter::send(wifiStream, F("AT+CWJAP_CUR=\"%S\",\"%S\"\r\n"), SSid, password); - ipOK = checkForOK(20000, OK_SEARCH, true); + ipOK = checkForOK(WIFI_CONNECT_TIMEOUT, OK_SEARCH, true); } } - delay(8000); // give a preconfigured ES8266 a chance to connect to a router if (ipOK) { // But we really only have the ESSID and password correct @@ -243,9 +247,15 @@ wifiSerialState WifiInterface::setup2(const __FlashStringHelper* SSid, const __F while (wifiStream->available()) StringFormatter::printEscape( wifiStream->read()); /// THIS IS A DIAG IN DISGUISE int i=0; - do StringFormatter::send(wifiStream, F("AT+CWSAP=\"DCCEX_%s\",\"PASS_%s\",1,4\r\n"), macTail, macTail); - while (i++<2 && !checkForOK(16000, OK_SEARCH, true)); // do twice if necessary but ignore failure as AP mode may still be ok - + do { + if (strncmp_P(yourNetwork, (const char*)password, 13) == 0) { + // unconfigured + StringFormatter::send(wifiStream, F("AT+CWSAP=\"DCCEX_%s\",\"PASS_%s\",1,4\r\n"), macTail, macTail); + } else { + // password configured by user + StringFormatter::send(wifiStream, F("AT+CWSAP=\"DCCEX_%s\",\"%s\",1,4\r\n"), macTail, password); + } + } while (i++<2 && !checkForOK(WIFI_CONNECT_TIMEOUT, OK_SEARCH, true)); // do twice if necessary but ignore failure as AP mode may still be ok } else { StringFormatter::send(wifiStream, F("AT+CWSAP_CUR=\"DCCEX_%s\",\"PASS_%s\",1,4\r\n"), macTail, macTail); @@ -257,17 +267,17 @@ wifiSerialState WifiInterface::setup2(const __FlashStringHelper* SSid, const __F } StringFormatter::send(wifiStream, F("AT+CIPSERVER=0\r\n")); // turn off tcp server (to clean connections before CIPMUX=1) - checkForOK(10000, OK_SEARCH, true); // ignore result in case it already was off + checkForOK(1000, OK_SEARCH, true); // ignore result in case it already was off StringFormatter::send(wifiStream, F("AT+CIPMUX=1\r\n")); // configure for multiple connections - if (!checkForOK(10000, OK_SEARCH, true)) return WIFI_DISCONNECTED; + if (!checkForOK(1000, OK_SEARCH, true)) return WIFI_DISCONNECTED; StringFormatter::send(wifiStream, F("AT+CIPSERVER=1,%d\r\n"), port); // turn on server on port - if (!checkForOK(10000, OK_SEARCH, true)) return WIFI_DISCONNECTED; + if (!checkForOK(1000, OK_SEARCH, true)) return WIFI_DISCONNECTED; #endif //DONT_TOUCH_WIFI_CONF StringFormatter::send(wifiStream, F("AT+CIFSR\r\n")); // Display ip addresses to the DIAG - if (!checkForOK(10000, OK_SEARCH, true, false)) return WIFI_DISCONNECTED; + if (!checkForOK(1000, OK_SEARCH, true, false)) return WIFI_DISCONNECTED; DIAG(F("\nPORT=%d\n"),port); return WIFI_CONNECTED; diff --git a/config.example.h b/config.example.h index 3ceb675..174e590 100644 --- a/config.example.h +++ b/config.example.h @@ -44,15 +44,37 @@ The configuration file for DCC++ EX Command Station // // DEFINE WiFi Parameters (only in effect if WIFI is on) // +// If DONT_TOUCH_WIFI_CONF is set, all WIFI config will be done with +// the <+> commands and this sketch will not change anything over +// AT commands and the other WIFI_* defines below do not have any effect. //#define DONT_TOUCH_WIFI_CONF // -// if DONT_TOUCH_WIFI_CONF is set, all WIFI config will be done with -// the <+> commands and this sketch will not change anything over -// AT commands and the WIFI_* defines below do not have any effect. +// WIFI_SSID is the network name IF you want to use your existing home network. +// Do NOT change this if you want to use the WiFi in Access Point (AP) mode. // +// If you do NOT set the WIFI_SSID, the WiFi chip will first try +// to connect to the previously configured network and if that fails +// fall back to Access Point mode. The SSID of the AP will be +// automatically set to DCCEX_*. +// +// Your SSID may not conain ``"'' (double quote, ASCII 0x22). #define WIFI_SSID "Your network name" +// +// WIFI_PASSWORD is the network password for your home network or if +// you want to change the password from default AP mode password +// to the AP password you want. +// Your password may not conain ``"'' (double quote, ASCII 0x22). #define WIFI_PASSWORD "Your network passwd" +// +// WIFI_HOSTNAME: You probably don't need to change this #define WIFI_HOSTNAME "dccex" +// +///////////////////////////////////////////////////////////////////////////////////// +// +// Wifi connect timeout in milliseconds. Default is 14000 (14 seconds). You only need +// to set this if you have an extremely slow Wifi router. +// +//#define WIFI_CONNECT_TIMEOUT 14000 ///////////////////////////////////////////////////////////////////////////////////// //