1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-12-25 13:41:23 +01:00

Merge branch 'EX-RAIL' into EX-RAIL-neil-RCN213

This commit is contained in:
Neil McKechnie 2021-08-25 00:38:38 +01:00 committed by GitHub
commit 77d4d7c400
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 104 additions and 44 deletions

View File

@ -109,7 +109,7 @@ void setup()
LCN::init(LCN_SERIAL); LCN::init(LCN_SERIAL);
#endif #endif
LCD(1,F("Ready")); LCD(3,F("Ready"));
} }
void loop() void loop()
@ -149,6 +149,6 @@ void loop()
if (freeNow < ramLowWatermark) if (freeNow < ramLowWatermark)
{ {
ramLowWatermark = freeNow; ramLowWatermark = freeNow;
LCD(2,F("Free RAM=%5db"), ramLowWatermark); LCD(3,F("Free RAM=%5db"), ramLowWatermark);
} }
} }

15
DCC.cpp
View File

@ -683,9 +683,13 @@ int DCC::nextLoco = 0;
//ACK MANAGER //ACK MANAGER
ackOp const * DCC::ackManagerProg; ackOp const * DCC::ackManagerProg;
ackOp const * DCC::ackManagerProgStart;
byte DCC::ackManagerByte; byte DCC::ackManagerByte;
byte DCC::ackManagerStash; byte DCC::ackManagerStash;
int DCC::ackManagerWord; int DCC::ackManagerWord;
byte DCC::ackManagerRetry;
byte DCC::ackRetry = 2;
int16_t DCC::ackRetrySum;
int DCC::ackManagerCv; int DCC::ackManagerCv;
byte DCC::ackManagerBitNum; byte DCC::ackManagerBitNum;
bool DCC::ackReceived; bool DCC::ackReceived;
@ -718,6 +722,8 @@ void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[]
ackManagerCv = cv; ackManagerCv = cv;
ackManagerProg = program; ackManagerProg = program;
ackManagerProgStart = program;
ackManagerRetry = ackRetry;
ackManagerByte = byteValueOrBitnum; ackManagerByte = byteValueOrBitnum;
ackManagerBitNum=byteValueOrBitnum; ackManagerBitNum=byteValueOrBitnum;
ackManagerCallback = callback; ackManagerCallback = callback;
@ -901,6 +907,15 @@ void DCC::ackManagerLoop() {
} }
void DCC::callback(int value) { 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; static unsigned long callbackStart;
// We are about to leave programming mode // We are about to leave programming mode
// Rule 1: If we have written to a decoder we must maintain power for 100mS // Rule 1: If we have written to a decoder we must maintain power for 100mS

12
DCC.h
View File

@ -64,8 +64,10 @@ enum CALLBACK_STATE : byte {
// Allocations with memory implications..! // Allocations with memory implications..!
// Base system takes approx 900 bytes + 8 per loco. Turnouts, Sensors etc are dynamically created // 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; const byte MAX_LOCOS = 20;
#elif defined(ARDUINO_AVR_NANO)
const byte MAX_LOCOS = 30;
#else #else
const byte MAX_LOCOS = 50; const byte MAX_LOCOS = 50;
#endif #endif
@ -113,6 +115,10 @@ public:
static inline void setGlobalSpeedsteps(byte s) { static inline void setGlobalSpeedsteps(byte s) {
globalSpeedsteps = s; globalSpeedsteps = s;
}; };
static inline void setAckRetry(byte retry) {
ackRetry = retry;
ackRetrySum = 0; // reset running total
};
private: private:
struct LOCO struct LOCO
@ -141,9 +147,13 @@ private:
// ACK MANAGER // ACK MANAGER
static ackOp const *ackManagerProg; static ackOp const *ackManagerProg;
static ackOp const *ackManagerProgStart;
static byte ackManagerByte; static byte ackManagerByte;
static byte ackManagerBitNum; static byte ackManagerBitNum;
static int ackManagerCv; static int ackManagerCv;
static byte ackManagerRetry;
static byte ackRetry;
static int16_t ackRetrySum;
static int ackManagerWord; static int ackManagerWord;
static byte ackManagerStash; static byte ackManagerStash;
static bool ackReceived; static bool ackReceived;

View File

@ -32,6 +32,16 @@
#include "DIAG.h" #include "DIAG.h"
#include <avr/wdt.h> #include <avr/wdt.h>
////////////////////////////////////////////////////////////////////////////////
//
// 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. // 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 // To discover new keyword numbers , use the <$ YOURKEYWORD> command
const int16_t HASH_KEYWORD_PROG = -29718; 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_CABS = -11981;
const int16_t HASH_KEYWORD_RAM = 25982; const int16_t HASH_KEYWORD_RAM = 25982;
const int16_t HASH_KEYWORD_CMD = 9962; 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_ACK = 3113;
const int16_t HASH_KEYWORD_ON = 2657; const int16_t HASH_KEYWORD_ON = 2657;
const int16_t HASH_KEYWORD_DCC = 6436; 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_PROGBOOST = -6353;
const int16_t HASH_KEYWORD_EEPROM = -7168; const int16_t HASH_KEYWORD_EEPROM = -7168;
const int16_t HASH_KEYWORD_LIMIT = 27413; 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_MAX = 16244;
const int16_t HASH_KEYWORD_MIN = 15978; 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_RESET = 26133;
const int16_t HASH_KEYWORD_RETRY = 25704;
const int16_t HASH_KEYWORD_SPEED28 = -17064; const int16_t HASH_KEYWORD_SPEED28 = -17064;
const int16_t HASH_KEYWORD_SPEED128 = 25816; const int16_t HASH_KEYWORD_SPEED128 = 25816;
const int16_t HASH_KEYWORD_SERVO=27709; const int16_t HASH_KEYWORD_SERVO=27709;
const int16_t HASH_KEYWORD_VPIN=-415; const int16_t HASH_KEYWORD_VPIN=-415;
const int16_t HASH_KEYWORD_C=67; const int16_t HASH_KEYWORD_C=67;
const int16_t HASH_KEYWORD_T=84; 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]; int16_t DCCEXParser::stashP[MAX_COMMAND_PARAMS];
bool DCCEXParser::stashBusy; bool DCCEXParser::stashBusy;
@ -257,6 +270,7 @@ void DCCEXParser::parse(const FSH * cmd) {
} }
// See documentation on DCC class for info on this section // See documentation on DCC class for info on this section
void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
{ {
(void)EEPROM; // tell compiler not to warn this is unused (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) if (mode == POWERMODE::OFF)
DCC::setProgTrackBoost(false); // Prog track boost mode will not outlive prog track off DCC::setProgTrackBoost(false); // Prog track boost mode will not outlive prog track off
StringFormatter::send(stream, F("<p%c>\n"), opcode); StringFormatter::send(stream, F("<p%c>\n"), opcode);
LCD(2, F("p%c"), opcode);
return; return;
} }
switch (p[0]) switch (p[0])
@ -462,6 +477,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
case HASH_KEYWORD_MAIN: case HASH_KEYWORD_MAIN:
DCCWaveform::mainTrack.setPowerMode(mode); DCCWaveform::mainTrack.setPowerMode(mode);
StringFormatter::send(stream, F("<p%c MAIN>\n"), opcode); StringFormatter::send(stream, F("<p%c MAIN>\n"), opcode);
LCD(2, F("p%c MAIN"), opcode);
return; return;
case HASH_KEYWORD_PROG: case HASH_KEYWORD_PROG:
@ -469,6 +485,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
if (mode == POWERMODE::OFF) if (mode == POWERMODE::OFF)
DCC::setProgTrackBoost(false); // Prog track boost mode will not outlive prog track off DCC::setProgTrackBoost(false); // Prog track boost mode will not outlive prog track off
StringFormatter::send(stream, F("<p%c PROG>\n"), opcode); StringFormatter::send(stream, F("<p%c PROG>\n"), opcode);
LCD(2, F("p%c PROG"), opcode);
return; return;
case HASH_KEYWORD_JOIN: case HASH_KEYWORD_JOIN:
DCCWaveform::mainTrack.setPowerMode(mode); DCCWaveform::mainTrack.setPowerMode(mode);
@ -477,9 +494,13 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
{ {
DCC::setProgTrackSyncMain(true); DCC::setProgTrackSyncMain(true);
StringFormatter::send(stream, F("<p1 JOIN>\n"), opcode); StringFormatter::send(stream, F("<p1 JOIN>\n"), opcode);
LCD(2, F("p1 JOIN"));
} }
else else
{
StringFormatter::send(stream, F("<p0>\n")); StringFormatter::send(stream, F("<p0>\n"));
LCD(2, F("p0"));
}
return; return;
} }
break; 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()); StringFormatter::send(stream, F("Free memory=%d\n"), minimumFreeMemory());
break; break;
case HASH_KEYWORD_ACK: // <D ACK ON/OFF> <D ACK [LIMIT|MIN|MAX] Value> case HASH_KEYWORD_ACK: // <D ACK ON/OFF> <D ACK [LIMIT|MIN|MAX|RETRY] Value>
if (params >= 3) { if (params >= 3) {
if (p[1] == HASH_KEYWORD_LIMIT) { if (p[1] == HASH_KEYWORD_LIMIT) {
DCCWaveform::progTrack.setAckLimit(p[2]); DCCWaveform::progTrack.setAckLimit(p[2]);
StringFormatter::send(stream, F("Ack limit=%dmA\n"), p[2]); LCD(1, F("Ack Limit=%dmA"), p[2]); // <D ACK LIMIT 42>
} else if (p[1] == HASH_KEYWORD_MIN) { } else if (p[1] == HASH_KEYWORD_MIN) {
DCCWaveform::progTrack.setMinAckPulseDuration(p[2]); DCCWaveform::progTrack.setMinAckPulseDuration(p[2]);
StringFormatter::send(stream, F("Ack min=%dus\n"), p[2]); LCD(0, F("Ack Min=%dus"), p[2]); // <D ACK MIN 1500>
} else if (p[1] == HASH_KEYWORD_MAX) { } else if (p[1] == HASH_KEYWORD_MAX) {
DCCWaveform::progTrack.setMaxAckPulseDuration(p[2]); DCCWaveform::progTrack.setMaxAckPulseDuration(p[2]);
StringFormatter::send(stream, F("Ack max=%dus\n"), p[2]); LCD(0, F("Ack Max=%dus"), p[2]); // <D ACK MAX 9000>
} 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]); // <D ACK RETRY 2>
} }
} else { } else {
StringFormatter::send(stream, F("Ack diag %S\n"), onOff ? F("on") : F("off")); StringFormatter::send(stream, F("Ack diag %S\n"), onOff ? F("on") : F("off"));
@ -801,6 +826,7 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
Diag::CMD = onOff; Diag::CMD = onOff;
return true; return true;
#ifdef HAS_ENOUGH_MEMORY
case HASH_KEYWORD_WIFI: // <D WIFI ON/OFF> case HASH_KEYWORD_WIFI: // <D WIFI ON/OFF>
Diag::WIFI = onOff; Diag::WIFI = onOff;
return true; return true;
@ -816,6 +842,7 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
case HASH_KEYWORD_LCN: // <D LCN ON/OFF> case HASH_KEYWORD_LCN: // <D LCN ON/OFF>
Diag::LCN = onOff; Diag::LCN = onOff;
return true; return true;
#endif
case HASH_KEYWORD_PROGBOOST: case HASH_KEYWORD_PROGBOOST:
DCC::setProgTrackBoost(true); DCC::setProgTrackBoost(true);

View File

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>. * along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/ */
#pragma GCC optimize ("-O3")
#include <Arduino.h> #include <Arduino.h>
#include "DCCWaveform.h" #include "DCCWaveform.h"
@ -46,10 +46,8 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) {
&& (mainDriver->getFaultPin() != UNUSED_PIN)); && (mainDriver->getFaultPin() != UNUSED_PIN));
// Only use PWM if both pins are PWM capable. Otherwise JOIN does not work // Only use PWM if both pins are PWM capable. Otherwise JOIN does not work
MotorDriver::usePWM= mainDriver->isPWMCapable() && progDriver->isPWMCapable(); MotorDriver::usePWM= mainDriver->isPWMCapable() && progDriver->isPWMCapable();
if (MotorDriver::usePWM) DIAG(F("Signal pin config: %S accuracy waveform"),
DIAG(F("Signal pin config: high accuracy waveform")); MotorDriver::usePWM ? F("high") : F("normal") );
else
DIAG(F("Signal pin config: normal accuracy waveform"));
DCCTimer::begin(DCCWaveform::interruptHandler); DCCTimer::begin(DCCWaveform::interruptHandler);
} }
@ -58,6 +56,8 @@ void DCCWaveform::loop(bool ackManagerActive) {
progTrack.checkPowerOverload(ackManagerActive); progTrack.checkPowerOverload(ackManagerActive);
} }
#pragma GCC push_options
#pragma GCC optimize ("-O3")
void DCCWaveform::interruptHandler() { void DCCWaveform::interruptHandler() {
// call the timer edge sensitive actions for progtrack and maintrack // call the timer edge sensitive actions for progtrack and maintrack
// member functions would be cleaner but have more overhead // member functions would be cleaner but have more overhead
@ -79,7 +79,7 @@ void DCCWaveform::interruptHandler() {
else if (progTrack.ackPending) progTrack.checkAck(); 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) // An instance of this class handles the DCC transmissions for one track. (main or prog)
// Interrupts are marshalled via the statics. // Interrupts are marshalled via the statics.
@ -124,6 +124,8 @@ void DCCWaveform::checkPowerOverload(bool ackManagerActive) {
if (!isMainTrack && !ackManagerActive && !progTrackSyncMain && !progTrackBoosted) if (!isMainTrack && !ackManagerActive && !progTrackSyncMain && !progTrackBoosted)
tripValue=progTripValue; tripValue=progTripValue;
// Trackname for diag messages later
const FSH*trackname = isMainTrack ? F("MAIN") : F("PROG");
switch (powerMode) { switch (powerMode) {
case POWERMODE::OFF: case POWERMODE::OFF:
sampleDelay = POWER_SAMPLE_OFF_WAIT; 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 // 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 // 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 { } else {
DIAG(F("*** %S FAULT PIN ACTIVE - OVERLOAD ***"), isMainTrack ? F("MAIN") : F("PROG")); DIAG(F("%S FAULT PIN ACTIVE - OVERLOAD"), trackname);
if (lastCurrent < tripValue) { if (lastCurrent < tripValue) {
lastCurrent = tripValue; // exaggerate lastCurrent = tripValue; // exaggerate
} }
@ -161,7 +163,7 @@ void DCCWaveform::checkPowerOverload(bool ackManagerActive) {
unsigned int maxmA=motorDriver->raw2mA(tripValue); unsigned int maxmA=motorDriver->raw2mA(tripValue);
power_good_counter=0; power_good_counter=0;
sampleDelay = power_sample_overload_wait; 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) if (power_sample_overload_wait >= 10000)
power_sample_overload_wait = 10000; power_sample_overload_wait = 10000;
else else
@ -173,7 +175,7 @@ void DCCWaveform::checkPowerOverload(bool ackManagerActive) {
setPowerMode(POWERMODE::ON); setPowerMode(POWERMODE::ON);
sampleDelay = POWER_SAMPLE_ON_WAIT; sampleDelay = POWER_SAMPLE_ON_WAIT;
// Debug code.... // 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; break;
default: default:
sampleDelay = 999; // cant get here..meaningless statement to avoid compiler warning. sampleDelay = 999; // cant get here..meaningless statement to avoid compiler warning.
@ -197,6 +199,8 @@ const bool DCCWaveform::signalTransform[]={
/* WAVE_LOW_0 -> */ LOW, /* WAVE_LOW_0 -> */ LOW,
/* WAVE_PENDING (should not happen) -> */ LOW}; /* WAVE_PENDING (should not happen) -> */ LOW};
#pragma GCC push_options
#pragma GCC optimize ("-O3")
void DCCWaveform::interrupt2() { void DCCWaveform::interrupt2() {
// calculate the next bit to be sent: // calculate the next bit to be sent:
// set state WAVE_MID_1 for a 1=bit // 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 // 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. return(0); // pending set off but not detected means no ACK.
} }
#pragma GCC push_options
#pragma GCC optimize ("-O3")
void DCCWaveform::checkAck() { void DCCWaveform::checkAck() {
// This function operates in interrupt() time so must be fast and can't DIAG // This function operates in interrupt() time so must be fast and can't DIAG
if (sentResetsSincePacket > 6) { //ACK timeout 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 ackPulseStart=0; // We have detected a too-short or too-long pulse so ignore and wait for next leading edge
} }
#pragma GCC pop_options

View File

@ -17,12 +17,6 @@
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>. * along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
* *
*/ */
#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 "defines.h"
#if ETHERNET_ON == true #if ETHERNET_ON == true
#include "EthernetInterface.h" #include "EthernetInterface.h"

View File

@ -22,12 +22,8 @@
#ifndef EthernetInterface_h #ifndef EthernetInterface_h
#define EthernetInterface_h #define EthernetInterface_h
#if __has_include ( "config.h")
#include "config.h" #include "defines.h")
#else
#warning config.h not found. Using defaults from config.example.h
#include "config.example.h"
#endif
#include "DCCEXParser.h" #include "DCCEXParser.h"
#include <Arduino.h> #include <Arduino.h>
#include <avr/pgmspace.h> #include <avr/pgmspace.h>

View File

@ -19,10 +19,9 @@
#ifndef LCDDisplay_h #ifndef LCDDisplay_h
#define LCDDisplay_h #define LCDDisplay_h
#include <Arduino.h> #include <Arduino.h>
#include "defines.h"
#include "DisplayInterface.h" #include "DisplayInterface.h"
#include "defines.h" // includes config.h as well
// Allow maximum message length to be overridden from config.h // Allow maximum message length to be overridden from config.h
#if !defined(MAX_MSG_SIZE) #if !defined(MAX_MSG_SIZE)
#define MAX_MSG_SIZE 20 #define MAX_MSG_SIZE 20

View File

@ -36,6 +36,7 @@ const int16_t HASH_KEYWORD_UNLATCH=1353;
const int16_t HASH_KEYWORD_PAUSE=-4142; const int16_t HASH_KEYWORD_PAUSE=-4142;
const int16_t HASH_KEYWORD_RESUME=27609; const int16_t HASH_KEYWORD_RESUME=27609;
const int16_t HASH_KEYWORD_KILL=5218; 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. // 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. // Each thread manages a loco on a journey through the layout, and/or may manage a scenery automation.
@ -193,6 +194,13 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
} }
return true; return true;
case HASH_KEYWORD_ROUTES: // </ ROUTES > JMRI withrottle support
if (paramCount>1) return false;
StringFormatter::send(stream,F("</ROUTES "));
emitWithrottleRouteList(stream);
StringFormatter::send(stream,F(">"));
return true;
default: default:
break; break;
} }

View File

@ -310,7 +310,8 @@ void Sensor::load(){
struct SensorData data; struct SensorData data;
Sensor *tt; Sensor *tt;
for(uint16_t i=0;i<EEStore::eeStore->data.nSensors;i++){ uint16_t i=EEStore::eeStore->data.nSensors;
while(i--){
EEPROM.get(EEStore::pointer(),data); EEPROM.get(EEStore::pointer(),data);
tt=create(data.snum, data.pin, data.pullUp); tt=create(data.snum, data.pin, data.pullUp);
EEStore::advance(sizeof(tt->data)); EEStore::advance(sizeof(tt->data));

View File

@ -22,10 +22,13 @@
#define DEFINES_H #define DEFINES_H
// defines.h relies on macros defined in config.h // defines.h relies on macros defined in config.h
#if __has_include ( "config.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" #include "config.h"
#else #else
#include "config.example.h" #include "config.example.h"
#endif
#endif #endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////