diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index cd6db16..026e763 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -109,7 +109,7 @@ void setup() LCN::init(LCN_SERIAL); #endif - LCD(1,F("Ready")); + LCD(3,F("Ready")); } void loop() @@ -149,6 +149,6 @@ void loop() if (freeNow < ramLowWatermark) { ramLowWatermark = freeNow; - LCD(2,F("Free RAM=%5db"), ramLowWatermark); + LCD(3,F("Free RAM=%5db"), ramLowWatermark); } } diff --git a/DCC.cpp b/DCC.cpp index dec6646..e3dd921 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -683,9 +683,13 @@ int DCC::nextLoco = 0; //ACK MANAGER ackOp const * DCC::ackManagerProg; +ackOp const * DCC::ackManagerProgStart; byte DCC::ackManagerByte; byte DCC::ackManagerStash; int DCC::ackManagerWord; +byte DCC::ackManagerRetry; +byte DCC::ackRetry = 2; +int16_t DCC::ackRetrySum; int DCC::ackManagerCv; byte DCC::ackManagerBitNum; bool DCC::ackReceived; @@ -718,6 +722,8 @@ void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[] ackManagerCv = cv; ackManagerProg = program; + ackManagerProgStart = program; + ackManagerRetry = ackRetry; ackManagerByte = byteValueOrBitnum; ackManagerBitNum=byteValueOrBitnum; ackManagerCallback = callback; @@ -901,6 +907,15 @@ void DCC::ackManagerLoop() { } void DCC::callback(int value) { + // check for automatic retry + if (value == -1 && ackManagerRetry > 0) { + ackRetrySum ++; + LCD(0, F("RETRY %d %d %d %d"), ackManagerCv, ackManagerRetry, ackRetry, ackRetrySum); + ackManagerRetry --; + ackManagerProg = ackManagerProgStart; + return; + } + static unsigned long callbackStart; // We are about to leave programming mode // Rule 1: If we have written to a decoder we must maintain power for 100mS diff --git a/DCC.h b/DCC.h index 1bdd5f0..cf1680f 100644 --- a/DCC.h +++ b/DCC.h @@ -64,8 +64,10 @@ enum CALLBACK_STATE : byte { // Allocations with memory implications..! // Base system takes approx 900 bytes + 8 per loco. Turnouts, Sensors etc are dynamically created -#ifdef ARDUINO_AVR_UNO +#if defined(ARDUINO_AVR_UNO) const byte MAX_LOCOS = 20; +#elif defined(ARDUINO_AVR_NANO) +const byte MAX_LOCOS = 30; #else const byte MAX_LOCOS = 50; #endif @@ -113,6 +115,10 @@ public: static inline void setGlobalSpeedsteps(byte s) { globalSpeedsteps = s; }; + static inline void setAckRetry(byte retry) { + ackRetry = retry; + ackRetrySum = 0; // reset running total + }; private: struct LOCO @@ -141,9 +147,13 @@ private: // ACK MANAGER static ackOp const *ackManagerProg; + static ackOp const *ackManagerProgStart; static byte ackManagerByte; static byte ackManagerBitNum; static int ackManagerCv; + static byte ackManagerRetry; + static byte ackRetry; + static int16_t ackRetrySum; static int ackManagerWord; static byte ackManagerStash; static bool ackReceived; diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index f44df2f..429e1dc 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -32,6 +32,16 @@ #include "DIAG.h" #include +//////////////////////////////////////////////////////////////////////////////// +// +// Figure out if we have enough memory for advanced features +// +#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) +// nope +#else +#define HAS_ENOUGH_MEMORY +#endif + // These keywords are used in the <1> command. The number is what you get if you use the keyword as a parameter. // To discover new keyword numbers , use the <$ YOURKEYWORD> command const int16_t HASH_KEYWORD_PROG = -29718; @@ -40,8 +50,6 @@ const int16_t HASH_KEYWORD_JOIN = -30750; const int16_t HASH_KEYWORD_CABS = -11981; const int16_t HASH_KEYWORD_RAM = 25982; const int16_t HASH_KEYWORD_CMD = 9962; -const int16_t HASH_KEYWORD_WIT = 31594; -const int16_t HASH_KEYWORD_WIFI = -5583; const int16_t HASH_KEYWORD_ACK = 3113; const int16_t HASH_KEYWORD_ON = 2657; const int16_t HASH_KEYWORD_DCC = 6436; @@ -49,17 +57,22 @@ const int16_t HASH_KEYWORD_SLOW = -17209; const int16_t HASH_KEYWORD_PROGBOOST = -6353; const int16_t HASH_KEYWORD_EEPROM = -7168; const int16_t HASH_KEYWORD_LIMIT = 27413; -const int16_t HASH_KEYWORD_ETHERNET = -30767; const int16_t HASH_KEYWORD_MAX = 16244; const int16_t HASH_KEYWORD_MIN = 15978; -const int16_t HASH_KEYWORD_LCN = 15137; const int16_t HASH_KEYWORD_RESET = 26133; +const int16_t HASH_KEYWORD_RETRY = 25704; const int16_t HASH_KEYWORD_SPEED28 = -17064; const int16_t HASH_KEYWORD_SPEED128 = 25816; const int16_t HASH_KEYWORD_SERVO=27709; const int16_t HASH_KEYWORD_VPIN=-415; const int16_t HASH_KEYWORD_C=67; const int16_t HASH_KEYWORD_T=84; +const int16_t HASH_KEYWORD_LCN = 15137; +#ifdef HAS_ENOUGH_MEMORY +const int16_t HASH_KEYWORD_WIFI = -5583; +const int16_t HASH_KEYWORD_ETHERNET = -30767; +const int16_t HASH_KEYWORD_WIT = 31594; +#endif int16_t DCCEXParser::stashP[MAX_COMMAND_PARAMS]; bool DCCEXParser::stashBusy; @@ -257,6 +270,7 @@ void DCCEXParser::parse(const FSH * cmd) { } // See documentation on DCC class for info on this section + void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) { (void)EEPROM; // tell compiler not to warn this is unused @@ -455,6 +469,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) if (mode == POWERMODE::OFF) DCC::setProgTrackBoost(false); // Prog track boost mode will not outlive prog track off StringFormatter::send(stream, F("\n"), opcode); + LCD(2, F("p%c"), opcode); return; } switch (p[0]) @@ -462,6 +477,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) case HASH_KEYWORD_MAIN: DCCWaveform::mainTrack.setPowerMode(mode); StringFormatter::send(stream, F("\n"), opcode); + LCD(2, F("p%c MAIN"), opcode); return; case HASH_KEYWORD_PROG: @@ -469,6 +485,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) if (mode == POWERMODE::OFF) DCC::setProgTrackBoost(false); // Prog track boost mode will not outlive prog track off StringFormatter::send(stream, F("\n"), opcode); + LCD(2, F("p%c PROG"), opcode); return; case HASH_KEYWORD_JOIN: DCCWaveform::mainTrack.setPowerMode(mode); @@ -477,9 +494,13 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) { DCC::setProgTrackSyncMain(true); StringFormatter::send(stream, F("\n"), opcode); + LCD(2, F("p1 JOIN")); } else + { StringFormatter::send(stream, F("\n")); + LCD(2, F("p0")); + } return; } break; @@ -779,17 +800,21 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) StringFormatter::send(stream, F("Free memory=%d\n"), minimumFreeMemory()); break; - case HASH_KEYWORD_ACK: // + case HASH_KEYWORD_ACK: // if (params >= 3) { if (p[1] == HASH_KEYWORD_LIMIT) { DCCWaveform::progTrack.setAckLimit(p[2]); - StringFormatter::send(stream, F("Ack limit=%dmA\n"), p[2]); + LCD(1, F("Ack Limit=%dmA"), p[2]); // } else if (p[1] == HASH_KEYWORD_MIN) { DCCWaveform::progTrack.setMinAckPulseDuration(p[2]); - StringFormatter::send(stream, F("Ack min=%dus\n"), p[2]); + LCD(0, F("Ack Min=%dus"), p[2]); // } else if (p[1] == HASH_KEYWORD_MAX) { DCCWaveform::progTrack.setMaxAckPulseDuration(p[2]); - StringFormatter::send(stream, F("Ack max=%dus\n"), p[2]); + LCD(0, F("Ack Max=%dus"), p[2]); // + } else if (p[1] == HASH_KEYWORD_RETRY) { + if (p[2] >255) p[2]=3; + DCC::setAckRetry(p[2]); + LCD(0, F("Ack Retry=%d"), p[2]); // } } else { StringFormatter::send(stream, F("Ack diag %S\n"), onOff ? F("on") : F("off")); @@ -801,21 +826,23 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) Diag::CMD = onOff; return true; +#ifdef HAS_ENOUGH_MEMORY case HASH_KEYWORD_WIFI: // Diag::WIFI = onOff; return true; - case HASH_KEYWORD_ETHERNET: // + case HASH_KEYWORD_ETHERNET: // Diag::ETHERNET = onOff; return true; case HASH_KEYWORD_WIT: // Diag::WITHROTTLE = onOff; return true; - + case HASH_KEYWORD_LCN: // Diag::LCN = onOff; return true; +#endif case HASH_KEYWORD_PROGBOOST: DCC::setProgTrackBoost(true); diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index df88e5d..0d23897 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ - #pragma GCC optimize ("-O3") + #include #include "DCCWaveform.h" @@ -46,10 +46,8 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { && (mainDriver->getFaultPin() != UNUSED_PIN)); // Only use PWM if both pins are PWM capable. Otherwise JOIN does not work MotorDriver::usePWM= mainDriver->isPWMCapable() && progDriver->isPWMCapable(); - if (MotorDriver::usePWM) - DIAG(F("Signal pin config: high accuracy waveform")); - else - DIAG(F("Signal pin config: normal accuracy waveform")); + DIAG(F("Signal pin config: %S accuracy waveform"), + MotorDriver::usePWM ? F("high") : F("normal") ); DCCTimer::begin(DCCWaveform::interruptHandler); } @@ -58,6 +56,8 @@ void DCCWaveform::loop(bool ackManagerActive) { progTrack.checkPowerOverload(ackManagerActive); } +#pragma GCC push_options +#pragma GCC optimize ("-O3") void DCCWaveform::interruptHandler() { // call the timer edge sensitive actions for progtrack and maintrack // member functions would be cleaner but have more overhead @@ -79,7 +79,7 @@ void DCCWaveform::interruptHandler() { else if (progTrack.ackPending) progTrack.checkAck(); } - +#pragma GCC push_options // An instance of this class handles the DCC transmissions for one track. (main or prog) // Interrupts are marshalled via the statics. @@ -124,6 +124,8 @@ void DCCWaveform::checkPowerOverload(bool ackManagerActive) { if (!isMainTrack && !ackManagerActive && !progTrackSyncMain && !progTrackBoosted) tripValue=progTripValue; + // Trackname for diag messages later + const FSH*trackname = isMainTrack ? F("MAIN") : F("PROG"); switch (powerMode) { case POWERMODE::OFF: sampleDelay = POWER_SAMPLE_OFF_WAIT; @@ -141,9 +143,9 @@ void DCCWaveform::checkPowerOverload(bool ackManagerActive) { } // Write this after the fact as we want to turn on as fast as possible // because we don't know which output actually triggered the fault pin - DIAG(F("*** COMMON FAULT PIN ACTIVE - TOGGLED POWER on %S ***"), isMainTrack ? F("MAIN") : F("PROG")); + DIAG(F("COMMON FAULT PIN ACTIVE - TOGGLED POWER on %S"), trackname); } else { - DIAG(F("*** %S FAULT PIN ACTIVE - OVERLOAD ***"), isMainTrack ? F("MAIN") : F("PROG")); + DIAG(F("%S FAULT PIN ACTIVE - OVERLOAD"), trackname); if (lastCurrent < tripValue) { lastCurrent = tripValue; // exaggerate } @@ -161,7 +163,7 @@ void DCCWaveform::checkPowerOverload(bool ackManagerActive) { unsigned int maxmA=motorDriver->raw2mA(tripValue); power_good_counter=0; sampleDelay = power_sample_overload_wait; - DIAG(F("*** %S TRACK POWER OVERLOAD current=%d max=%d offtime=%d ***"), isMainTrack ? F("MAIN") : F("PROG"), mA, maxmA, sampleDelay); + DIAG(F("%S TRACK POWER OVERLOAD current=%d max=%d offtime=%d"), trackname, mA, maxmA, sampleDelay); if (power_sample_overload_wait >= 10000) power_sample_overload_wait = 10000; else @@ -173,7 +175,7 @@ void DCCWaveform::checkPowerOverload(bool ackManagerActive) { setPowerMode(POWERMODE::ON); sampleDelay = POWER_SAMPLE_ON_WAIT; // Debug code.... - DIAG(F("*** %S TRACK POWER RESET delay=%d ***"), isMainTrack ? F("MAIN") : F("PROG"), sampleDelay); + DIAG(F("%S TRACK POWER RESET delay=%d"), trackname, sampleDelay); break; default: sampleDelay = 999; // cant get here..meaningless statement to avoid compiler warning. @@ -197,6 +199,8 @@ const bool DCCWaveform::signalTransform[]={ /* WAVE_LOW_0 -> */ LOW, /* WAVE_PENDING (should not happen) -> */ LOW}; +#pragma GCC push_options +#pragma GCC optimize ("-O3") void DCCWaveform::interrupt2() { // calculate the next bit to be sent: // set state WAVE_MID_1 for a 1=bit @@ -252,7 +256,7 @@ void DCCWaveform::interrupt2() { } } } - +#pragma GCC pop_options // Wait until there is no packet pending, then make this pending @@ -306,6 +310,8 @@ byte DCCWaveform::getAck() { return(0); // pending set off but not detected means no ACK. } +#pragma GCC push_options +#pragma GCC optimize ("-O3") void DCCWaveform::checkAck() { // This function operates in interrupt() time so must be fast and can't DIAG if (sentResetsSincePacket > 6) { //ACK timeout @@ -355,3 +361,4 @@ void DCCWaveform::checkAck() { } ackPulseStart=0; // We have detected a too-short or too-long pulse so ignore and wait for next leading edge } +#pragma GCC pop_options diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index 22b61f3..c6f80b3 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -17,12 +17,6 @@ * along with CommandStation. If not, see . * */ -#if __has_include ( "config.h") - #include "config.h" -#else - #warning config.h not found. Using defaults from config.example.h - #include "config.example.h" -#endif #include "defines.h" #if ETHERNET_ON == true #include "EthernetInterface.h" diff --git a/EthernetInterface.h b/EthernetInterface.h index e97ebfc..ab21f6a 100644 --- a/EthernetInterface.h +++ b/EthernetInterface.h @@ -22,12 +22,8 @@ #ifndef EthernetInterface_h #define EthernetInterface_h -#if __has_include ( "config.h") - #include "config.h" -#else - #warning config.h not found. Using defaults from config.example.h - #include "config.example.h" -#endif + +#include "defines.h") #include "DCCEXParser.h" #include #include diff --git a/LCDDisplay.h b/LCDDisplay.h index 6c6e150..6e2c69d 100644 --- a/LCDDisplay.h +++ b/LCDDisplay.h @@ -19,10 +19,9 @@ #ifndef LCDDisplay_h #define LCDDisplay_h #include +#include "defines.h" #include "DisplayInterface.h" -#include "defines.h" // includes config.h as well - // Allow maximum message length to be overridden from config.h #if !defined(MAX_MSG_SIZE) #define MAX_MSG_SIZE 20 diff --git a/RMFT2.cpp b/RMFT2.cpp index 1ccedf9..13627bd 100644 --- a/RMFT2.cpp +++ b/RMFT2.cpp @@ -36,6 +36,7 @@ const int16_t HASH_KEYWORD_UNLATCH=1353; const int16_t HASH_KEYWORD_PAUSE=-4142; const int16_t HASH_KEYWORD_RESUME=27609; const int16_t HASH_KEYWORD_KILL=5218; +const int16_t HASH_KEYWORD_ROUTES=-3702; // One instance of RMFT clas is used for each "thread" in the automation. // Each thread manages a loco on a journey through the layout, and/or may manage a scenery automation. @@ -192,7 +193,14 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { task->loco=cab; } return true; - + + case HASH_KEYWORD_ROUTES: // JMRI withrottle support + if (paramCount>1) return false; + StringFormatter::send(stream,F("")); + return true; + default: break; } diff --git a/Sensors.cpp b/Sensors.cpp index fc8abb5..4d3283a 100644 --- a/Sensors.cpp +++ b/Sensors.cpp @@ -310,7 +310,8 @@ void Sensor::load(){ struct SensorData data; Sensor *tt; - for(uint16_t i=0;idata.nSensors;i++){ + uint16_t i=EEStore::eeStore->data.nSensors; + while(i--){ EEPROM.get(EEStore::pointer(),data); tt=create(data.snum, data.pin, data.pullUp); EEStore::advance(sizeof(tt->data)); diff --git a/defines.h b/defines.h index 95102f9..1d76f95 100644 --- a/defines.h +++ b/defines.h @@ -22,10 +22,13 @@ #define DEFINES_H // defines.h relies on macros defined in config.h -#if __has_include ( "config.h") - #include "config.h" -#else - #include "config.example.h" +// but it may have already been included (for cosmetic convenence) by the .ino +#ifndef MOTOR_SHIELD_TYPE + #if __has_include ( "config.h") + #include "config.h" + #else + #include "config.example.h" + #endif #endif ////////////////////////////////////////////////////////////////////////////////